Problem/Motivation
In #2521852: Make it possible to use your own exception handler we added a test to ensure users could override _drupal_error_handler()
with a custom error handler. Back then, we were on PHP 5, and a common way to trigger an error was instantiating a service with an erroneous definition, such as missing a required dependency, so this test was put in UncaughtExceptionTest::testMissingDependencyCustomErrorHandler()
.
With PHP 7, PHP started throwing those kinds of errors as \Error objects, allowing them to be caught instead of reaching the error handler. #2599446: UncaughtExceptionTest fails on PHP7 updated the test accordingly, though in so doing, we stopped testing the actual intent of that test, which was to test a custom error handler.
In #3284422: [META] Symfony 6.2 compatibility we updated to Symfony 6.2, but had to change our http_kernel.basic
service to not catch all throwables, in order to preserve Symfony 6.1's behavior of not catching them, because of the testMissingDependencyCustomErrorHandler()
test, because that test relies on middleware, rather than http_kernel.basic
, to catch the error.
Steps to reproduce
Proposed resolution
- Change the argument for
http_kernel.basic
to true, allowing it to catch all throwables, per Symfony 6.2's default behavior. - Users who want the pre-Symfony-6.2 behavior can alter the
http_kernel.basic
service definition and change the last parameter from true to false if they really want to. We already published a change record about that back in August in anticipation of Symfony 6.2's new default behavior. - Fix the custom error handler test to actually test a custom error handler by testing it with something, such as a PHP warning, that PHP invokes an error handler for instead of throwing to a catch block. We already have a test that takes this approach in
ErrorHandlerTest::testErrorHandler()
for testing the non-custom error handler, so move the custom error handler test to be near that one.
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
Comment | File | Size | Author |
---|---|---|---|
#24 | 3319170-24.patch | 4.9 KB | effulgentsia |
| |||
#17 | 3319170-17.patch | 2.51 KB | effulgentsia |
#10 | 3319170-9-sf6.2.patch | 59.12 KB | longwave |
#10 | 3319170-9.patch | 1.88 KB | longwave |
#2 | 3319170-2.patch | 1.4 KB | longwave |
|
Comments
Comment #2
longwaveThis removes the test coverage so the parent issue does not have to change the
$handleAllThrowables
argument to the HTTP kernel service.Comment #3
longwaveActually the existing change record at https://www.drupal.org/node/3305024 is probably enough.
Comment #4
AaronMcHalePresume this has an impact on #3051459: Replace error and exception handlers with implementation from Symfony ErrorHandler component?
Comment #5
longwaveHopefully it brings us closer to that, given we are now using HttpKernel's native error handling? Wouldn't any implementation there be done as some kind of listener inside the HttpKernel, so it could return an appropriate HTTP response? This issue is about removing support for deferring error handling to *outside* of the Symfony framework.
Comment #6
andypostAs SF said this parameter added only to prepare to 7.0 so looks it must have
Comment #7
effulgentsia CreditAttribution: effulgentsia at Acquia commentedWithout overriding
http_kernel.basic
, is there a proper place for someone to do this that's not index.php? And not just this example, but the one in the issue summary of #2521852: Make it possible to use your own exception handler of inserting https://booboo.thephpleague.com/ or similar. If the concept of a custom error handler is still a reasonable concept, and it's only that its location needs to be moved from index.php to elsewhere, then could we change this test to that rather than removing it entirely?Comment #8
effulgentsia CreditAttribution: effulgentsia at Acquia commentedTo clarify #7, I'm not necessarily advocating for preserving the ability to use
set_error_handler()
, but rather to accomplish the goal that someone might have for inserting error handling logic. For example, in the case of https://github.com/thephpleague/booboo, maybe what would be needed is instead of index.php calling$booboo->register()
which relies onset_error_handler()
, the new recommended practice might be to add an event subscriber or middleware that calls$booboo->errorHandler()
directly? I just want to make sure we have some clarity on what that new recommended practice should be, and ideally include that in a test and in the change record.Comment #9
effulgentsia CreditAttribution: effulgentsia at Acquia commentedOr maybe it would make more sense to use https://github.com/symfony/error-handler as the reference example.
Comment #10
longwaveThe correct way of handling errors inside Symfony is to listen for the
KernelEvents::EXCEPTION
event. Or if you want to handle them outside of Symfony, you can remove core'sexception.final
service and use set_error_handler(), which we can still do here to make this test pass.The attached patches should prove that this latter approach works before and after Symfony 6.2.
Comment #13
effulgentsia CreditAttribution: effulgentsia at Acquia commentedMakes sense. In that case, it seems like the difference that Symfony 6.2 introduces is that HttpKernel will now dispatch this event for all throwables, not just exceptions. So I think that the CR for this issue, whether that's https://www.drupal.org/node/3305024 or a new one, should show a before/after where before you used set_error_handler() in index.php, and the after is where you can now add a KernelEvents::EXCEPTION subscriber that works for errors that are not exceptions.
From a BC standpoint, if you wanted to keep an existing set_error_handler() in index.php working the way it did in Drupal 9.5, what you'd probably want to do is not remove the
exception.final
service, but rather override it with a subclass that only calls the parent class if$event->getThrowable()
is an instance of\Exception
. Would it make sense to mention that in https://www.drupal.org/node/3305024 as something that we would recommend trying before deciding to override thehttp_kernel.basic
service's arguments?Comment #14
effulgentsia CreditAttribution: effulgentsia at Acquia commented#13 is about change records. For the purposes of what this issue does with respect to test coverage, +1 to #10: I think it makes a lot of sense for the test coverage of a custom error handler to remove the
exception.final
service. The fact that #10 failed in another test is a bit concerning: that would seem to point to either changes to \Drupal::state() or to the event dispatcher persisting across tests.Comment #15
effulgentsia CreditAttribution: effulgentsia at Acquia commentedAlternatively, instead of having the test remove the
exception.final
service, we could make the test symmetrical withtestUncaughtExceptionCustomExceptionHandler()
by triggering the missing dependency error from middleware (MonkeysInTheControlRoom
) rather than in the controller (LonelyMonkeyController
). I think this might actually be the better way to test it, since then the use case ofset_error_handler()
is clearer: it's not necessarily that it's undesirable to handle errors inKernelEvents::EXCEPTION
, it's that there are code paths, like middleware, for whichKernelEvents::EXCEPTION
isn't dispatched.Comment #16
longwaveThe parent issue was committed without deciding the resolution for this, marking this as an RC blocker now.
Comment #17
effulgentsia CreditAttribution: effulgentsia at Acquia commentedThis implements #15, but it's failing locally for me, and I don't know yet if that's a quirk of my machine, a quirk of the test, or if there's something about the flow of error handling that I'm misunderstanding.
Comment #18
andypostpatch fails to apply, probably because HTTPS endpoint in Gitlab is out of sync with SSH one
Comment #19
hestenetWe've made a chance to redirect https Git traffic to our primary while we sort out the issue with our git secondary lagging... requeued the patch tests in #17.
Comment #20
hestenetNo luck on requeues... we'll continue to work on this.
Comment #21
effulgentsia CreditAttribution: effulgentsia at Acquia commentedThe reason #17 fails for me locally is that even though
\Drupal::service('broken_class_with_missing_dependency')
throws anArgumentCountError
which is not anException
,_drupal_exception_handler()
still runs, which preempts the error handler from running. But I'm not clear on why that's different from earlier Symfony versions. In other words, how doestestMissingDependencyCustomErrorHandler()
currently pass tests in HEAD without running into that?Comment #22
hestenetPatches are now able to get past the 'fail to apply' due to GitLab replication mismatch stage - so I will bow out from here :)
Comment #24
effulgentsia CreditAttribution: effulgentsia at Acquia commentedAh, it's because the custom error handler isn't invoked in that test in HEAD either. Instead, #2599446: UncaughtExceptionTest fails on PHP7 changed
MonkeysInTheControlRoom
to catch the\TypeError
and just do the same thing with it as what the test's custom error handler does.In that case, my suggestion is to remove
testMissingDependencyCustomErrorHandler()
, since a missing dependency is a thrown/caught error that doesn't involve an error handler, and to instead to add a test toErrorHandlerTest
that tests a custom error handler for something that isn't thrown/caught, such as a PHP warning. This patch does that.Comment #25
effulgentsia CreditAttribution: effulgentsia at Acquia commentedUpdated the issue title and summary accordingly.
Comment #26
effulgentsia CreditAttribution: effulgentsia at Acquia commentedComment #27
effulgentsia CreditAttribution: effulgentsia at Acquia commentedComment #28
longwave+1 - thanks for working through this, this solution works for me. The error trapping is very edge case stuff but if we can prove it still works as expected then that is good.
Comment #30
catchCommitted/pushed to 10.1.x and cherry-picked to 10.0.x, thanks!
Comment #31
effulgentsia CreditAttribution: effulgentsia at Acquia commentedThank you!