Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7dd1aaf

Browse filesBrowse files
committed
[FrameworkBundle] Catch Fatal errors in commands registration
1 parent fea348c commit 7dd1aaf
Copy full SHA for 7dd1aaf

File tree

3 files changed

+98
-7
lines changed
Filter options

3 files changed

+98
-7
lines changed

‎src/Symfony/Bundle/FrameworkBundle/Console/Application.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Console/Application.php
+42-2Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Console;
1313

14+
use Symfony\Component\Console\Exception\RuntimeException;
15+
use Symfony\Component\Debug\Exception\FatalThrowableError;
1416
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
1517
use Symfony\Component\Console\Application as BaseApplication;
1618
use Symfony\Component\Console\Command\Command;
@@ -30,6 +32,7 @@ class Application extends BaseApplication
3032
{
3133
private $kernel;
3234
private $commandsRegistered = false;
35+
private $registrationErrors = array();
3336

3437
/**
3538
* Constructor.
@@ -70,9 +73,25 @@ public function doRun(InputInterface $input, OutputInterface $output)
7073

7174
$this->setDispatcher($this->kernel->getContainer()->get('event_dispatcher'));
7275

76+
if ($this->registrationErrors) {
77+
$this->renderRegistrationErrors($output);
78+
}
79+
7380
return parent::doRun($input, $output);
7481
}
7582

83+
/**
84+
* {@inheritdoc}
85+
*/
86+
protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
87+
{
88+
if ($this->registrationErrors) {
89+
$this->renderRegistrationErrors($output);
90+
}
91+
92+
return parent::doRunCommand($command, $input, $output);
93+
}
94+
7695
/**
7796
* {@inheritdoc}
7897
*/
@@ -138,7 +157,13 @@ protected function registerCommands()
138157

139158
foreach ($this->kernel->getBundles() as $bundle) {
140159
if ($bundle instanceof Bundle) {
141-
$bundle->registerCommands($this);
160+
try {
161+
$bundle->registerCommands($this);
162+
} catch (\Exception $e) {
163+
$this->registrationErrors[] = $e;
164+
} catch (\Throwable $e) {
165+
$this->registrationErrors[] = new FatalThrowableError($e);
166+
}
142167
}
143168
}
144169

@@ -149,9 +174,24 @@ protected function registerCommands()
149174
if ($container->hasParameter('console.command.ids')) {
150175
foreach ($container->getParameter('console.command.ids') as $id) {
151176
if (false !== $id) {
152-
$this->add($container->get($id));
177+
try {
178+
$this->add($container->get($id));
179+
} catch (\Exception $e) {
180+
$this->registrationErrors[] = $e;
181+
} catch (\Throwable $e) {
182+
$this->registrationErrors[] = new FatalThrowableError($e);
183+
}
153184
}
154185
}
155186
}
156187
}
188+
189+
private function renderRegistrationErrors(OutputInterface $output)
190+
{
191+
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
192+
$this->doRenderException(new RuntimeException('Some commands could not be registered.'), $output);
193+
foreach ($this->registrationErrors as $error) {
194+
$this->doRenderException($error, $output);
195+
}
196+
}
157197
}

‎src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php
+43Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,13 @@
1515
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
1616
use Symfony\Component\Console\Command\Command;
1717
use Symfony\Component\Console\Input\ArrayInput;
18+
use Symfony\Component\Console\Input\InputInterface;
1819
use Symfony\Component\Console\Output\NullOutput;
20+
use Symfony\Component\Console\Output\OutputInterface;
1921
use Symfony\Component\Console\Tester\ApplicationTester;
22+
use Symfony\Component\DependencyInjection\ContainerBuilder;
23+
use Symfony\Component\EventDispatcher\EventDispatcher;
24+
use Symfony\Component\HttpKernel\KernelInterface;
2025

2126
class ApplicationTest extends TestCase
2227
{
@@ -130,6 +135,36 @@ public function testBundleCommandCanOverriddeAPreExistingCommandWithTheSameName(
130135
$this->assertSame($newCommand, $application->get('example'));
131136
}
132137

138+
public function testRunOnlyWarnsOnUnregistrableCommand()
139+
{
140+
$container = new ContainerBuilder();
141+
$container->register('event_dispatcher', EventDispatcher::class);
142+
$container->register(ThrowingCommand::class, ThrowingCommand::class);
143+
$container->setParameter('console.command.ids', array(ThrowingCommand::class => ThrowingCommand::class));
144+
145+
$kernel = $this->getMockBuilder(KernelInterface::class)->getMock();
146+
$kernel
147+
->method('getBundles')
148+
->willReturn(array($this->createBundleMock(
149+
array((new Command('fine'))->setCode(function (InputInterface $input, OutputInterface $output) { $output->write('fine'); }))
150+
)));
151+
$kernel
152+
->method('getContainer')
153+
->willReturn($container);
154+
155+
$application = new Application($kernel);
156+
$application->setAutoExit(false);
157+
158+
$tester = new ApplicationTester($application);
159+
$tester->run(array('command' => 'fine'));
160+
$output = $tester->getDisplay();
161+
162+
$this->assertSame(0, $tester->getStatusCode());
163+
$this->assertContains('Some commands could not be registered.', $output);
164+
$this->assertContains('throwing', $output);
165+
$this->assertContains('fine', $output);
166+
}
167+
133168
private function getKernel(array $bundles, $useDispatcher = false)
134169
{
135170
$container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock();
@@ -189,3 +224,11 @@ private function createBundleMock(array $commands)
189224
return $bundle;
190225
}
191226
}
227+
228+
class ThrowingCommand extends Command
229+
{
230+
public function __construct()
231+
{
232+
throw new \Exception('throwing');
233+
}
234+
}

‎src/Symfony/Component/Console/Application.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Application.php
+13-5Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,19 @@ public function renderException(\Exception $e, OutputInterface $output)
706706
{
707707
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
708708

709+
$this->doRenderException($e, $output);
710+
711+
if (null !== $this->runningCommand) {
712+
$output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET);
713+
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
714+
}
715+
}
716+
717+
/**
718+
* @internal
719+
*/
720+
protected function doRenderException(\Exception $e, OutputInterface $output)
721+
{
709722
do {
710723
$title = sprintf(
711724
' [%s%s] ',
@@ -767,11 +780,6 @@ public function renderException(\Exception $e, OutputInterface $output)
767780
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
768781
}
769782
} while ($e = $e->getPrevious());
770-
771-
if (null !== $this->runningCommand) {
772-
$output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET);
773-
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
774-
}
775783
}
776784

777785
/**

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.