Problem/Motivation
The fallback exception handler in php7 can receive EngineExceptions which(by design) do not extend Exceptions. This means our exception handler actually fatal's handing these exceptions leading to loosing the original error.
Proposed resolution
- Remove Exception type hint in Error class.
- Catch BaseExceptions in our shutdown function and error handler fallback.
- Let ErrorHandlerTest test for a TypeException (which is an EngineException/BaseException) instead of a recoverable fatal error.
Remaining tasks
Review patch
User interface changes
N/A
API changes
N/A
Original report by @neclimdul
Need to fill this out a bit but short of it is the fallback exception handler in php7 can receive EngineExceptions which(by design) do not extend Exceptions. This means our exception handler actually fatal's handing these exceptions leading to loosing the original error.
Opened an internals discussion around this to see if this is a php bug or if we need to handle it.
http://news.php.net/php.internals/85613
http://www.serverphorums.com/read.php?7,1172307
Left as a task until we know if this is a bug.
| Comment | File | Size | Author |
|---|---|---|---|
| #33 | 2463285-33.patch | 8.3 KB | stefan.r |
| #33 | interdiff-29-33.txt | 1.68 KB | stefan.r |
| #30 | interdiff-19-29.txt | 3.58 KB | stefan.r |
| #30 | 2463285-29.patch | 8.31 KB | stefan.r |
| #20 | 2463285-19.patch | 8.21 KB | stefan.r |
Comments
Comment #1
neclimdulComment #2
stefan.r commentedFrom the RFC:
The secondary vote passed, so maybe we can use BaseException for the PHP7 type hint and \Exception for PHP<7? This would require two different error handler definitions depending on PHP version...
Comment #3
stefan.r commentedJust so we don't have to copypaste full classes, we could do something like this...
Comment #4
stefan.r commentedComment #6
stefan.r commentedSeems like on the mailing list they had the same idea:
http://www.serverphorums.com/read.php?7,1170469,1172542
See also https://github.com/php/php-src/pull/1095
Comment #7
neclimdulThe response I got:
http://news.php.net/php.internals/85619
He does suggest using version compare but even splitting out a lot of code into the base, man that makes a ugly difficult to maintain class. Is there a reason to not just remove the type hint and document why it s not there?
Comment #8
stefan.r commentedRemoving the type hint obviously would solve the issue as well :)
If we do want to keep it, I could write a test that parses the source code of both the PHP 5 class and the PHP 7 class and assures they're identical line by line (other than the type hint)?
Comment #9
stefan.r commentedI discussed with @dawehner and @Berdir on IRC and they lean toward dropping the type hint.
Upon doing a search for occurences of "Exception" type hints, I found the following:
Then there are also the catch statements in _drupal_shutdown_function and _drupal_exception_handler...
Comment #10
neclimdulThe try/catch should probably be outside of the scope of at least this issue. The new exception types specifically do not extend \Exception so they are not caught like this because they should be handled differently.
Comment #11
stefan.r commentedThis removes type hints from the Error class, and catches any BaseExceptions that may occur in the shutdown function, or during handling or the first one.
Comment #12
stefan.r commentedOops, missed your comment. How do they require different handling specifically (in this case)?
Comment #13
berdirAs written in #2454439: [META] Support PHP 7, there fewer errors/warnings in the error handling tests, but they still fail. I'm not sure if we want to fix that here or in a separate issue, but being able to pass the two relevant tests on php7 (ErrorHandlerTest and SimpleTestErrorCollectorTest) might be a good indicator to have things working? What I'm a bit worried about is those might require php5/7 specific code/assertions.
Comment #14
stefan.r commented@Berdir what are the specific fails, does this patch fix any of them?
Comment #15
neclimdulMegh, I guess that's OK actually. It raises a couple questions in my mind.
1) Should it be second since its less likely?
2) Should we make a class for 5.X so BaseException actually exists even if it won't be used?
Comment #16
stefan.r commented@neclimdul just to address your two questions:
Well EngineExceptions happening during exception handling may be purely theoretical, but catching a BaseException is still more likely regardless, as Exceptions are BaseExceptions as well :)
I did consider this but in this specific case I don't see the added value? Apparently PHP is fine with trying to catch an exception of a nonexistent class, and I don't think we'll use BaseExceptions elsewhere... Anything that's not an actual Exception will be what used to be a parse error/fatal error, and those will be dealt with by the error handler.
Comment #17
stefan.r commentedOn a side note, if this patch gets OK'd we'll have to fix some spelling:
Comment #18
stefan.r commentedThis fixes the error in ErrorHandlerTest:
Comment #19
berdirNice, did you test the error collector test too?
Comment #20
stefan.r commentedComment #21
stefan.r commented@Berdir the error collector test didn't give me any errors, but I don't believe it gave me any errors on my baseline D8 either in PHP7. May be because I have a different setup.
Comment #22
stefan.r commentedIf it's not a PHP bug, I guess it's a Drupal bug as we're supposed to support PHP7?
Comment #23
stefan.r commentedComment #24
berdirYou can't type hint it, but for the @param, you can actually explicitly document it's \Exception|\BaseException
same here.
@see functions need () at the end.
Here too, maybe the docblock below can be simplified then?
Comment #27
berdirYeah, still failing for me. In a *very* funny way :)
Latest php7-master (I had to recompile from scratch, got messed up otherwise)
Comment #28
berdirSorry, forget about that, it's working, I messed up.
Comment #29
fabianx commentedThat is no longer line 66, but line 70 I believe :).
That should make that green again.
--
Besides that: Looks great!
Comment #30
stefan.r commentedComment #31
stefan.r commentedActually this patch will fail again, it is still line 66 I think as we don't change ErrorTestController
Comment #33
stefan.r commentedComment #34
fabianx commentedRTBC, looks great now!
Comment #35
alexpottIt would be nice to get an answer on http://news.php.net/php.internals/85613 before proceeding with this.
Comment #36
stefan.r commentedTheir interface doesnt show replies somehow, see http://www.serverphorums.com/read.php?7,1172307 for that
Comment #37
stefan.r commentedComment #38
alexpottMy other concern with this issue is that currently our exception handler is totally avoided if the front controller is used - see index.php. I'm not sure what we should about this.Adding yet another catch there is awful.
Personally I think we should remove the exception catch from index.php and put this information in the output of
_drupal_exception_handler()but this is hard because_drupal_log_error()has too many dependencies.Comment #39
berdirRight, but is that really related to this issue? We're not changing any of that here, just making the same code work on PHP 7 as well.
Agreed that we should either drop the global exception handler or the stuff in index.php and not keep both. Exceptions that happen in controllers are handled separtely anyway.
There are also issues to try and remove our custom exception handling with whoops or a similar library.
Comment #40
fabianx commentedI think we should go ahead with the patch here. Given they (PHP) need the BC break, dropping the typehinting is the most sensible thing we can do - unless we want to do conditional class loading based on PHP version ...
--
index.php only catches HttpExceptionInterface type exceptions.
RuntimeExceptions hence are never caught by that (they are converted from normal Exceptions inside the application however).
BUT: What is now BaseException has before been a fatal exit(1) WSOD error, so nothing a front-controller needed to handle anyway.
Therefore back to RTBC.
Comment #41
stefan.r commented@Fabianx I do see a second catch in index.php for regular Exceptions...
Maybe we should still do a followup issue for removing those from index.php somehow?
Comment #42
fabianx commentedYes, but that Exception is re-thrown.
So while for consistency it is not good to not have \BaseException caught in the front-controller, it is also not a problem, because those have been WSOD in previous PHP versions anyway, so never reached that point and are now handled by the error handler.
So I think, yes lets follow-up on that to remove that and instead install our error handler way earlier (which is what this tries to do).
Similar to how the assertion patch works ...
Comment #43
alexpottThis issue addresses a major bug and is allowed per https://www.drupal.org/core/beta-changes. Committed 700499c and pushed to 8.0.x. Thanks!
Comment #46
Nux commentedSorry for changing the status, but you might interested in the ongoing RFC that removes `BaseException` and adds base class named `Error`. I'm not sure but probably also using `Drupal\Core\Utility\Error` would be hard if it won't be renamed.
RFC voting period ends on June 24th, 2015, but from current votes I assume it will pass.
Comment #47
stefan.r commentedThanks, created #2505315: Catch PHP7 Throwable objects instead of BaseExceptions in the error handler as a follow up