Last active
June 1, 2017 12:57
-
-
Save geerteltink/0f0cb7656988401719431364d757ad34 to your computer and use it in GitHub Desktop.
Symfony Console ProxyCommand
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
<?php | |
require __DIR__ . '/../vendor/autoload.php'; | |
use App\Infrastructure\Console\ProxyCommand; | |
use Symfony\Component\Console\Application; | |
/** @var \Interop\Container\ContainerInterface $container */ | |
$container = require __DIR__ . '/../config/container.php'; | |
$application = new Application('Application console'); | |
$commands = $container->get('config')['console']['commands'] ?? []; | |
foreach ($commands as $command) { | |
$application->add(new ProxyCommand($command, $container)); | |
} | |
$application->run(); |
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
<?php | |
namespace App\Console; | |
use Psr\Log\LoggerInterface; | |
use Symfony\Component\Console\Command\Command; | |
use Symfony\Component\Console\Input\InputArgument; | |
use Symfony\Component\Console\Input\InputInterface; | |
use Symfony\Component\Console\Input\InputOption; | |
use Symfony\Component\Console\Output\OutputInterface; | |
class GreetCommand extends Command | |
{ | |
private $logger; | |
public function __construct(LoggerInterface $logger) | |
{ | |
$this->logger = $logger; | |
parent::__construct(); | |
} | |
protected function configure(): void | |
{ | |
$this | |
->setName('demo:greet') | |
->setDescription('Greet someone') | |
->addArgument( | |
'name', | |
InputArgument::OPTIONAL, | |
'Who do you want to greet?' | |
) | |
->addOption( | |
'yell', | |
null, | |
InputOption::VALUE_NONE, | |
'If set, the task will yell in uppercase letters' | |
); | |
} | |
protected function execute(InputInterface $input, OutputInterface $output) | |
{ | |
$name = $input->getArgument('name'); | |
$text = 'Hello'; | |
if ($name) { | |
$text = 'Hello ' . $name; | |
} | |
if ($input->getOption('yell')) { | |
$text = strtoupper($text); | |
} | |
$output->writeln($text); | |
$this->logger->info('GreetCommand triggered', ['name' => $name]); | |
} | |
} |
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
<?php | |
namespace App\Console; | |
use Interop\Container\ContainerInterface; | |
use Psr\Log\LoggerInterface; | |
class GreetCommandFactory | |
{ | |
public function __invoke(ContainerInterface $container) | |
{ | |
return new GreetCommand( | |
$container->get(LoggerInterface::class) | |
); | |
} | |
} |
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
<?php | |
namespace App\Infrastructure\Console; | |
use Psr\Container\ContainerInterface; | |
use ReflectionClass; | |
use Symfony\Component\Console\Command\Command; | |
use Symfony\Component\Console\Input\InputInterface; | |
use Symfony\Component\Console\Output\OutputInterface; | |
/** | |
* Symfony Console Proxy Command | |
* | |
* Symfony Console loads all classes and their dependencies. Even if you don't | |
* want to execute a certain command. | |
* | |
* This ProxyCommand starts the Command but without loading the dependencies. It | |
* than copies all original settings to the ProxyCommand. When the actual | |
* command is called it will construct the original command with its dependencies. | |
*/ | |
class ProxyCommand extends Command | |
{ | |
/** | |
* @var string | |
*/ | |
private $command; | |
/** | |
* @var ContainerInterface | |
*/ | |
private $container; | |
public function __construct(string $command, ContainerInterface $container) | |
{ | |
$this->command = $command; | |
$this->container = $container; | |
parent::__construct(); | |
} | |
/** | |
* Use Reflection voodoo to to capture the original configuration | |
*/ | |
protected function configure(): void | |
{ | |
// Construct the ReflectionClass | |
$refCommand = new ReflectionClass($this->command); | |
// Make sure the configure method is accessible | |
$configure = $refCommand->getMethod('configure'); | |
$configure->setAccessible(true); | |
// Create the command without loading dependencies | |
$command = $refCommand->newInstanceWithoutConstructor(); | |
// Call the parent constructor | |
$parent = $refCommand->getParentClass(); | |
$construct = $parent->getMethod('__construct'); | |
$construct->invoke($command); | |
// Copy original properties to this proxy command | |
$baseCommand = new ReflectionClass(Command::class); | |
$properties = $baseCommand->getProperties(); | |
foreach ($properties as $property) { | |
$property->setAccessible(true); | |
$property->setValue($this, $property->getValue($command)); | |
} | |
} | |
/** | |
* Get the original command and inject all its dependencies this time | |
* | |
* @param InputInterface $input | |
* @param OutputInterface $output | |
* | |
* @return int|null | |
*/ | |
protected function execute(InputInterface $input, OutputInterface $output): ?int | |
{ | |
/** @var Command $command */ | |
$command = $this->container->get($this->command); | |
// Execute the original command | |
return $command->execute($input, $output); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment