feature #18140 [Console] Add console.ERROR event and deprecate console.EXCEPTION (wouterj)
This PR was merged into the 3.3-dev branch.
Discussion
----------
[Console] Add console.ERROR event and deprecate console.EXCEPTION
| Q | A |
| --- | --- |
| Branch | master |
| Bug fix? | yes |
| New feature? | yes |
| BC breaks? | no |
| Deprecations? | yes |
| Tests pass? | yes |
| Fixed tickets | - |
| License | MIT |
| Doc PR | todo |
## The Problem
The current `console.EXCEPTION` event is only dispatched for exceptions during the execution of `Command#execute()`. All other exceptions (e.g. the ones thrown by listeners to events) are catched by the `try ... catch` loop in `Application#doRunCommand()`. This means that there is _no way to override exception handling_.
## The Solution
This PR adds a `console.ERROR` event which has the same scope as the default `try ... catch` loop. This allows to customize all exception handling.
In order to keep BC, a new event was created and `console.EXCEPTION` was deprecated.
Commits
-------
c02a4c9857
Added a console.ERROR event
This commit is contained in:
commit
ba4d6bce29
@ -73,6 +73,13 @@ Debug
|
|||||||
|
|
||||||
* The `ContextErrorException` class is deprecated. `\ErrorException` will be used instead in 4.0.
|
* The `ContextErrorException` class is deprecated. `\ErrorException` will be used instead in 4.0.
|
||||||
|
|
||||||
|
Console
|
||||||
|
-------
|
||||||
|
|
||||||
|
* The `console.exception` event and the related `ConsoleExceptionEvent` class
|
||||||
|
have been deprecated in favor of the `console.error` event and the `ConsoleErrorEvent`
|
||||||
|
class. The deprecated event and class will be removed in 4.0.
|
||||||
|
|
||||||
DependencyInjection
|
DependencyInjection
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
@ -58,6 +58,9 @@ Console
|
|||||||
$commandTester->execute();
|
$commandTester->execute();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* The `console.exception` event and the related `ConsoleExceptionEvent` class have
|
||||||
|
been removed in favor of the `console.error` event and the `ConsoleErrorEvent` class.
|
||||||
|
|
||||||
Debug
|
Debug
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ use Symfony\Component\Console\Helper\HelperSet;
|
|||||||
use Symfony\Component\Console\Helper\Helper;
|
use Symfony\Component\Console\Helper\Helper;
|
||||||
use Symfony\Component\Console\Helper\FormatterHelper;
|
use Symfony\Component\Console\Helper\FormatterHelper;
|
||||||
use Symfony\Component\Console\Event\ConsoleCommandEvent;
|
use Symfony\Component\Console\Event\ConsoleCommandEvent;
|
||||||
|
use Symfony\Component\Console\Event\ConsoleErrorEvent;
|
||||||
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
|
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
|
||||||
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
|
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
|
||||||
use Symfony\Component\Console\Exception\CommandNotFoundException;
|
use Symfony\Component\Console\Exception\CommandNotFoundException;
|
||||||
@ -118,16 +119,40 @@ class Application
|
|||||||
$this->configureIO($input, $output);
|
$this->configureIO($input, $output);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
$e = null;
|
||||||
$exitCode = $this->doRun($input, $output);
|
$exitCode = $this->doRun($input, $output);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
$exception = $e;
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$exception = new FatalThrowableError($e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $e && null !== $this->dispatcher) {
|
||||||
|
$event = new ConsoleErrorEvent($this->runningCommand, $input, $output, $e, $e->getCode());
|
||||||
|
$this->dispatcher->dispatch(ConsoleEvents::ERROR, $event);
|
||||||
|
|
||||||
|
$e = $event->getError();
|
||||||
|
|
||||||
|
if ($event->isErrorHandled()) {
|
||||||
|
$e = null;
|
||||||
|
$exitCode = 0;
|
||||||
|
} else {
|
||||||
|
$exitCode = $e->getCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
$event = new ConsoleTerminateEvent($this->runningCommand, $input, $output, $exitCode);
|
||||||
|
$this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $e) {
|
||||||
if (!$this->catchExceptions) {
|
if (!$this->catchExceptions) {
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($output instanceof ConsoleOutputInterface) {
|
if ($output instanceof ConsoleOutputInterface) {
|
||||||
$this->renderException($e, $output->getErrorOutput());
|
$this->renderException($exception, $output->getErrorOutput());
|
||||||
} else {
|
} else {
|
||||||
$this->renderException($e, $output);
|
$this->renderException($exception, $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
$exitCode = $e->getCode();
|
$exitCode = $e->getCode();
|
||||||
@ -863,17 +888,17 @@ class Application
|
|||||||
} catch (\Throwable $x) {
|
} catch (\Throwable $x) {
|
||||||
$e = new FatalThrowableError($x);
|
$e = new FatalThrowableError($x);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null !== $e) {
|
if (null !== $e) {
|
||||||
$event = new ConsoleExceptionEvent($command, $input, $output, $e, $e->getCode());
|
$event = new ConsoleExceptionEvent($command, $input, $output, $e, $e->getCode(), false);
|
||||||
$this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event);
|
$this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event);
|
||||||
|
|
||||||
if ($e !== $event->getException()) {
|
if ($e !== $event->getException()) {
|
||||||
|
@trigger_error('The "console.exception" event is deprecated since version 3.3 and will be removed in 4.0. Use the "console.error" event instead.', E_USER_DEPRECATED);
|
||||||
|
|
||||||
$x = $e = $event->getException();
|
$x = $e = $event->getException();
|
||||||
}
|
}
|
||||||
|
|
||||||
$event = new ConsoleTerminateEvent($command, $input, $output, $e->getCode());
|
|
||||||
$this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
|
|
||||||
|
|
||||||
throw $x;
|
throw $x;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -8,6 +8,8 @@ CHANGELOG
|
|||||||
* added `AddConsoleCommandPass` (originally in FrameworkBundle)
|
* added `AddConsoleCommandPass` (originally in FrameworkBundle)
|
||||||
* [BC BREAK] `Input::getOption()` no longer returns the default value for options
|
* [BC BREAK] `Input::getOption()` no longer returns the default value for options
|
||||||
with value optional explicitly passed empty
|
with value optional explicitly passed empty
|
||||||
|
* added console.error event to catch exceptions thrown by other listeners
|
||||||
|
* deprecated console.exception event in favor of console.error
|
||||||
|
|
||||||
3.2.0
|
3.2.0
|
||||||
------
|
------
|
||||||
|
@ -40,7 +40,8 @@ final class ConsoleEvents
|
|||||||
const TERMINATE = 'console.terminate';
|
const TERMINATE = 'console.terminate';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The EXCEPTION event occurs when an uncaught exception appears.
|
* The EXCEPTION event occurs when an uncaught exception appears
|
||||||
|
* while executing Command#run().
|
||||||
*
|
*
|
||||||
* This event allows you to deal with the exception or
|
* This event allows you to deal with the exception or
|
||||||
* to modify the thrown exception.
|
* to modify the thrown exception.
|
||||||
@ -48,6 +49,21 @@ final class ConsoleEvents
|
|||||||
* @Event("Symfony\Component\Console\Event\ConsoleExceptionEvent")
|
* @Event("Symfony\Component\Console\Event\ConsoleExceptionEvent")
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
|
*
|
||||||
|
* @deprecated The console.exception event is deprecated since version 3.3 and will be removed in 4.0. Use the console.error event instead.
|
||||||
*/
|
*/
|
||||||
const EXCEPTION = 'console.exception';
|
const EXCEPTION = 'console.exception';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ERROR event occurs when an uncaught exception appears or
|
||||||
|
* a throwable error.
|
||||||
|
*
|
||||||
|
* This event allows you to deal with the exception/error or
|
||||||
|
* to modify the thrown exception.
|
||||||
|
*
|
||||||
|
* @Event("Symfony\Component\Console\Event\ConsoleErrorEvent")
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
const ERROR = 'console.error';
|
||||||
}
|
}
|
||||||
|
112
src/Symfony/Component/Console/Event/ConsoleErrorEvent.php
Normal file
112
src/Symfony/Component/Console/Event/ConsoleErrorEvent.php
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Console\Event;
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Debug\Exception\FatalThrowableError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows to handle throwables thrown while running a command.
|
||||||
|
*
|
||||||
|
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||||
|
*/
|
||||||
|
class ConsoleErrorEvent extends ConsoleExceptionEvent
|
||||||
|
{
|
||||||
|
private $error;
|
||||||
|
private $handled = false;
|
||||||
|
|
||||||
|
public function __construct(Command $command, InputInterface $input, OutputInterface $output, $error, $exitCode)
|
||||||
|
{
|
||||||
|
if (!$error instanceof \Throwable && !$error instanceof \Exception) {
|
||||||
|
throw new InvalidArgumentException(sprintf('The error passed to ConsoleErrorEvent must be an instance of \Throwable or \Exception, "%s" was passed instead.', is_object($error) ? get_class($error) : gettype($error)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$exception = $error;
|
||||||
|
if (!$error instanceof \Exception) {
|
||||||
|
$exception = new FatalThrowableError($error);
|
||||||
|
}
|
||||||
|
parent::__construct($command, $input, $output, $exception, $exitCode, false);
|
||||||
|
|
||||||
|
$this->error = $error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the thrown error/exception.
|
||||||
|
*
|
||||||
|
* @return \Throwable
|
||||||
|
*/
|
||||||
|
public function getError()
|
||||||
|
{
|
||||||
|
return $this->error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the thrown error/exception.
|
||||||
|
*
|
||||||
|
* @param \Throwable $error
|
||||||
|
*/
|
||||||
|
public function setError($error)
|
||||||
|
{
|
||||||
|
if (!$error instanceof \Throwable && !$error instanceof \Exception) {
|
||||||
|
throw new InvalidArgumentException(sprintf('The error passed to ConsoleErrorEvent must be an instance of \Throwable or \Exception, "%s" was passed instead.', is_object($error) ? get_class($error) : gettype($error)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->error = $error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the error/exception as handled.
|
||||||
|
*
|
||||||
|
* If it is not marked as handled, the error/exception will be displayed in
|
||||||
|
* the command output.
|
||||||
|
*/
|
||||||
|
public function markErrorAsHandled()
|
||||||
|
{
|
||||||
|
$this->handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the error/exception is handled by a listener or not.
|
||||||
|
*
|
||||||
|
* If it is not yet handled, the error/exception will be displayed in the
|
||||||
|
* command output.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isErrorHandled()
|
||||||
|
{
|
||||||
|
return $this->handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Since version 3.3, to be removed in 4.0. Use getError() instead
|
||||||
|
*/
|
||||||
|
public function getException()
|
||||||
|
{
|
||||||
|
@trigger_error(sprintf('The %s() method is deprecated since version 3.3 and will be removed in 4.0. Use ConsoleErrorEvent::getError() instead.', __METHOD__), E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
return parent::getException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Since version 3.3, to be removed in 4.0. Use setError() instead
|
||||||
|
*/
|
||||||
|
public function setException(\Exception $exception)
|
||||||
|
{
|
||||||
|
@trigger_error(sprintf('The %s() method is deprecated since version 3.3 and will be removed in 4.0. Use ConsoleErrorEvent::setError() instead.', __METHOD__), E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
parent::setException($exception);
|
||||||
|
}
|
||||||
|
}
|
@ -28,7 +28,7 @@ class ConsoleEvent extends Event
|
|||||||
private $input;
|
private $input;
|
||||||
private $output;
|
private $output;
|
||||||
|
|
||||||
public function __construct(Command $command, InputInterface $input, OutputInterface $output)
|
public function __construct(Command $command = null, InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$this->command = $command;
|
$this->command = $command;
|
||||||
$this->input = $input;
|
$this->input = $input;
|
||||||
@ -38,7 +38,7 @@ class ConsoleEvent extends Event
|
|||||||
/**
|
/**
|
||||||
* Gets the command that is executed.
|
* Gets the command that is executed.
|
||||||
*
|
*
|
||||||
* @return Command A Command instance
|
* @return Command|null A Command instance
|
||||||
*/
|
*/
|
||||||
public function getCommand()
|
public function getCommand()
|
||||||
{
|
{
|
||||||
|
@ -19,17 +19,24 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||||||
* Allows to handle exception thrown in a command.
|
* Allows to handle exception thrown in a command.
|
||||||
*
|
*
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* @deprecated ConsoleExceptionEvent is deprecated since version 3.3 and will be removed in 4.0. Use ConsoleErrorEvent instead.
|
||||||
*/
|
*/
|
||||||
class ConsoleExceptionEvent extends ConsoleEvent
|
class ConsoleExceptionEvent extends ConsoleEvent
|
||||||
{
|
{
|
||||||
private $exception;
|
private $exception;
|
||||||
private $exitCode;
|
private $exitCode;
|
||||||
|
private $handled = false;
|
||||||
|
|
||||||
public function __construct(Command $command, InputInterface $input, OutputInterface $output, \Exception $exception, $exitCode)
|
public function __construct(Command $command, InputInterface $input, OutputInterface $output, \Exception $exception, $exitCode, $deprecation = true)
|
||||||
{
|
{
|
||||||
|
if ($deprecation) {
|
||||||
|
@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use the ConsoleErrorEvent instead.', __CLASS__), E_USER_DEPRECATED);
|
||||||
|
}
|
||||||
|
|
||||||
parent::__construct($command, $input, $output);
|
parent::__construct($command, $input, $output);
|
||||||
|
|
||||||
$this->setException($exception);
|
$this->exception = $exception;
|
||||||
$this->exitCode = (int) $exitCode;
|
$this->exitCode = (int) $exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ class ConsoleTerminateEvent extends ConsoleEvent
|
|||||||
*/
|
*/
|
||||||
private $exitCode;
|
private $exitCode;
|
||||||
|
|
||||||
public function __construct(Command $command, InputInterface $input, OutputInterface $output, $exitCode)
|
public function __construct(Command $command = null, InputInterface $input, OutputInterface $output, $exitCode)
|
||||||
{
|
{
|
||||||
parent::__construct($command, $input, $output);
|
parent::__construct($command, $input, $output);
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ namespace Symfony\Component\Console\EventListener;
|
|||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\Console\Event\ConsoleEvent;
|
use Symfony\Component\Console\Event\ConsoleEvent;
|
||||||
use Symfony\Component\Console\ConsoleEvents;
|
use Symfony\Component\Console\ConsoleEvents;
|
||||||
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
|
use Symfony\Component\Console\Event\ConsoleErrorEvent;
|
||||||
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
|
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
|
||||||
@ -31,15 +31,15 @@ class ExceptionListener implements EventSubscriberInterface
|
|||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onConsoleException(ConsoleExceptionEvent $event)
|
public function onConsoleError(ConsoleErrorEvent $event)
|
||||||
{
|
{
|
||||||
if (null === $this->logger) {
|
if (null === $this->logger) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$exception = $event->getException();
|
$error = $event->getError();
|
||||||
|
|
||||||
$this->logger->error('Exception thrown while running command "{command}". Message: "{message}"', array('exception' => $exception, 'command' => $this->getInputString($event), 'message' => $exception->getMessage()));
|
$this->logger->error('Error thrown while running command "{command}". Message: "{message}"', array('error' => $error, 'command' => $this->getInputString($event), 'message' => $error->getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onConsoleTerminate(ConsoleTerminateEvent $event)
|
public function onConsoleTerminate(ConsoleTerminateEvent $event)
|
||||||
@ -60,7 +60,7 @@ class ExceptionListener implements EventSubscriberInterface
|
|||||||
public static function getSubscribedEvents()
|
public static function getSubscribedEvents()
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
ConsoleEvents::EXCEPTION => array('onConsoleException', -128),
|
ConsoleEvents::ERROR => array('onConsoleError', -128),
|
||||||
ConsoleEvents::TERMINATE => array('onConsoleTerminate', -128),
|
ConsoleEvents::TERMINATE => array('onConsoleTerminate', -128),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||||||
use Symfony\Component\Console\Output\StreamOutput;
|
use Symfony\Component\Console\Output\StreamOutput;
|
||||||
use Symfony\Component\Console\Tester\ApplicationTester;
|
use Symfony\Component\Console\Tester\ApplicationTester;
|
||||||
use Symfony\Component\Console\Event\ConsoleCommandEvent;
|
use Symfony\Component\Console\Event\ConsoleCommandEvent;
|
||||||
|
use Symfony\Component\Console\Event\ConsoleErrorEvent;
|
||||||
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
|
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
|
||||||
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
|
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
|
||||||
use Symfony\Component\Console\Exception\CommandNotFoundException;
|
use Symfony\Component\Console\Exception\CommandNotFoundException;
|
||||||
@ -969,7 +970,7 @@ class ApplicationTest extends TestCase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \LogicException
|
* @expectedException \LogicException
|
||||||
* @expectedExceptionMessage caught
|
* @expectedExceptionMessage error
|
||||||
*/
|
*/
|
||||||
public function testRunWithExceptionAndDispatcher()
|
public function testRunWithExceptionAndDispatcher()
|
||||||
{
|
{
|
||||||
@ -1000,7 +1001,77 @@ class ApplicationTest extends TestCase
|
|||||||
|
|
||||||
$tester = new ApplicationTester($application);
|
$tester = new ApplicationTester($application);
|
||||||
$tester->run(array('command' => 'foo'));
|
$tester->run(array('command' => 'foo'));
|
||||||
$this->assertContains('before.foo.caught.after.', $tester->getDisplay());
|
$this->assertContains('before.foo.error.after.', $tester->getDisplay());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRunDispatchesAllEventsWithExceptionInListener()
|
||||||
|
{
|
||||||
|
$dispatcher = $this->getDispatcher();
|
||||||
|
$dispatcher->addListener('console.command', function () {
|
||||||
|
throw new \RuntimeException('foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
$application = new Application();
|
||||||
|
$application->setDispatcher($dispatcher);
|
||||||
|
$application->setAutoExit(false);
|
||||||
|
|
||||||
|
$application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) {
|
||||||
|
$output->write('foo.');
|
||||||
|
});
|
||||||
|
|
||||||
|
$tester = new ApplicationTester($application);
|
||||||
|
$tester->run(array('command' => 'foo'));
|
||||||
|
$this->assertContains('before.error.after.', $tester->getDisplay());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRunAllowsErrorListenersToSilenceTheException()
|
||||||
|
{
|
||||||
|
$dispatcher = $this->getDispatcher();
|
||||||
|
$dispatcher->addListener('console.error', function (ConsoleErrorEvent $event) {
|
||||||
|
$event->getOutput()->write('silenced.');
|
||||||
|
|
||||||
|
$event->markErrorAsHandled();
|
||||||
|
});
|
||||||
|
|
||||||
|
$dispatcher->addListener('console.command', function () {
|
||||||
|
throw new \RuntimeException('foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
$application = new Application();
|
||||||
|
$application->setDispatcher($dispatcher);
|
||||||
|
$application->setAutoExit(false);
|
||||||
|
|
||||||
|
$application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) {
|
||||||
|
$output->write('foo.');
|
||||||
|
});
|
||||||
|
|
||||||
|
$tester = new ApplicationTester($application);
|
||||||
|
$tester->run(array('command' => 'foo'));
|
||||||
|
$this->assertContains('before.error.silenced.after.', $tester->getDisplay());
|
||||||
|
$this->assertEquals(0, $tester->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLegacyExceptionListenersAreStillTriggered()
|
||||||
|
{
|
||||||
|
$dispatcher = $this->getDispatcher();
|
||||||
|
$dispatcher->addListener('console.exception', function (ConsoleExceptionEvent $event) {
|
||||||
|
$event->getOutput()->write('caught.');
|
||||||
|
|
||||||
|
$event->setException(new \RuntimeException('replaced in caught.'));
|
||||||
|
});
|
||||||
|
|
||||||
|
$application = new Application();
|
||||||
|
$application->setDispatcher($dispatcher);
|
||||||
|
$application->setAutoExit(false);
|
||||||
|
|
||||||
|
$application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) {
|
||||||
|
throw new \RuntimeException('foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
$tester = new ApplicationTester($application);
|
||||||
|
$tester->run(array('command' => 'foo'));
|
||||||
|
$this->assertContains('before.caught.error.after.', $tester->getDisplay());
|
||||||
|
$this->assertContains('replaced in caught.', $tester->getDisplay());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRunWithError()
|
public function testRunWithError()
|
||||||
@ -1028,7 +1099,7 @@ class ApplicationTest extends TestCase
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \LogicException
|
* @expectedException \LogicException
|
||||||
* @expectedExceptionMessage caught
|
* @expectedExceptionMessage error
|
||||||
*/
|
*/
|
||||||
public function testRunWithErrorAndDispatcher()
|
public function testRunWithErrorAndDispatcher()
|
||||||
{
|
{
|
||||||
@ -1045,7 +1116,7 @@ class ApplicationTest extends TestCase
|
|||||||
|
|
||||||
$tester = new ApplicationTester($application);
|
$tester = new ApplicationTester($application);
|
||||||
$tester->run(array('command' => 'dym'));
|
$tester->run(array('command' => 'dym'));
|
||||||
$this->assertContains('before.dym.caught.after.', $tester->getDisplay(), 'The PHP Error did not dispached events');
|
$this->assertContains('before.dym.error.after.', $tester->getDisplay(), 'The PHP Error did not dispached events');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRunDispatchesAllEventsWithError()
|
public function testRunDispatchesAllEventsWithError()
|
||||||
@ -1062,7 +1133,7 @@ class ApplicationTest extends TestCase
|
|||||||
|
|
||||||
$tester = new ApplicationTester($application);
|
$tester = new ApplicationTester($application);
|
||||||
$tester->run(array('command' => 'dym'));
|
$tester->run(array('command' => 'dym'));
|
||||||
$this->assertContains('before.dym.caught.after.', $tester->getDisplay(), 'The PHP Error did not dispached events');
|
$this->assertContains('before.dym.error.after.', $tester->getDisplay(), 'The PHP Error did not dispached events');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRunWithErrorFailingStatusCode()
|
public function testRunWithErrorFailingStatusCode()
|
||||||
@ -1173,32 +1244,6 @@ class ApplicationTest extends TestCase
|
|||||||
$this->assertSame(array($width, 80), $application->getTerminalDimensions());
|
$this->assertSame(array($width, 80), $application->getTerminalDimensions());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getDispatcher($skipCommand = false)
|
|
||||||
{
|
|
||||||
$dispatcher = new EventDispatcher();
|
|
||||||
$dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use ($skipCommand) {
|
|
||||||
$event->getOutput()->write('before.');
|
|
||||||
|
|
||||||
if ($skipCommand) {
|
|
||||||
$event->disableCommand();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$dispatcher->addListener('console.terminate', function (ConsoleTerminateEvent $event) use ($skipCommand) {
|
|
||||||
$event->getOutput()->writeln('after.');
|
|
||||||
|
|
||||||
if (!$skipCommand) {
|
|
||||||
$event->setExitCode(113);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$dispatcher->addListener('console.exception', function (ConsoleExceptionEvent $event) {
|
|
||||||
$event->getOutput()->write('caught.');
|
|
||||||
|
|
||||||
$event->setException(new \LogicException('caught.', $event->getExitCode(), $event->getException()));
|
|
||||||
});
|
|
||||||
|
|
||||||
return $dispatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSetRunCustomDefaultCommand()
|
public function testSetRunCustomDefaultCommand()
|
||||||
{
|
{
|
||||||
$command = new \FooCommand();
|
$command = new \FooCommand();
|
||||||
@ -1255,6 +1300,32 @@ class ApplicationTest extends TestCase
|
|||||||
$inputStream = $tester->getInput()->getStream();
|
$inputStream = $tester->getInput()->getStream();
|
||||||
$this->assertEquals($tester->getInput()->isInteractive(), @posix_isatty($inputStream));
|
$this->assertEquals($tester->getInput()->isInteractive(), @posix_isatty($inputStream));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getDispatcher($skipCommand = false)
|
||||||
|
{
|
||||||
|
$dispatcher = new EventDispatcher();
|
||||||
|
$dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use ($skipCommand) {
|
||||||
|
$event->getOutput()->write('before.');
|
||||||
|
|
||||||
|
if ($skipCommand) {
|
||||||
|
$event->disableCommand();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$dispatcher->addListener('console.terminate', function (ConsoleTerminateEvent $event) use ($skipCommand) {
|
||||||
|
$event->getOutput()->writeln('after.');
|
||||||
|
|
||||||
|
if (!$skipCommand) {
|
||||||
|
$event->setExitCode(113);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$dispatcher->addListener('console.error', function (ConsoleErrorEvent $event) {
|
||||||
|
$event->getOutput()->write('error.');
|
||||||
|
|
||||||
|
$event->setError(new \LogicException('error.', $event->getExitCode(), $event->getError()));
|
||||||
|
});
|
||||||
|
|
||||||
|
return $dispatcher;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CustomApplication extends Application
|
class CustomApplication extends Application
|
||||||
|
@ -14,7 +14,7 @@ namespace Symfony\Component\Console\Tests\EventListener;
|
|||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
|
use Symfony\Component\Console\Event\ConsoleErrorEvent;
|
||||||
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
|
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
|
||||||
use Symfony\Component\Console\EventListener\ExceptionListener;
|
use Symfony\Component\Console\EventListener\ExceptionListener;
|
||||||
use Symfony\Component\Console\Input\ArgvInput;
|
use Symfony\Component\Console\Input\ArgvInput;
|
||||||
@ -25,7 +25,7 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||||||
|
|
||||||
class ExceptionListenerTest extends TestCase
|
class ExceptionListenerTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testOnConsoleException()
|
public function testOnConsoleError()
|
||||||
{
|
{
|
||||||
$exception = new \RuntimeException('An error occurred');
|
$exception = new \RuntimeException('An error occurred');
|
||||||
|
|
||||||
@ -33,11 +33,11 @@ class ExceptionListenerTest extends TestCase
|
|||||||
$logger
|
$logger
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('error')
|
->method('error')
|
||||||
->with('Exception thrown while running command "{command}". Message: "{message}"', array('exception' => $exception, 'command' => 'test:run --foo=baz buzz', 'message' => 'An error occurred'))
|
->with('Error thrown while running command "{command}". Message: "{message}"', array('error' => $exception, 'command' => 'test:run --foo=baz buzz', 'message' => 'An error occurred'))
|
||||||
;
|
;
|
||||||
|
|
||||||
$listener = new ExceptionListener($logger);
|
$listener = new ExceptionListener($logger);
|
||||||
$listener->onConsoleException($this->getConsoleExceptionEvent($exception, new ArgvInput(array('console.php', 'test:run', '--foo=baz', 'buzz')), 1));
|
$listener->onConsoleError($this->getConsoleErrorEvent($exception, new ArgvInput(array('console.php', 'test:run', '--foo=baz', 'buzz')), 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testOnConsoleTerminateForNonZeroExitCodeWritesToLog()
|
public function testOnConsoleTerminateForNonZeroExitCodeWritesToLog()
|
||||||
@ -69,7 +69,7 @@ class ExceptionListenerTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array(
|
array(
|
||||||
'console.exception' => array('onConsoleException', -128),
|
'console.error' => array('onConsoleError', -128),
|
||||||
'console.terminate' => array('onConsoleTerminate', -128),
|
'console.terminate' => array('onConsoleTerminate', -128),
|
||||||
),
|
),
|
||||||
ExceptionListener::getSubscribedEvents()
|
ExceptionListener::getSubscribedEvents()
|
||||||
@ -109,9 +109,9 @@ class ExceptionListenerTest extends TestCase
|
|||||||
return $this->getMockForAbstractClass(LoggerInterface::class);
|
return $this->getMockForAbstractClass(LoggerInterface::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getConsoleExceptionEvent(\Exception $exception, InputInterface $input, $exitCode)
|
private function getConsoleErrorEvent(\Exception $exception, InputInterface $input, $exitCode)
|
||||||
{
|
{
|
||||||
return new ConsoleExceptionEvent(new Command('test:run'), $input, $this->getOutput(), $exception, $exitCode);
|
return new ConsoleErrorEvent(new Command('test:run'), $input, $this->getOutput(), $exception, $exitCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getConsoleTerminateEvent(InputInterface $input, $exitCode)
|
private function getConsoleTerminateEvent(InputInterface $input, $exitCode)
|
||||||
|
Reference in New Issue
Block a user