-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Fix error handling during opcache compilation #18541
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
base: master
Are you sure you want to change the base?
Conversation
…ile()` The logic to disable userland error handlers existed since the first commit including OPcache in php-src. The reason unfortunately is not explained. No existing tests require that userland error handlers do not trigger in `opcache_compile_file()`. The newly added tests are intended to exercise all possible kinds of edge-cases that might arise and cause issues (e.g. throwing Exceptions, performing a bailout, or loading a different file from within the error handler). They all pass for both `--repeat 1` and `--repeat 2`, as well as with OPcache enabled and without OPcache enabled (in the latter case with the exception of the `opcache_get_status()` verification). The list of cached files at the end of execution also matches my expectations. Therefore it seems that disabling the userland error handler when compiling a file with OPcache is not (or at least no longer) necessary. Fixes php#17422.
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.
Some first remarks regarding the tests. Did not yet work through the C code.
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 we also have a test that tries to catch the exception or are the new tests in ext/opcache/tests/gh17422/
sufficient?
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.
This one fails when OPcache is not loaded. It still says the “During inheritance of C” bit. Thus:
We could apply the same behavior when opcache is disabled.
This would make sense to me.
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 changes all make sense to me as far as I'm qualified to tell 😄
A thought before reviewing this in detail: Should this behavior be unified for opcache / no opcache? Consider this example: // a.php
set_error_handler(function () {
require __DIR__ . '/c.php';
Foo::test();
});
require __DIR__ . '/b.php';
// b.php
class Foo {
public static function test() {
switch (true) {
default:
continue;
}
echo "b.php\n";
}
}
// c.php
class Foo {
public static function test() {
echo "c.php\n";
}
} This behavior will diverge currently, printing b.php with opcache, but |
@iluuu1994 From the initial PR description. And I agree that unifying the behavior makes sense. The issue only existed in the first place, because the behavior was not consistent between OPcache and non-OPcache. To me any observable behavioral differences introduced by OPcache are a bug (unless they are the main purpose of OPcache, e.g. autoloading seeing outdates files when revalidation is disabled). |
Ah thank you, I missed this last sentence. 🙂 And Arnaud is fast at implementing than me commenting. 😄
I think diverging behavior is ok if the engine benefits from it. As an example, cache slot merging is observable, but it's a worthwhile trade-off. |
Possible fix for GH-17422
Based on #17627
Current behavior
Currently, Opcache disables the user-defined error handler during compilation. When
opcache.record_errors
is enabled, errors are replayed when loading the script from cache later, this time without disabling the user-defined error handler.Changes
This PR makes the following changes:
Errors emitted during compilation are delayed and handled after compilation, when it's safe to execute user-defined error handlers. As before, when
opcache.record_errors
is enabled, errors are also replayed when loading the script from cache and user-defined error handler are called, if any.This is done by changing the
EG(record_errors)
mechanism so that errors are not only recorded, but also delayed.Class linking:
Since class linking uses the same mechanism, errors emitted during class linking are also delayed (with Opcache), and replayed after linking. Exceptions thrown by user-defined error handlers are not promoted to fatal errors anymore (when opcache is enabled).
Fatal errors:
Fatal errors are not safe to delay, so they are always processed immediately. Any delayed errors are also processed, but unfortunately it's not safe to execute user-defined error handlers at this point, so these errors still bypass them.
Notes
This targets master because of the behavior changes.
We could apply the same behavior when opcache is disabled. (Edit: done)
UPGRADING draft: