-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit e5b3fe5
Showing
9 changed files
with
396 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# Shopware Psysh | ||
---- | ||
|
||
![showcase](https://media.giphy.com/media/lMqlDUn389q7P31kti/source.gif) | ||
|
||
Inspired by the `frosh tinker` that inspired by `laravel tinker` command from laravel this plugin adds a similar command to Shopware 6. | ||
|
||
It's basically a fork version of `frosh tinker` for Shopware 6 with additional features. | ||
|
||
Read more: | ||
|
||
- [`frosh/tinker`](https://github.com/FriendsOfShopware/FroshTinker) repository | ||
- [`bobthecow/psysh`](https://github.com/bobthecow/psysh) repository | ||
|
||
## New command: | ||
|
||
``` | ||
bin/console sw:psysh | ||
``` | ||
|
||
- Enter `ls` to get list of scoped variables. | ||
- Enter `list` to get list of avaialbe commands. | ||
|
||
## Additional features: | ||
- Shopware's Core Services aliases | ||
(e.g new EqualsFilter via CLI instead of fully qualified class name `Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter`. | ||
_(There is some rare cases that two classes use the same class alias so you need to give the fullname to make it work)_ | ||
|
||
- Shopware's service container auto-completion. | ||
![alias and auto suggest](https://i.imgur.com/xhK4QIy.png) | ||
|
||
|
||
|
||
- Struct Object caster. | ||
![Struct object caster](https://i.imgur.com/C9pUnOy.png) | ||
|
||
- Default scoped variables: $container (Service Container object), $connection (Doctrine Connection object), $context (Default context object), $criteria (default criteria object)...and more | ||
|
||
## Requirements | ||
|
||
- Shopware 6.3 or above (older versions might work, but were not tested) | ||
- PHP 7.1 or above | ||
|
||
## Installation via composer | ||
|
||
``` | ||
composer require vin-sw/psysh | ||
bin/console plugin:refresh | ||
bin/console plugin:install --activate ShopwarePsysh | ||
bin/console sw:psysh | ||
``` | ||
|
||
## Contributing | ||
|
||
Feel free to fork and send pull requests! | ||
|
||
|
||
## Licence | ||
|
||
This project uses the [MIT License](LICENCE.md). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"name": "vin-sw/psysh", | ||
"description": "Psysh integration for Shopware", | ||
"type": "shopware-platform-plugin", | ||
"license": "MIT", | ||
"autoload": { | ||
"psr-4": { | ||
"ShopwarePsysh\\": "src/" | ||
} | ||
}, | ||
"extra": { | ||
"shopware-plugin-class": "ShopwarePsysh\\ShopwarePsysh", | ||
"label": { | ||
"de-DE": "Psysh integration for Shopware", | ||
"en-GB": "Psysh integration for Shopware" | ||
} | ||
}, | ||
"require": { | ||
"psy/psysh": "^0.10.5" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?php declare(strict_types=1); | ||
namespace ShopwarePsysh\Caster; | ||
|
||
use Shopware\Core\Framework\Struct\Struct; | ||
use Symfony\Component\VarDumper\Cloner\Stub; | ||
use Symfony\Component\VarDumper\Exception\ThrowingCasterException; | ||
|
||
class ShopwareCaster implements ShopwareCasterInterface | ||
{ | ||
public function cast($object, $array, Stub $stub, $isNested, $filter): array | ||
{ | ||
if (!$object instanceof Struct) { | ||
return $array; | ||
} | ||
|
||
try { | ||
return $object->jsonSerialize(); | ||
} catch (ThrowingCasterException $exception) { | ||
return $array; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?php declare(strict_types=1); | ||
namespace ShopwarePsysh\Caster; | ||
|
||
use Symfony\Component\VarDumper\Cloner\Stub; | ||
|
||
interface ShopwareCasterInterface | ||
{ | ||
public function cast($object, $array, Stub $stub, $isNested, $filter): array; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace ShopwarePsysh\Command; | ||
|
||
use Doctrine\DBAL\Connection; | ||
use Psr\Container\ContainerInterface; | ||
use Psy\Configuration; | ||
use Psy\Shell; | ||
use Shopware\Core\Framework\Context; | ||
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; | ||
use Shopware\Core\Framework\Struct\Struct; | ||
use ShopwarePsysh\Caster\ShopwareCasterInterface; | ||
use Symfony\Component\Console\Command\Command; | ||
use Symfony\Component\Console\Input\InputArgument; | ||
use Symfony\Component\Console\Input\InputInterface; | ||
use Symfony\Component\Console\Output\OutputInterface; | ||
use Symfony\Component\DependencyInjection\ContainerAwareInterface; | ||
use Symfony\Component\Finder\Finder; | ||
|
||
class PsyshCommand extends Command | ||
{ | ||
protected static $defaultName = 'sw:psysh'; | ||
/** | ||
* @var ContainerInterface | ||
*/ | ||
private $container; | ||
/** | ||
* @var iterable | ||
*/ | ||
private $casters; | ||
|
||
/** | ||
* PsyshCommand constructor. | ||
* @param string|null $name | ||
* @param iterable $casters | ||
*/ | ||
public function __construct(iterable $casters, string $name = null) | ||
{ | ||
parent::__construct($name ?? self::$defaultName); | ||
|
||
$this->casters = $casters; | ||
} | ||
|
||
/** | ||
* @internal | ||
* @required | ||
*/ | ||
public function setContainer(ContainerInterface $container): ?ContainerInterface | ||
{ | ||
$previous = $this->container; | ||
$this->container = $container; | ||
|
||
return $previous; | ||
} | ||
|
||
protected function configure() | ||
{ | ||
$this->addArgument('include', InputArgument::IS_ARRAY, 'Include file(s) before starting tinker'); | ||
} | ||
|
||
protected function execute(InputInterface $input, OutputInterface $output) | ||
{ | ||
$this->getApplication()->setCatchExceptions(false); | ||
|
||
$config = new Configuration(); | ||
$config->loadConfig($input->getArguments()); | ||
|
||
$config->setUpdateCheck('never'); | ||
$config->setPrompt('> '); | ||
$config->setStartupMessage(' | ||
<info>Enter list to see available commands</info> | ||
<info>Enter ls to see list of scoped variables</info> | ||
'); | ||
|
||
$config->addMatchers([ | ||
new ShopwareClassesMatcher(), | ||
new ShopwareServicesMatcher(), | ||
]); | ||
|
||
$config->getPresenter()->addCasters( | ||
$this->getCasters() | ||
); | ||
|
||
try { | ||
$this->registerAliases(); | ||
} catch (\Throwable $ex) { | ||
} | ||
|
||
$shell = new Shell($config); | ||
|
||
$shell->setScopeVariables([ | ||
'application' => $this->getApplication(), | ||
'container' => $this->container, | ||
'connection' => $this->container->get(Connection::class), | ||
'commands' => $this->getCommands(), | ||
'context' => Context::createDefaultContext(), | ||
'criteria' => new Criteria(), | ||
'env' => $_ENV | ||
]); | ||
|
||
$shell->addCommands($this->getCommands()); | ||
$shell->setIncludes($input->getArgument('include')); | ||
|
||
return $shell->run(); | ||
} | ||
|
||
protected function getCommands(): array | ||
{ | ||
$commands = $this->getApplication()->all(); | ||
|
||
foreach ($commands as $command) { | ||
if ($command instanceof ContainerAwareInterface) { | ||
$command->setContainer($this->getApplication()->getContainer()); | ||
} | ||
} | ||
|
||
return $commands; | ||
} | ||
|
||
private function registerAliases(): void | ||
{ | ||
$projectDir = $this->container->get('kernel')->getProjectDir(); | ||
$platformDir = $projectDir . '/platform'; | ||
|
||
$finder = new Finder(); | ||
$finder->files()->name('*.php')->in((file_exists($platformDir) ? $platformDir : $projectDir) .'/src/Core'); | ||
|
||
foreach ($finder as $file) { | ||
$namespace = 'Shopware\\Core\\'; | ||
|
||
if ($relativePath = $file->getRelativePath()) { | ||
$namespace .= strtr($relativePath, '/', '\\') . '\\'; | ||
} | ||
|
||
if (strpos($namespace, 'Test')) { | ||
continue; | ||
} | ||
|
||
$baseName = $file->getBasename('.php'); | ||
|
||
$class = $namespace . $baseName; | ||
|
||
try { | ||
class_alias($class, $baseName); | ||
} catch (\Throwable $e) { | ||
// | ||
} | ||
} | ||
} | ||
|
||
private function getCasters(): array | ||
{ | ||
$casters = []; | ||
|
||
foreach ($this->casters as $caster) { | ||
if (!$caster instanceof ShopwareCasterInterface) { | ||
continue; | ||
} | ||
|
||
$casters[Struct::class] = [$caster, 'cast']; | ||
} | ||
|
||
return $casters; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace ShopwarePsysh\Command; | ||
|
||
use Psy\TabCompletion\Matcher\AbstractMatcher; | ||
use Psy\TabCompletion\Matcher\ClassNamesMatcher; | ||
|
||
class ShopwareClassesMatcher extends ClassNamesMatcher | ||
{ | ||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function getMatches(array $tokens, array $info = []) | ||
{ | ||
$class = $this->getNamespaceAndClass($tokens); | ||
if (\strlen($class) > 0 && $class[0] === '\\') { | ||
$class = \substr($class, 1, \strlen($class)); | ||
} | ||
$quotedClass = \preg_quote($class); | ||
|
||
return array_values(\array_map( | ||
function ($className) use ($class) { | ||
// get the number of namespace separators | ||
$nsPos = \substr_count($class, '\\'); | ||
$pieces = \explode('\\', $className); | ||
//$methods = Mirror::get($class); | ||
return \implode('\\', \array_slice($pieces, $nsPos, \count($pieces))); | ||
}, | ||
\array_filter( | ||
\get_declared_classes(), | ||
function ($className) use ($quotedClass) { | ||
return AbstractMatcher::startsWith($quotedClass, $className) || (bool) strpos($className, $quotedClass); | ||
} | ||
) | ||
)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace ShopwarePsysh\Command; | ||
|
||
use Psy\TabCompletion\Matcher\AbstractContextAwareMatcher; | ||
use Psy\TabCompletion\Matcher\AbstractMatcher; | ||
use Symfony\Component\DependencyInjection\Container; | ||
|
||
class ShopwareServicesMatcher extends AbstractContextAwareMatcher | ||
{ | ||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function getMatches(array $tokens, array $info = []) | ||
{ | ||
if (count($tokens) <= 5 || $tokens[1][1] !== '$container') { | ||
return []; | ||
} | ||
$input = str_replace("'", '', $tokens[5][1]); | ||
|
||
$object = $this->getVariable('container'); | ||
|
||
if (!$object instanceof Container) { | ||
return []; | ||
} | ||
|
||
$services = $object->getServiceIds(); | ||
|
||
return \array_filter( | ||
$services, | ||
function ($var) use ($input) { | ||
return AbstractMatcher::startsWith($input, $var) || (bool) strpos($var, $input); | ||
} | ||
); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function hasMatched(array $tokens) | ||
{ | ||
$token = \array_pop($tokens); | ||
$prevToken = \array_pop($tokens); | ||
|
||
if (empty($tokens[1][1]) || $tokens[1][1] !== '$container') { | ||
return false; | ||
} | ||
|
||
if ($prevToken === '(' || $token === '(') { | ||
return true; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?xml version="1.0" ?> | ||
|
||
<container xmlns="http://symfony.com/schema/dic/services" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> | ||
|
||
<services> | ||
<service id="ShopwarePsysh\Command\PsyshCommand"> | ||
<argument type="tagged" tag="shopware.caster"/> | ||
<tag name="console.command"/> | ||
<call method="setContainer"> | ||
<argument type="service" id="service_container"/> | ||
</call> | ||
</service> | ||
|
||
<service id="ShopwarePsysh\Caster\ShopwareCaster"> | ||
<tag name="shopware.caster"/> | ||
</service> | ||
</services> | ||
</container> |
Oops, something went wrong.