-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Console] Allow Application to handle PHP 7 Errors #20808
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
It's surely debatable but I would use |
*/ | ||
public function areThrowablesCaught() | ||
{ | ||
return $this->catchExceptions; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be return $this->catchThrowables
I guess
$application->setCatchThrowables(true); | ||
$application->setAutoExit(false); | ||
|
||
$this->assertTrue($application->areThrowablesCaught()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe a test should be added for ensuring they are not caught by default?
*/ | ||
public function renderException(\Exception $e, OutputInterface $output) | ||
public function renderException(/*\Throwable */$e, OutputInterface $output) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removing the typehint is a BC break for classes extending the method (and there are such classes out there)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. For now I've deprecated the renderException
method in favor of renderThrowable
.
The typehint is still commented and would be uncommented as soon as the PHP 5 support is dropped. I guess it's likely to happen for 4.0, so I can add it to UPGRADE-4.0.md
file?
@@ -62,6 +62,7 @@ class Application | |||
private $name; | |||
private $version; | |||
private $catchExceptions = true; | |||
private $catchThrowables = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think having a second flag makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would you suggest instead?
We still have to differentiate exceptions from other throwables, as the last ones are currently handled in the fullstack by an exception handler, whereas exceptions are handled by the Console component itself.
We could make catchExceptions
accept bit flags (Application::CATCH_EXCEPTIONS
, Application::CATCH_THROWABLES
) and consider true
the same as Application::CATCH_EXCEPTIONS
only. But I don't like it semantically and would complicate things.
I asked myself about that. But it might be misleading for PHP 5 users (actual php 5 errors won't be handled by the application). |
From the PHP manual:
Which class being neither a subclass of |
You're right. Forget about this argument. Still it might be misleading for PHP 5 users. Unless we register |
How bad would it be to handle errors by using the |
I would not rename |
Fair enough. Last chance for this then is to create a special exception wrapping an |
There is no more BC break nor deprecation here AFAIK. So the label can be removed :) The typehint will however change in 4.0 without BC layer between, as it was done in 3.0 (#13756). Or we can keep the internal |
public function setCatchErrors($boolean) | ||
{ | ||
$this->catchErrors = (bool) $boolean; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The addition of these 2 methods does not seem related to this PR and should be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- I doubt about the usefulness of
areErrorsCaught
right now, but this echoesareExceptionsCaught
. setCatchErrors
is the way to tell the application instance to catch php 7 errors and cannot be removed (reasons below).
IMHO, we still have to differentiate exceptions from errors, as the last ones are currently handled in the full stack framework by the handler registered through the Debug component and the DebugHandlersListener
from HttpKernel, whereas classic exceptions are handled by the Console component itself. Thus, relying on catchExceptions
would change the behavior using the full stack, which can be annoying and considered a BC break. The handler used in full stack does extra work on the exception. For instance:
Using full stack:
[Symfony\Component\Debug\Exception\UndefinedMethodException]
Attempted to call an undefined method named "Option" of class "Symfony\Component\Console\Input\ArgvInput".
Did you mean to call e.g. "getOption", "getOptions", "getParameterOption", "hasOption", "hasParameterOption" or "setOption"?
versus the console component standalone, with the catchErrors
flag:
[Error]
Call to undefined method Symfony\Component\Console\Input\ArgvInput::Option()
(stack traces are the same)
Thus the second catchErrors
flag, otherwise you'll get the last sample using the full stack when an error occurred.
Also see #19813 (comment)
This PR was squashed before being merged into the 2.7 branch (closes #20813). Discussion ---------- [Console] Review Application docblocks | Q | A | ------------- | --- | Branch? | 2.7 | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | N/A | License | MIT | Doc PR | N/A ~I know there must be a lot of other places in the core where there is some repeated or useless informations in docblocks, but everytime I dig into the `Application` class, I see this, and I don't want to repeat things for consistency when adding new methods 😅 (for instance in #20808, the `setCatchThrowables / areThrowablesCaught ` methods do not need a docblock description IMHO).~ ~This PR adapts docblocks where:~ - ~A docblock description is not required, as everything can be expressed in the `@return / @param` argument (the case mentioned above)~ - ~Information is redundant between description and tags, and the context does not have to be reminded again:~ ```diff /** * Adds an array of command objects. * * If a Command is not enabled it will not be added. * - * @param Command[] $commands An array of commands + * @param Command[] $commands */ public function addCommands(array $commands) { foreach ($commands as $command) { $this->add($command); } } ``` Commits ------- d8c18cc [Console] Review Application docblocks
I've just realized the debug component was already required by the console component, thus I don't need the internal I've also removed the notice in |
Rethinking about this, I think the |
Still doesn't look sensible to me to loose informations brung by the If I apply the following patch: PATCH 1 (handle errors/exceptions the same way)diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php
index fd12aa0..6175332 100644
--- a/src/Symfony/Component/Console/Application.php
+++ b/src/Symfony/Component/Console/Application.php
@@ -36,6 +36,7 @@ use Symfony\Component\Console\Event\ConsoleExceptionEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\Console\Exception\CommandNotFoundException;
use Symfony\Component\Console\Exception\LogicException;
+use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\Exception\FatalThrowableError;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@@ -62,7 +63,6 @@ class Application
private $name;
private $version;
private $catchExceptions = true;
- private $catchErrors = false;
private $autoExit = true;
private $definition;
private $helperSet;
@@ -124,9 +124,10 @@ class Application
throw $e;
}
} catch (\Error $e) {
- if (!$this->catchErrors) {
+ if (!$this->catchExceptions) {
throw $e;
}
+
$e = new FatalThrowableError($e);
}
@@ -278,22 +279,6 @@ class Application
}
/**
- * @return bool Whether errors are caught or not during commands execution
- */
- public function areErrorsCaught()
- {
- return $this->catchErrors;
- }
-
- /**
- * @param bool $boolean Whether to catch errors or not during commands execution
- */
- public function setCatchErrors($boolean)
- {
- $this->catchErrors = (bool) $boolean;
- }
-
- /**
* Gets whether to automatically exit after a command execution or not.
*
* @return bool Whether to automatically exit after a command execution or not the output changes from: to If I try to detect the Symfony PATCH 2 (detect error handler)diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php
index fd12aa0..ddc81f1 100644
--- a/src/Symfony/Component/Console/Application.php
+++ b/src/Symfony/Component/Console/Application.php
@@ -36,6 +36,7 @@ use Symfony\Component\Console\Event\ConsoleExceptionEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\Console\Exception\CommandNotFoundException;
use Symfony\Component\Console\Exception\LogicException;
+use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\Exception\FatalThrowableError;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@@ -62,7 +63,6 @@ class Application
private $name;
private $version;
private $catchExceptions = true;
- private $catchErrors = false;
private $autoExit = true;
private $definition;
private $helperSet;
@@ -124,9 +124,18 @@ class Application
throw $e;
}
} catch (\Error $e) {
- if (!$this->catchErrors) {
+ if (!$this->catchExceptions) {
+ throw $e;
+ }
+
+ $handler = set_exception_handler('var_dump');
+ $handler = is_array($handler) ? $handler[0] : null;
+ restore_exception_handler();
+ // Symfony Debug error handler was registered, let it handle the error:
+ if ($handler instanceof ErrorHandler) {
throw $e;
}
+
$e = new FatalThrowableError($e);
}
@@ -278,22 +287,6 @@ class Application
}
/**
- * @return bool Whether errors are caught or not during commands execution
- */
- public function areErrorsCaught()
- {
- return $this->catchErrors;
- }
-
- /**
- * @param bool $boolean Whether to catch errors or not during commands execution
- */
- public function setCatchErrors($boolean)
- {
- $this->catchErrors = (bool) $boolean;
- }
-
- /**
* Gets whether to automatically exit after a command execution or not.
*
* @return bool Whether to automatically exit after a command execution or not
problem is solved using the fullstack. But that's wrong, as it only tells the function ($e) use ($app, $output) {
$app->renderException($e, $output);
}; So any standalone console app using the Debug component would get the raw output instead of being nicely handled by the console component. Maybe we could try looking if the Also we should remember that despite the issue I'm trying to describe is about Symfony's So, yes, I can revert changes to handle errors and exceptions exactly the same way, without this flag (PATCH 1). I just want to be sure everyone is aware of this. To me it's a stumbling block. 😕 At least, the new flag is opt-in and won't cause more troubles than adding 1 line to enable errors catching in a standalone console app. However, if anyone has another clean and BC solution regarding this, I'll be glad to give it a try. :) |
Is it still relevant? We fixed quite a few bugs related to that recently. |
I need to check, but last time I did, it was still needed to me as trying to explain in my last comment. 😕 I didn't follow much changes lately. Which commits should have enhanced the situation regarding this? |
@@ -124,7 +123,14 @@ public function run(InputInterface $input = null, OutputInterface $output = null | |||
if (!$this->catchExceptions) { | |||
throw $e; | |||
} | |||
} catch (\Error $e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn’t this be \Throwable
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using Throwable
would make the catchErrors
flag catch both exceptions and errors, where there already is a dedicated flag for exceptions.
So, if we keep both flags (and I still think we need it currently, as I tried to explain in my previous comments, unless we find a better alternative :/ ), it should probably remains \Error
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed!
Since #18140 was merged ( ping @wouterj ), this one does not make sense anymore as is (or should revert some changes of #18140). But as explained, catching errors indistinctly from exceptions could be considered as a BC break, as error handlers are not used anymore. Using the same So you can close this as soon as the BC break is acknowledged but considered acceptable to you. But this is also a DX regression in the case of the FrameworkBundle, which used to register a handler & enhance the messages, but doesn't anymore now. :/ |
Could you confirm #22261 fixes the BC break? I'm not able to add a relevant test for it |
I think we can close this one, I'm going to revisit the issues spotted here as introduced by #18140 separately. |
…as-grekas) This PR was merged into the 3.3-dev branch. Discussion ---------- [Console] Review console.ERROR related behavior | Q | A | ------------- | --- | Branch? | 3.3 | Bug fix? | yes | New feature? | yes | BC breaks? | no | Deprecations? | yes | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - This PR is a follow up of #18140 that I wanted to do since a few weeks. It enhances this work with fixes and behavior changes. It embeds #22435 and resolves issues like the one described in #20808. - makes ConsoleErrorEvent *not* extend the deprecated ConsoleExceptionEvent - replace ConsoleErrorEvent::markErrorAsHandled by ConsoleErrorEvent::setExitCode - triggers the deprecation in a more appropriate moment - renames ExceptionListener to ErrorListener - tweaks the behavior in relation to #22435 Commits ------- a7c67c9 [Console] Review console.ERROR related behavior
This allows to handle nicely any
\Throwable
when using the console component standalone with PHP 7.With the full-stack framework, a throwable is caught and handled by the Debug component
ErrorHandler
and the exception handler registered bySymfony\Component\HttpKernel\EventListener\DebugHandlersListener
. (see #20797)