8000 feature #23836 [FrameworkBundle] Catch Fatal errors in commands regis… · symfony/symfony@1982fff · GitHub
[go: up one dir, main page]

Skip to content 10000

Commit 1982fff

Browse files
committed
feature #23836 [FrameworkBundle] Catch Fatal errors in commands registration (chalasr)
This PR was merged into the 3.4 branch. Discussion ---------- [FrameworkBundle] Catch Fatal errors in commands registration | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #23610 | License | MIT | Doc PR | n/a #### before ![before](https://image.prntscr.com/image/QDmEVSPiQY6VCY7PoNAIbg.png) #### after ![after](https://image.prntscr.com/image/B2EuZtzwTfiVS6F3kTAa4Q.png) Trying to run the failing command itself ![failing](https://image.prntscr.com/image/7RgdtCVyQXyVit8TPkh98A.png) Exceptions/errors thrown in `registerCommands()` (bundles/non-lazy command services registration) are caught and displayed as a warning, allowing to run other valid commands. Commits ------- 46b6b42 [FrameworkBundle] Catch Fatal errors in commands registration
2 parents 01d1563 + 46b6b42 commit 1982fff

File tree

3 files changed

+102
-7
lines changed

3 files changed

+102
-7
lines changed

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

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Console;
1313

14+
use Symfony\Component\Console\Output\ConsoleOutputInterface;
15+
use Symfony\Component\Console\Style\SymfonyStyle;
16+
use Symfony\Component\Debug\Exception\FatalThrowableError;
1417
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
1518
use Symfony\Component\Console\Application as BaseApplication;
1619
use Symfony\Component\Console\Command\Command;
@@ -30,6 +33,7 @@ class Application extends BaseApplication
3033
{
3134
private $kernel;
3235
private $commandsRegistered = false;
36+
private $registrationErrors = array();
3337

3438
/**
3539
* Constructor.
@@ -70,9 +74,25 @@ public function doRun(InputInterface $input, OutputInterface $output)
7074

7175
$this->setDispatcher($this->kernel->getContainer()->get('event_dispatcher'));
7276

77+
if ($this->registrationErrors) {
78+
$this->renderRegistrationErrors($input, $output);
79+
}
80+
7381
return parent::doRun($input, $output);
7482
}
7583

84+
/**
85+
* {@inheritdoc}
86+
*/
87+
protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
88+
{
89+
if ($this->registrationErrors) {
90+
$this->renderRegistrationErrors($input, $output);
91+
}
92+
93+
return parent::doRunCommand($command, $input, $output);
94+
}
95+
7696
/**
7797
* {@inheritdoc}
7898
*/
@@ -138,7 +158,13 @@ protected function registerCommands()
138158

139159
foreach ($this->kernel->getBundles() as $bundle) {
140160
if ($bundle instanceof Bundle) {
141-
$bundle->registerCommands($this);
161+
try {
162+
$bundle->registerCommands($this);
163+
} catch (\Exception $e) {
164+
$this->registrationErrors[] = $e;
165+
} catch (\Throwable $e) {
166+
$this->registrationErrors[] = new FatalThrowableError($e);
167+
}
142168
}
143169
}
144170

@@ -149,9 +175,30 @@ protected function registerCommands()
149175
if ($container->hasParameter('console.command.ids')) {
150176
foreach ($container->getParameter('console.command.ids') as $id) {
151177
if (false !== $id) {
152-
$this->add($container->get($id));
178+
try {
179+
$this->add($container->get($id));
180+
} catch (\Exception $e) {
181+
$this->registrationErrors[] = $e;
182+
} catch (\Throwable $e) {
183+
$this->registrationErrors[] = new FatalThrowableError($e);
184+
}
153185
}
154186
}
155187
}
156188
}
189+
190+
private function renderRegistrationErrors(InputInterface $input, OutputInterface $output)
191+
{
192+
if ($output instanceof ConsoleOutputInterface) {
193+
$output = $output->getErrorOutput();
194+
}
195+
196+
(new SymfonyStyle($input, $output))->warning('Some commands could not be registered.');
197+
198+
foreach ($this->registrationErrors as $error) {
199+
$this->doRenderException($error, $output);
200+
}
201+
202+
$this->registrationErrors = array();
203+
}
157204
}

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

Lines 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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,16 @@ 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+
protected function doRenderException(\Exception $e, OutputInterface $output)
718+
{
709719
do {
710720
$title = sprintf(
711721
' [%s%s] ',
@@ -767,11 +777,6 @@ public function renderException(\Exception $e, OutputInterface $output)
767777
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
768778
}
769779
} 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-
}
775780
}
776781

777782
/**

0 commit comments

Comments
 (0)
0