From e42936504cb5767735eaa5479344cb9236c000f3 Mon Sep 17 00:00:00 2001 From: Francesco Levorato Date: Wed, 11 Apr 2012 23:35:46 +0200 Subject: [PATCH 1/3] [FrameworkBundle] Added events for CLI commands This adds an init and terminate event for commands. They are dispatched from ContainerAwareCommand. The cache:clear command can't implement this (cf. #3889 on Github). --- .../Bundle/FrameworkBundle/CHANGELOG.md | 1 + .../Command/CacheClearCommand.php | 17 +++++- .../Command/ContainerAwareCommand.php | 23 ++++++++ .../FrameworkBundle/Console/ConsoleEvents.php | 43 +++++++++++++++ .../FrameworkBundle/Event/ConsoleEvent.php | 54 +++++++++++++++++++ .../Event/ConsoleTerminateEvent.php | 46 ++++++++++++++++ .../Tests/Console/ApplicationTest.php | 53 ++++++++++++++++-- .../Tests/Console/Fixtures/FooCommand.php | 24 +++++++++ .../Tests/Console/Fixtures/SilentCommand.php | 25 +++++++++ 9 files changed, 282 insertions(+), 4 deletions(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Console/ConsoleEvents.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Event/ConsoleEvent.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Event/ConsoleTerminateEvent.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Console/Fixtures/FooCommand.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Console/Fixtures/SilentCommand.php diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index f652d53195b71..5b0ac2ebcfb02 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -7,6 +7,7 @@ CHANGELOG * replaced Symfony\Bundle\FrameworkBundle\Controller\TraceableControllerResolver by Symfony\Component\HttpKernel\Controller\TraceableControllerResolver * replaced Symfony\Component\HttpKernel\Debug\ContainerAwareTraceableEventDispatcher by Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher * added Client::enableProfiler() + * added an init and terminate event dispatched by CLI commands 2.1.0 ----- diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php index a9aa7a5b8f6fc..94e4c025b5eba 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Command; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -23,10 +24,12 @@ * @author Francis Besset * @author Fabien Potencier */ -class CacheClearCommand extends ContainerAwareCommand +class CacheClearCommand extends Command { protected $name; + private $container; + /** * @see Command */ @@ -174,4 +177,16 @@ protected function getContainerClass() return new $class($parent->getEnvironment(), $parent->isDebug()); } + + /** + * @return ContainerInterface + */ + protected function getContainer() + { + if (null === $this->container) { + $this->container = $this->getApplication()->getKernel()->getContainer(); + } + + return $this->container; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerAwareCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerAwareCommand.php index 0540870657417..9829a747f293a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerAwareCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerAwareCommand.php @@ -11,7 +11,12 @@ namespace Symfony\Bundle\FrameworkBundle\Command; +use Symfony\Bundle\FrameworkBundle\Console\ConsoleEvents; +use Symfony\Bundle\FrameworkBundle\Event\ConsoleEvent; +use Symfony\Bundle\FrameworkBundle\Event\ConsoleTerminateEvent; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerAwareInterface; @@ -27,6 +32,24 @@ abstract class ContainerAwareCommand extends Command implements ContainerAwareIn */ private $container; + /** + * {@inheritdoc} + */ + public function run(InputInterface $input, OutputInterface $output) + { + $dispatcher = $this->getContainer()->get('event_dispatcher'); + + $initEvent = new ConsoleEvent($input, $output); + $dispatcher->dispatch(ConsoleEvents::INIT, $initEvent); + + $exitCode = parent::run($input, $output); + + $terminateEvent = new ConsoleTerminateEvent($input, $output, $exitCode); + $dispatcher->dispatch(ConsoleEvents::TERMINATE, $terminateEvent); + + return $exitCode; + } + /** * @return ContainerInterface */ diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/ConsoleEvents.php b/src/Symfony/Bundle/FrameworkBundle/Console/ConsoleEvents.php new file mode 100644 index 0000000000000..b8fee5fcee9f0 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Console/ConsoleEvents.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Console; + +/** + * Contains all events thrown during Console commands execution + * + * @author Francesco Levorato + */ +final class ConsoleEvents +{ + /** + * The INIT event allows you to attach listeners before any command is + * executed by the console. It also allows you to modify the input and output + * before they are handled to the command. + * + * The event listener method receives a \Symfony\Bundle\FrameworkBundle\Event\ConsoleEvent + * instance. + * + * @var string + */ + const INIT = 'console.init'; + + /** + * The TERMINATE event allows you to attach listeners after a command is + * executed by the console. + * + * The event listener method receives a \Symfony\Bundle\FrameworkBundle\Event\ConsoleTerminateEvent + * instance. + * + * @var string + */ + const TERMINATE = 'console.terminate'; +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Event/ConsoleEvent.php b/src/Symfony/Bundle/FrameworkBundle/Event/ConsoleEvent.php new file mode 100644 index 0000000000000..23b6b42f882c3 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Event/ConsoleEvent.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Event; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\EventDispatcher\Event; + +/** + * Allows to inspect input and output of a command. + * + * @author Francesco Levorato + */ +class ConsoleEvent extends Event +{ + private $input; + + private $output; + + public function __construct(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + } + + /** + * Returns the input object + * + * @return InputInterface + */ + public function getInput() + { + return $this->input; + } + + /** + * Returns the output object + * + * @return OutputInterface + */ + public function getOutput() + { + return $this->output; + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Event/ConsoleTerminateEvent.php b/src/Symfony/Bundle/FrameworkBundle/Event/ConsoleTerminateEvent.php new file mode 100644 index 0000000000000..8ade530a85dc5 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Event/ConsoleTerminateEvent.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Event; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Allows to receive the exit code of a command after its execution. + * + * @author Francesco Levorato + */ +class ConsoleTerminateEvent extends ConsoleEvent +{ + /** + * The exit code of the command. + * + * @var integer + */ + private $exitCode; + + public function __construct(InputInterface $input, OutputInterface $output, $exitCode) + { + parent::__construct($input, $output); + $this->exitCode = $exitCode; + } + + /** + * Returns the exit code. + * + * @return integer + */ + public function getExitCode() + { + return $this->exitCode; + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php index 1427453b10650..b870e1d728584 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php @@ -12,6 +12,8 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Console; use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Bundle\FrameworkBundle\Tests\Console\Fixtures\FooCommand; +use Symfony\Bundle\FrameworkBundle\Tests\Console\Fixtures\SilentCommand; use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\NullOutput; @@ -22,7 +24,7 @@ public function testBundleInterfaceImplementation() { $bundle = $this->getMock("Symfony\Component\HttpKernel\Bundle\BundleInterface"); - $kernel = $this->getKernel(array($bundle)); + $kernel = $this->getKernel(array($bundle), $this->never()); $application = new Application($kernel); $application->doRun(new ArrayInput(array('list')), new NullOutput()); @@ -33,13 +35,33 @@ public function testBundleCommandsAreRegistered() $bundle = $this->getMock("Symfony\Component\HttpKernel\Bundle\Bundle"); $bundle->expects($this->once())->method('registerCommands'); - $kernel = $this->getKernel(array($bundle)); + $kernel = $this->getKernel(array($bundle), $this->never()); $application = new Application($kernel); $application->doRun(new ArrayInput(array('list')), new NullOutput()); } - private function getKernel(array $bundles) + public function testCommandDispatchEvents() + { + $kernel = $this->getKernel(array(), $this->once()); + + $application = new Application($kernel); + $application->add(new FooCommand('foo')); + + $application->doRun(new ArrayInput(array('foo')), new NullOutput()); + } + + public function testSilentCommand() + { + $kernel = $this->getKernel(array(), $this->never()); + + $application = new Application($kernel); + $application->add(new SilentCommand('chut')); + + $application->doRun(new ArrayInput(array('chut')), new NullOutput()); + } + + private function getKernel(array $bundles, $dispatcherExpected = null) { $kernel = $this->getMock("Symfony\Component\HttpKernel\KernelInterface"); $kernel @@ -47,6 +69,31 @@ private function getKernel(array $bundles) ->method('getBundles') ->will($this->returnValue($bundles)) ; + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + + $dispatcherExpected = $dispatcherExpected ?: $this->any(); + if ($this->never() == $dispatcherExpected) { + $container + ->expects($dispatcherExpected) + ->method('get'); + } else { + $eventDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $eventDispatcher + ->expects($this->atLeastOnce()) + ->method('dispatch'); + $container + ->expects($dispatcherExpected) + ->method('get') + ->with($this->equalTo('event_dispatcher')) + ->will($this->returnValue($eventDispatcher)); + } + + $kernel + ->expects($this->any()) + ->method('getContainer') + ->will($this->returnValue($container)) + ; return $kernel; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Fixtures/FooCommand.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Fixtures/FooCommand.php new file mode 100644 index 0000000000000..513f60198f78b --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Fixtures/FooCommand.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Console\Fixtures; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class FooCommand extends ContainerAwareCommand +{ + protected function execute(InputInterface $input, OutputInterface $output) + { + return 0; + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Fixtures/SilentCommand.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Fixtures/SilentCommand.php new file mode 100644 index 0000000000000..1e3621b437c73 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Fixtures/SilentCommand.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Console\Fixtures; + + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class SilentCommand extends Command +{ + protected function execute(InputInterface $input, OutputInterface $output) + { + return 0; + } +} From 41557c94bff1f73348214c5ef4eae63e04890735 Mon Sep 17 00:00:00 2001 From: Bilal Amarni Date: Wed, 6 Feb 2013 11:19:01 +0100 Subject: [PATCH 2/3] [FrameworkBundle] added helperSet to console event objects --- .../Command/ContainerAwareCommand.php | 3 +++ .../FrameworkBundle/Event/ConsoleEvent.php | 23 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerAwareCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerAwareCommand.php index 9829a747f293a..e87cb3108235a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerAwareCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerAwareCommand.php @@ -38,13 +38,16 @@ abstract class ContainerAwareCommand extends Command implements ContainerAwareIn public function run(InputInterface $input, OutputInterface $output) { $dispatcher = $this->getContainer()->get('event_dispatcher'); + $helperSet = $this->getHelperSet(); $initEvent = new ConsoleEvent($input, $output); + $initEvent->setHelperSet($helperSet); $dispatcher->dispatch(ConsoleEvents::INIT, $initEvent); $exitCode = parent::run($input, $output); $terminateEvent = new ConsoleTerminateEvent($input, $output, $exitCode); + $terminateEvent->setHelperSet($helperSet); $dispatcher->dispatch(ConsoleEvents::TERMINATE, $terminateEvent); return $exitCode; diff --git a/src/Symfony/Bundle/FrameworkBundle/Event/ConsoleEvent.php b/src/Symfony/Bundle/FrameworkBundle/Event/ConsoleEvent.php index 23b6b42f882c3..76a72d1e05d7e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Event/ConsoleEvent.php +++ b/src/Symfony/Bundle/FrameworkBundle/Event/ConsoleEvent.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Event; +use Symfony\Component\Console\Helper\HelperSet; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\EventDispatcher\Event; @@ -26,12 +27,34 @@ class ConsoleEvent extends Event private $output; + private $helperSet; + public function __construct(InputInterface $input, OutputInterface $output) { $this->input = $input; $this->output = $output; } + /** + * Sets the helper set. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } + /** * Returns the input object * From 4a630fde6b6a0f70b579fb2dd494332882030bc7 Mon Sep 17 00:00:00 2001 From: Bilal Amarni Date: Wed, 6 Feb 2013 11:19:28 +0100 Subject: [PATCH 3/3] [FrameworkBundle] tweaked some tests --- .../Tests/Console/ApplicationTest.php | 27 ++++++++++--------- .../Tests/Console/Fixtures/SilentCommand.php | 25 ----------------- 2 files changed, 14 insertions(+), 38 deletions(-) delete mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Console/Fixtures/SilentCommand.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php index b870e1d728584..8c83891ec0496 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php @@ -13,8 +13,8 @@ use Symfony\Bundle\FrameworkBundle\Tests\TestCase; use Symfony\Bundle\FrameworkBundle\Tests\Console\Fixtures\FooCommand; -use Symfony\Bundle\FrameworkBundle\Tests\Console\Fixtures\SilentCommand; use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Bundle\FrameworkBundle\Console\ConsoleEvents; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\NullOutput; @@ -51,16 +51,6 @@ public function testCommandDispatchEvents() $application->doRun(new ArrayInput(array('foo')), new NullOutput()); } - public function testSilentCommand() - { - $kernel = $this->getKernel(array(), $this->never()); - - $application = new Application($kernel); - $application->add(new SilentCommand('chut')); - - $application->doRun(new ArrayInput(array('chut')), new NullOutput()); - } - private function getKernel(array $bundles, $dispatcherExpected = null) { $kernel = $this->getMock("Symfony\Component\HttpKernel\KernelInterface"); @@ -80,8 +70,19 @@ private function getKernel(array $bundles, $dispatcherExpected = null) } else { $eventDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); $eventDispatcher - ->expects($this->atLeastOnce()) - ->method('dispatch'); + ->expects($this->at(0)) + ->method('dispatch') + ->with( + $this->equalTo(ConsoleEvents::INIT), + $this->isInstanceOf('Symfony\Bundle\FrameworkBundle\Event\ConsoleEvent') + ); + $eventDispatcher + ->expects($this->at(1)) + ->method('dispatch') + ->with( + $this->equalTo(ConsoleEvents::TERMINATE), + $this->isInstanceOf('Symfony\Bundle\FrameworkBundle\Event\ConsoleTerminateEvent') + ); $container ->expects($dispatcherExpected) ->method('get') diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Fixtures/SilentCommand.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Fixtures/SilentCommand.php deleted file mode 100644 index 1e3621b437c73..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Fixtures/SilentCommand.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\Console\Fixtures; - - -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; - -class SilentCommand extends Command -{ - protected function execute(InputInterface $input, OutputInterface $output) - { - return 0; - } -}