diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index b7aaa6a29e65a..842ef19070128 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -79,6 +79,7 @@ class Application implements ResetInterface private string $version; private ?CommandLoaderInterface $commandLoader = null; private bool $catchExceptions = true; + private bool $catchErrors = false; private bool $autoExit = true; private InputDefinition $definition; private HelperSet $helperSet; @@ -172,8 +173,11 @@ public function run(InputInterface $input = null, OutputInterface $output = null try { $exitCode = $this->doRun($input, $output); - } catch (\Exception $e) { - if (!$this->catchExceptions) { + } catch (\Throwable $e) { + if ($e instanceof \Exception && !$this->catchExceptions) { + throw $e; + } + if (!$e instanceof \Exception && !$this->catchErrors) { throw $e; } @@ -427,6 +431,14 @@ public function setCatchExceptions(bool $boolean) $this->catchExceptions = $boolean; } + /** + * Sets whether to catch errors or not during commands execution. + */ + public function setCatchErrors(bool $catchErrors = true): void + { + $this->catchErrors = $catchErrors; + } + /** * Gets whether to automatically exit after a command execution or not. */ diff --git a/src/Symfony/Component/Console/CHANGELOG.md b/src/Symfony/Component/Console/CHANGELOG.md index 48b8f5a707c51..7132a052c8ec0 100644 --- a/src/Symfony/Component/Console/CHANGELOG.md +++ b/src/Symfony/Component/Console/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * Add `SignalMap` to map signal value to its name * Multi-line text in vertical tables is aligned properly + * The application can also catch errors with `Application::setCatchErrors(true)` 6.3 --- diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 229297c654c00..e59942ac8cc01 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -771,10 +771,15 @@ public function testFindAmbiguousCommandsIfAllAlternativesAreHidden() $this->assertInstanceOf(\FooCommand::class, $application->find('foo:')); } - public function testSetCatchExceptions() + /** + * @testWith [true] + * [false] + */ + public function testSetCatchExceptions(bool $catchErrors) { $application = new Application(); $application->setAutoExit(false); + $application->setCatchErrors($catchErrors); putenv('COLUMNS=120'); $tester = new ApplicationTester($application); @@ -798,6 +803,33 @@ public function testSetCatchExceptions() } } + /** + * @testWith [true] + * [false] + */ + public function testSetCatchErrors(bool $catchExceptions) + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions($catchExceptions); + $application->add((new Command('boom'))->setCode(fn () => throw new \Error('This is an error.'))); + + putenv('COLUMNS=120'); + $tester = new ApplicationTester($application); + + try { + $tester->run(['command' => 'boom']); + $this->fail('The exception is not catched.'); + } catch (\Throwable $e) { + $this->assertInstanceOf(\Error::class, $e); + $this->assertSame('This is an error.', $e->getMessage()); + } + + $application->setCatchErrors(true); + $tester->run(['command' => 'boom']); + $this->assertStringContainsString(' This is an error.', $tester->getDisplay(true)); + } + public function testAutoExitSetting() { $application = new Application();