bug #22322 [Console] Fix fatal error when logging console.error without command (chalasr)

This PR was merged into the 3.3-dev branch.

Discussion
----------

[Console] Fix fatal error when logging console.error without command

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #22449
| License       | MIT
| Doc PR        | n/a

Happens now that the console.error event is dispatched on command not found, I  ran into using `server:run` on 3.3 without `web-server-bundle` enabled:

> PHP Fatal error:  Uncaught Symfony\Component\Debug\Exception\FatalThrowableError: Call to a member function getName() on null

In this case, this first tries to cast the event input as string (less good than when the command name is available, there're extra quotes around the command name) and, if can't be casted, uses a generic message.

Commits
-------

97129fc611 Fix fatal error when logging console.error without a command
This commit is contained in:
Fabien Potencier 2017-04-19 17:40:30 -06:00
commit 9c0067b19f
2 changed files with 54 additions and 7 deletions

View File

@ -39,7 +39,11 @@ class ExceptionListener implements EventSubscriberInterface
$error = $event->getError();
$this->logger->error('Error thrown while running command "{command}". Message: "{message}"', array('error' => $error, 'command' => $this->getInputString($event), 'message' => $error->getMessage()));
if (!$inputString = $this->getInputString($event)) {
return $this->logger->error('An error occurred while using the console. Message: "{message}"', array('error' => $error, 'message' => $error->getMessage()));
}
$this->logger->error('Error thrown while running command "{command}". Message: "{message}"', array('error' => $error, 'command' => $inputString, 'message' => $error->getMessage()));
}
public function onConsoleTerminate(ConsoleTerminateEvent $event)
@ -54,7 +58,11 @@ class ExceptionListener implements EventSubscriberInterface
return;
}
$this->logger->error('Command "{command}" exited with code "{code}"', array('command' => $this->getInputString($event), 'code' => $exitCode));
if (!$inputString = $this->getInputString($event)) {
return $this->logger->error('The console exited with code "{code}"', array('code' => $exitCode));
}
$this->logger->error('Command "{command}" exited with code "{code}"', array('command' => $inputString, 'code' => $exitCode));
}
public static function getSubscribedEvents()
@ -67,11 +75,15 @@ class ExceptionListener implements EventSubscriberInterface
private static function getInputString(ConsoleEvent $event)
{
$commandName = $event->getCommand()->getName();
$commandName = $event->getCommand() ? $event->getCommand()->getName() : null;
$input = $event->getInput();
if (method_exists($input, '__toString')) {
return str_replace(array("'$commandName'", "\"$commandName\""), $commandName, (string) $input);
if ($commandName) {
return str_replace(array("'$commandName'", "\"$commandName\""), $commandName, (string) $input);
}
return (string) $input;
}
return $commandName;

View File

@ -19,6 +19,7 @@ use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\Console\EventListener\ExceptionListener;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\Input;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@ -37,7 +38,22 @@ class ExceptionListenerTest extends TestCase
;
$listener = new ExceptionListener($logger);
$listener->onConsoleError($this->getConsoleErrorEvent($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, new Command('test:run')));
}
public function testOnConsoleErrorWithNoCommandAndNoInputString()
{
$exception = new \RuntimeException('An error occurred');
$logger = $this->getLogger();
$logger
->expects($this->once())
->method('error')
->with('An error occurred while using the console. Message: "{message}"', array('error' => $exception, 'message' => 'An error occurred'))
;
$listener = new ExceptionListener($logger);
$listener->onConsoleError($this->getConsoleErrorEvent($exception, new NonStringInput(), 1));
}
public function testOnConsoleTerminateForNonZeroExitCodeWritesToLog()
@ -109,9 +125,9 @@ class ExceptionListenerTest extends TestCase
return $this->getMockForAbstractClass(LoggerInterface::class);
}
private function getConsoleErrorEvent(\Exception $exception, InputInterface $input, $exitCode)
private function getConsoleErrorEvent(\Exception $exception, InputInterface $input, $exitCode, Command $command = null)
{
return new ConsoleErrorEvent($input, $this->getOutput(), $exception, $exitCode, new Command('test:run'));
return new ConsoleErrorEvent($input, $this->getOutput(), $exception, $exitCode, $command);
}
private function getConsoleTerminateEvent(InputInterface $input, $exitCode)
@ -124,3 +140,22 @@ class ExceptionListenerTest extends TestCase
return $this->getMockBuilder(OutputInterface::class)->getMock();
}
}
class NonStringInput extends Input
{
public function getFirstArgument()
{
}
public function hasParameterOption($values, $onlyParams = false)
{
}
public function getParameterOption($values, $default = false, $onlyParams = false)
{
}
public function parse()
{
}
}