diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index df2e1ce152..08abac098b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 2.3.0 ----- + * added an init and terminate event dispatched by CLI commands * added `--clean` option the the `translation:update` command * added `http_method_override` option diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php index e2bade38a7..d2c08175a1 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,8 +24,10 @@ use Symfony\Component\Finder\Finder; * @author Francis Besset * @author Fabien Potencier */ -class CacheClearCommand extends ContainerAwareCommand +class CacheClearCommand extends Command { + private $container; + /** * {@inheritdoc} */ @@ -197,4 +200,16 @@ EOF; 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 035f5536ee..13a14f3810 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 0000000000..b8fee5fcee --- /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 0000000000..23b6b42f88 --- /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 0000000000..8ade530a85 --- /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 1427453b10..b870e1d728 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 @@ class ApplicationTest extends TestCase { $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 @@ class ApplicationTest extends TestCase $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 @@ class ApplicationTest extends TestCase ->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 0000000000..513f60198f --- /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 0000000000..1e3621b437 --- /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; + } +}