mirror of
https://github.com/php/php-src.git
synced 2025-08-15 13:38:49 +02:00
Fix error handling inconsistency with opcache
When opcache is enabled, error handling is altered in the following ways: * Errors emitted during compilation bypass the user-defined error handler * Exceptions emitted during class linking are turned into fatal errors Changes here make the behavior consistent regardless of opcache being enabled or not: * Errors emitted during compilation and class linking are always delayed and handled after compilation or class linking. During handling, user-defined error handlers are not bypassed. Fatal errors emitted during compilation or class linking cause any delayed errors to be handled immediately (without calling user-defined error handlers, as it would be unsafe). * Exceptions thrown by user-defined error handlers when handling class linking error are not promoted to fatal errors anymore and do not prevent linking. Fixes GH-17422. Closes GH-18541. Closes GH-17627. Co-authored-by: Tim Düsterhus <tim@bastelstu.be>
This commit is contained in:
parent
9b777b3c35
commit
7b3e68ff69
36 changed files with 408 additions and 61 deletions
1
.github/scripts/windows/test_task.bat
vendored
1
.github/scripts/windows/test_task.bat
vendored
|
@ -128,6 +128,7 @@ mkdir %PHP_BUILD_DIR%\test_file_cache
|
|||
rem generate php.ini
|
||||
echo extension_dir=%PHP_BUILD_DIR% > %PHP_BUILD_DIR%\php.ini
|
||||
echo opcache.file_cache=%PHP_BUILD_DIR%\test_file_cache >> %PHP_BUILD_DIR%\php.ini
|
||||
echo opcache.record_warnings=1 >> %PHP_BUILD_DIR%\php.ini
|
||||
rem work-around for some spawned PHP processes requiring OpenSSL and sockets
|
||||
echo extension=php_openssl.dll >> %PHP_BUILD_DIR%\php.ini
|
||||
echo extension=php_sockets.dll >> %PHP_BUILD_DIR%\php.ini
|
||||
|
|
5
NEWS
5
NEWS
|
@ -15,8 +15,11 @@ PHP NEWS
|
|||
- OPcache:
|
||||
. Disallow changing opcache.memory_consumption when SHM is already set up.
|
||||
(timwolla)
|
||||
. Fixed GH-15074 (Compiling opcache statically into ZTS PHP fails). (Arnaud)
|
||||
. Fixed bug GH-15074 (Compiling opcache statically into ZTS PHP fails).
|
||||
(Arnaud)
|
||||
. Make OPcache non-optional (Arnaud, timwolla)
|
||||
. Fixed bug GH-17422 (OPcache bypasses the user-defined error handler for
|
||||
deprecations). (Arnaud, timwolla)
|
||||
|
||||
- OpenSSL:
|
||||
. Add $digest_algo parameter to openssl_public_encrypt() and
|
||||
|
|
|
@ -44,6 +44,12 @@ PHP 8.5 UPGRADE NOTES
|
|||
. Traits are now bound before the parent class. This is a subtle behavioral
|
||||
change, but should more closely match user expectations, demonstrated by
|
||||
GH-15753 and GH-16198.
|
||||
. Errors emitted during compilation and class linking are now always delayed
|
||||
and handled after compilation or class linking. Fatal errors emitted during
|
||||
compilation or class linking cause any delayed errors to be handled
|
||||
immediately, without calling user-defined error handlers.
|
||||
. Exceptions thrown by user-defined error handlers when handling class linking
|
||||
errors are not promoted to fatal errors anymore and do not prevent linking.
|
||||
|
||||
- DOM:
|
||||
. Cloning a DOMNamedNodeMap, DOMNodeList, Dom\NamedNodeMap, Dom\NodeList,
|
||||
|
|
|
@ -71,6 +71,8 @@ PHP 8.5 INTERNALS UPGRADE NOTES
|
|||
* zend_register_string_constant()
|
||||
* zend_register_stringl_constant()
|
||||
. EG(fake_scope) now is a _const_ zend_class_entry*.
|
||||
. zend_begin_record_errors() or EG(record_errors)=true cause errors to be
|
||||
delayed. Before, errors would be recorded but not delayed.
|
||||
|
||||
========================
|
||||
2. Build system changes
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
--TEST--
|
||||
Deprecation promoted to exception should result in fatal error during inheritance
|
||||
Deprecation promoted to exception during inheritance
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (getenv('SKIP_PRELOAD')) die('skip Error handler not active during preloading');
|
||||
|
@ -17,7 +17,8 @@ $class = new class extends DateTime {
|
|||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: During inheritance of DateTime: Uncaught Exception: Return type of DateTime@anonymous::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in %s:%d
|
||||
Fatal error: Uncaught Exception: Return type of DateTime@anonymous::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): {closure:%s:%d}(8192, 'Return type of ...', '%s', 8)
|
||||
#1 {main} in %s on line %d
|
||||
#1 {main}
|
||||
thrown in %s on line %d
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
--TEST--
|
||||
Deprecation promoted to exception during inheritance
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (getenv('SKIP_PRELOAD')) die('skip Error handler not active during preloading');
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
set_error_handler(function($code, $message) {
|
||||
throw new Exception($message);
|
||||
});
|
||||
|
||||
try {
|
||||
class C extends DateTime {
|
||||
public function getTimezone() {}
|
||||
public function getTimestamp() {}
|
||||
};
|
||||
} catch (Exception $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
var_dump(new C());
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Exception: Return type of C::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
|
||||
object(C)#%d (3) {
|
||||
["date"]=>
|
||||
string(%d) "%s"
|
||||
["timezone_type"]=>
|
||||
int(3)
|
||||
["timezone"]=>
|
||||
string(3) "UTC"
|
||||
}
|
|
@ -14,5 +14,8 @@ class C implements Serializable {
|
|||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: During inheritance of C, while implementing Serializable: Uncaught Exception: C implements the Serializable interface, which is deprecated. Implement __serialize() and __unserialize() instead (or in addition, if support for old PHP versions is necessary) in %s:%d
|
||||
%a
|
||||
Fatal error: Uncaught Exception: C implements the Serializable interface, which is deprecated. Implement __serialize() and __unserialize() instead (or in addition, if support for old PHP versions is necessary) in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): {closure:%s:%d}(8192, 'C implements th...', '%s', 7)
|
||||
#1 {main}
|
||||
thrown in %s on line %d
|
||||
|
|
41
Zend/zend.c
41
Zend/zend.c
|
@ -1452,6 +1452,29 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
|
|||
return;
|
||||
}
|
||||
|
||||
/* Emit any delayed error before handling fatal error */
|
||||
if ((type & E_FATAL_ERRORS) && !(type & E_DONT_BAIL) && EG(num_errors)) {
|
||||
uint32_t num_errors = EG(num_errors);
|
||||
zend_error_info **errors = EG(errors);
|
||||
EG(num_errors) = 0;
|
||||
EG(errors) = NULL;
|
||||
|
||||
bool orig_record_errors = EG(record_errors);
|
||||
EG(record_errors) = false;
|
||||
|
||||
/* Disable user error handler before emitting delayed errors, as
|
||||
* it's unsafe to execute user code after a fatal error. */
|
||||
int orig_user_error_handler_error_reporting = EG(user_error_handler_error_reporting);
|
||||
EG(user_error_handler_error_reporting) = 0;
|
||||
|
||||
zend_emit_recorded_errors_ex(num_errors, errors);
|
||||
|
||||
EG(user_error_handler_error_reporting) = orig_user_error_handler_error_reporting;
|
||||
EG(record_errors) = orig_record_errors;
|
||||
EG(num_errors) = num_errors;
|
||||
EG(errors) = errors;
|
||||
}
|
||||
|
||||
if (EG(record_errors)) {
|
||||
zend_error_info *info = emalloc(sizeof(zend_error_info));
|
||||
info->type = type;
|
||||
|
@ -1464,6 +1487,11 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
|
|||
EG(num_errors)++;
|
||||
EG(errors) = erealloc(EG(errors), sizeof(zend_error_info*) * EG(num_errors));
|
||||
EG(errors)[EG(num_errors)-1] = info;
|
||||
|
||||
/* Do not process non-fatal recorded error */
|
||||
if (!(type & E_FATAL_ERRORS) || (type & E_DONT_BAIL)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Always clear the last backtrace.
|
||||
|
@ -1752,13 +1780,18 @@ ZEND_API void zend_begin_record_errors(void)
|
|||
EG(errors) = NULL;
|
||||
}
|
||||
|
||||
ZEND_API void zend_emit_recorded_errors_ex(uint32_t num_errors, zend_error_info **errors)
|
||||
{
|
||||
for (uint32_t i = 0; i < num_errors; i++) {
|
||||
zend_error_info *error = errors[i];
|
||||
zend_error_zstr_at(error->type, error->filename, error->lineno, error->message);
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_API void zend_emit_recorded_errors(void)
|
||||
{
|
||||
EG(record_errors) = false;
|
||||
for (uint32_t i = 0; i < EG(num_errors); i++) {
|
||||
zend_error_info *error = EG(errors)[i];
|
||||
zend_error_zstr_at(error->type, error->filename, error->lineno, error->message);
|
||||
}
|
||||
zend_emit_recorded_errors_ex(EG(num_errors), EG(errors));
|
||||
}
|
||||
|
||||
ZEND_API void zend_free_recorded_errors(void)
|
||||
|
|
|
@ -444,6 +444,7 @@ ZEND_API void zend_replace_error_handling(zend_error_handling_t error_handling,
|
|||
ZEND_API void zend_restore_error_handling(zend_error_handling *saved);
|
||||
ZEND_API void zend_begin_record_errors(void);
|
||||
ZEND_API void zend_emit_recorded_errors(void);
|
||||
ZEND_API void zend_emit_recorded_errors_ex(uint32_t num_errors, zend_error_info **errors);
|
||||
ZEND_API void zend_free_recorded_errors(void);
|
||||
END_EXTERN_C()
|
||||
|
||||
|
|
|
@ -1327,7 +1327,6 @@ ZEND_API zend_class_entry *zend_bind_class_in_slot(
|
|||
|
||||
ce = zend_do_link_class(ce, lc_parent_name, Z_STR_P(lcname));
|
||||
if (ce) {
|
||||
ZEND_ASSERT(!EG(exception));
|
||||
zend_observer_class_linked_notify(ce, Z_STR_P(lcname));
|
||||
return ce;
|
||||
}
|
||||
|
|
|
@ -295,7 +295,8 @@ struct _zend_executor_globals {
|
|||
size_t fiber_stack_size;
|
||||
|
||||
/* If record_errors is enabled, all emitted diagnostics will be recorded,
|
||||
* in addition to being processed as usual. */
|
||||
* and their processing is delayed until zend_emit_recorded_errors()
|
||||
* is called or a fatal diagnostic is emitted. */
|
||||
bool record_errors;
|
||||
uint32_t num_errors;
|
||||
zend_error_info **errors;
|
||||
|
|
|
@ -1085,10 +1085,7 @@ static void ZEND_COLD emit_incompatible_method_error(
|
|||
"Return type of %s should either be compatible with %s, "
|
||||
"or the #[\\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice",
|
||||
ZSTR_VAL(child_prototype), ZSTR_VAL(parent_prototype));
|
||||
if (EG(exception)) {
|
||||
zend_exception_uncaught_error(
|
||||
"During inheritance of %s", ZSTR_VAL(parent_scope->name));
|
||||
}
|
||||
ZEND_ASSERT(!EG(exception));
|
||||
}
|
||||
} else {
|
||||
zend_error_at(E_COMPILE_ERROR, func_filename(child), func_lineno(child),
|
||||
|
@ -3561,8 +3558,6 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
|
|||
}
|
||||
#endif
|
||||
|
||||
bool orig_record_errors = EG(record_errors);
|
||||
|
||||
if (ce->ce_flags & ZEND_ACC_IMMUTABLE && is_cacheable) {
|
||||
if (zend_inheritance_cache_get && zend_inheritance_cache_add) {
|
||||
zend_class_entry *ret = zend_inheritance_cache_get(ce, parent, traits_and_interfaces);
|
||||
|
@ -3574,16 +3569,21 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
|
|||
Z_CE_P(zv) = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Make sure warnings (such as deprecations) thrown during inheritance
|
||||
* will be recorded in the inheritance cache. */
|
||||
zend_begin_record_errors();
|
||||
} else {
|
||||
is_cacheable = 0;
|
||||
}
|
||||
proto = ce;
|
||||
}
|
||||
|
||||
/* Delay and record warnings (such as deprecations) thrown during
|
||||
* inheritance, so they will be recorded in the inheritance cache.
|
||||
* Warnings must be delayed in all cases so that we get a consistent
|
||||
* behavior regardless of cacheability. */
|
||||
bool orig_record_errors = EG(record_errors);
|
||||
if (!orig_record_errors) {
|
||||
zend_begin_record_errors();
|
||||
}
|
||||
|
||||
zend_try {
|
||||
if (ce->ce_flags & ZEND_ACC_IMMUTABLE) {
|
||||
/* Lazy class loading */
|
||||
|
@ -3774,6 +3774,7 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
|
|||
}
|
||||
|
||||
if (!orig_record_errors) {
|
||||
zend_emit_recorded_errors();
|
||||
zend_free_recorded_errors();
|
||||
}
|
||||
if (traits_and_interfaces) {
|
||||
|
@ -3934,10 +3935,12 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_
|
|||
orig_linking_class = CG(current_linking_class);
|
||||
CG(current_linking_class) = is_cacheable ? ce : NULL;
|
||||
|
||||
bool orig_record_errors = EG(record_errors);
|
||||
|
||||
zend_try{
|
||||
CG(zend_lineno) = ce->info.user.line_start;
|
||||
|
||||
if (is_cacheable) {
|
||||
if (!orig_record_errors) {
|
||||
zend_begin_record_errors();
|
||||
}
|
||||
|
||||
|
@ -3959,13 +3962,13 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_
|
|||
|
||||
CG(current_linking_class) = orig_linking_class;
|
||||
} zend_catch {
|
||||
EG(record_errors) = false;
|
||||
zend_free_recorded_errors();
|
||||
if (!orig_record_errors) {
|
||||
EG(record_errors) = false;
|
||||
zend_free_recorded_errors();
|
||||
}
|
||||
zend_bailout();
|
||||
} zend_end_try();
|
||||
|
||||
EG(record_errors) = false;
|
||||
|
||||
if (is_cacheable) {
|
||||
HashTable *ht = (HashTable*)ce->inheritance_cache;
|
||||
zend_class_entry *new_ce;
|
||||
|
@ -3983,6 +3986,11 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_
|
|||
}
|
||||
}
|
||||
|
||||
if (!orig_record_errors) {
|
||||
zend_emit_recorded_errors();
|
||||
zend_free_recorded_errors();
|
||||
}
|
||||
|
||||
if (ZSTR_HAS_CE_CACHE(ce->name)) {
|
||||
ZSTR_SET_CE_CACHE(ce->name, ce);
|
||||
}
|
||||
|
|
|
@ -650,7 +650,17 @@ ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type)
|
|||
}
|
||||
}
|
||||
} else {
|
||||
bool orig_record_errors = EG(record_errors);
|
||||
if (!orig_record_errors) {
|
||||
zend_begin_record_errors();
|
||||
}
|
||||
|
||||
op_array = zend_compile(ZEND_USER_FUNCTION);
|
||||
|
||||
if (!orig_record_errors) {
|
||||
zend_emit_recorded_errors();
|
||||
zend_free_recorded_errors();
|
||||
}
|
||||
}
|
||||
|
||||
zend_restore_lexical_state(&original_lex_state);
|
||||
|
|
|
@ -7938,7 +7938,7 @@ ZEND_VM_HANDLER(145, ZEND_DECLARE_CLASS_DELAYED, CONST, CONST)
|
|||
if (zv) {
|
||||
SAVE_OPLINE();
|
||||
ce = zend_bind_class_in_slot(zv, lcname, Z_STR_P(RT_CONSTANT(opline, opline->op2)));
|
||||
if (!ce) {
|
||||
if (EG(exception)) {
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
|
@ -7962,7 +7962,7 @@ ZEND_VM_HANDLER(146, ZEND_DECLARE_ANON_CLASS, ANY, ANY, CACHE_SLOT)
|
|||
if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
|
||||
SAVE_OPLINE();
|
||||
ce = zend_do_link_class(ce, (OP2_TYPE == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL, rtd_key);
|
||||
if (!ce) {
|
||||
if (EG(exception)) {
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
|
|
4
Zend/zend_vm_execute.h
generated
4
Zend/zend_vm_execute.h
generated
|
@ -3212,7 +3212,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ANON_CLASS_SPEC_HANDLE
|
|||
if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
|
||||
SAVE_OPLINE();
|
||||
ce = zend_do_link_class(ce, (opline->op2_type == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL, rtd_key);
|
||||
if (!ce) {
|
||||
if (EG(exception)) {
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
|
@ -8014,7 +8014,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_CLASS_DELAYED_SPEC_CON
|
|||
if (zv) {
|
||||
SAVE_OPLINE();
|
||||
ce = zend_bind_class_in_slot(zv, lcname, Z_STR_P(RT_CONSTANT(opline, opline->op2)));
|
||||
if (!ce) {
|
||||
if (EG(exception)) {
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1734,19 +1734,11 @@ static void zend_accel_set_auto_globals(int mask)
|
|||
ZCG(auto_globals_mask) |= mask;
|
||||
}
|
||||
|
||||
static void replay_warnings(uint32_t num_warnings, zend_error_info **warnings) {
|
||||
for (uint32_t i = 0; i < num_warnings; i++) {
|
||||
zend_error_info *warning = warnings[i];
|
||||
zend_error_zstr_at(warning->type, warning->filename, warning->lineno, warning->message);
|
||||
}
|
||||
}
|
||||
|
||||
static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handle, int type, zend_op_array **op_array_p)
|
||||
{
|
||||
zend_persistent_script *new_persistent_script;
|
||||
uint32_t orig_functions_count, orig_class_count;
|
||||
zend_op_array *orig_active_op_array;
|
||||
zval orig_user_error_handler;
|
||||
zend_op_array *op_array;
|
||||
bool do_bailout = false;
|
||||
accel_time_t timestamp = 0;
|
||||
|
@ -1814,13 +1806,6 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl
|
|||
orig_active_op_array = CG(active_op_array);
|
||||
orig_functions_count = EG(function_table)->nNumUsed;
|
||||
orig_class_count = EG(class_table)->nNumUsed;
|
||||
ZVAL_COPY_VALUE(&orig_user_error_handler, &EG(user_error_handler));
|
||||
|
||||
/* Override them with ours */
|
||||
ZVAL_UNDEF(&EG(user_error_handler));
|
||||
if (ZCG(accel_directives).record_warnings) {
|
||||
zend_begin_record_errors();
|
||||
}
|
||||
|
||||
zend_try {
|
||||
orig_compiler_options = CG(compiler_options);
|
||||
|
@ -1850,13 +1835,12 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl
|
|||
|
||||
/* Restore originals */
|
||||
CG(active_op_array) = orig_active_op_array;
|
||||
EG(user_error_handler) = orig_user_error_handler;
|
||||
EG(record_errors) = 0;
|
||||
|
||||
if (!op_array) {
|
||||
/* compilation failed */
|
||||
zend_free_recorded_errors();
|
||||
if (do_bailout) {
|
||||
EG(record_errors) = false;
|
||||
zend_free_recorded_errors();
|
||||
zend_bailout();
|
||||
}
|
||||
return NULL;
|
||||
|
@ -1871,10 +1855,6 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl
|
|||
zend_accel_move_user_functions(CG(function_table), CG(function_table)->nNumUsed - orig_functions_count, &new_persistent_script->script);
|
||||
zend_accel_move_user_classes(CG(class_table), CG(class_table)->nNumUsed - orig_class_count, &new_persistent_script->script);
|
||||
zend_accel_build_delayed_early_binding_list(new_persistent_script);
|
||||
new_persistent_script->num_warnings = EG(num_errors);
|
||||
new_persistent_script->warnings = EG(errors);
|
||||
EG(num_errors) = 0;
|
||||
EG(errors) = NULL;
|
||||
|
||||
efree(op_array); /* we have valid persistent_script, so it's safe to free op_array */
|
||||
|
||||
|
@ -1956,7 +1936,7 @@ static zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int
|
|||
}
|
||||
}
|
||||
}
|
||||
replay_warnings(persistent_script->num_warnings, persistent_script->warnings);
|
||||
zend_emit_recorded_errors_ex(persistent_script->num_warnings, persistent_script->warnings);
|
||||
|
||||
if (persistent_script->ping_auto_globals_mask & ~ZCG(auto_globals_mask)) {
|
||||
zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask & ~ZCG(auto_globals_mask));
|
||||
|
@ -1965,11 +1945,22 @@ static zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int
|
|||
return zend_accel_load_script(persistent_script, 1);
|
||||
}
|
||||
|
||||
zend_begin_record_errors();
|
||||
|
||||
persistent_script = opcache_compile_file(file_handle, type, &op_array);
|
||||
|
||||
if (persistent_script) {
|
||||
if (ZCG(accel_directives).record_warnings) {
|
||||
persistent_script->num_warnings = EG(num_errors);
|
||||
persistent_script->warnings = EG(errors);
|
||||
}
|
||||
|
||||
from_memory = false;
|
||||
persistent_script = cache_script_in_file_cache(persistent_script, &from_memory);
|
||||
|
||||
zend_emit_recorded_errors();
|
||||
zend_free_recorded_errors();
|
||||
|
||||
return zend_accel_load_script(persistent_script, from_memory);
|
||||
}
|
||||
|
||||
|
@ -2168,6 +2159,8 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
|
|||
return accelerator_orig_compile_file(file_handle, type);
|
||||
}
|
||||
|
||||
zend_begin_record_errors();
|
||||
|
||||
SHM_PROTECT();
|
||||
HANDLE_UNBLOCK_INTERRUPTIONS();
|
||||
persistent_script = opcache_compile_file(file_handle, type, &op_array);
|
||||
|
@ -2179,6 +2172,11 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
|
|||
*/
|
||||
from_shared_memory = false;
|
||||
if (persistent_script) {
|
||||
if (ZCG(accel_directives).record_warnings) {
|
||||
persistent_script->num_warnings = EG(num_errors);
|
||||
persistent_script->warnings = EG(errors);
|
||||
}
|
||||
|
||||
/* See GH-17246: we disable GC so that user code cannot be executed during the optimizer run. */
|
||||
bool orig_gc_state = gc_enable(false);
|
||||
persistent_script = cache_script_in_shared_memory(persistent_script, key, &from_shared_memory);
|
||||
|
@ -2191,6 +2189,8 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
|
|||
if (!persistent_script) {
|
||||
SHM_PROTECT();
|
||||
HANDLE_UNBLOCK_INTERRUPTIONS();
|
||||
zend_emit_recorded_errors();
|
||||
zend_free_recorded_errors();
|
||||
return op_array;
|
||||
}
|
||||
if (from_shared_memory) {
|
||||
|
@ -2204,6 +2204,9 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
|
|||
persistent_script->dynamic_members.last_used = ZCG(request_time);
|
||||
SHM_PROTECT();
|
||||
HANDLE_UNBLOCK_INTERRUPTIONS();
|
||||
|
||||
zend_emit_recorded_errors();
|
||||
zend_free_recorded_errors();
|
||||
} else {
|
||||
|
||||
#ifndef ZEND_WIN32
|
||||
|
@ -2246,7 +2249,7 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
|
|||
SHM_PROTECT();
|
||||
HANDLE_UNBLOCK_INTERRUPTIONS();
|
||||
|
||||
replay_warnings(persistent_script->num_warnings, persistent_script->warnings);
|
||||
zend_emit_recorded_errors_ex(persistent_script->num_warnings, persistent_script->warnings);
|
||||
from_shared_memory = true;
|
||||
}
|
||||
|
||||
|
@ -2313,7 +2316,7 @@ static zend_class_entry* zend_accel_inheritance_cache_get(zend_class_entry *ce,
|
|||
entry = zend_accel_inheritance_cache_find(entry, ce, parent, traits_and_interfaces, &needs_autoload);
|
||||
if (entry) {
|
||||
if (!needs_autoload) {
|
||||
replay_warnings(entry->num_warnings, entry->warnings);
|
||||
zend_emit_recorded_errors_ex(entry->num_warnings, entry->warnings);
|
||||
if (ZCSG(map_ptr_last) > CG(map_ptr_last)) {
|
||||
zend_map_ptr_extend(ZCSG(map_ptr_last));
|
||||
}
|
||||
|
@ -2467,9 +2470,6 @@ static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce,
|
|||
entry->next = proto->inheritance_cache;
|
||||
proto->inheritance_cache = entry;
|
||||
|
||||
EG(num_errors) = 0;
|
||||
EG(errors) = NULL;
|
||||
|
||||
ZCSG(map_ptr_last) = CG(map_ptr_last);
|
||||
|
||||
zend_shared_alloc_destroy_xlat_table();
|
||||
|
|
17
ext/opcache/tests/gh17422/001.phpt
Normal file
17
ext/opcache/tests/gh17422/001.phpt
Normal file
|
@ -0,0 +1,17 @@
|
|||
--TEST--
|
||||
GH-17422 (OPcache bypasses the user-defined error handler for deprecations)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
|
||||
echo "set_error_handler: {$errstr}", PHP_EOL;
|
||||
});
|
||||
|
||||
require __DIR__ . "/warning.inc";
|
||||
|
||||
warning();
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
set_error_handler: "continue" targeting switch is equivalent to "break"
|
||||
OK: warning
|
21
ext/opcache/tests/gh17422/002.phpt
Normal file
21
ext/opcache/tests/gh17422/002.phpt
Normal file
|
@ -0,0 +1,21 @@
|
|||
--TEST--
|
||||
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Throwing error handler
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
|
||||
throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
|
||||
});
|
||||
|
||||
try {
|
||||
require __DIR__ . "/warning.inc";
|
||||
} catch (\Exception $e) {
|
||||
echo "Caught: ", $e->getMessage(), PHP_EOL;
|
||||
}
|
||||
|
||||
warning();
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Caught: "continue" targeting switch is equivalent to "break"
|
||||
OK: warning
|
17
ext/opcache/tests/gh17422/003.phpt
Normal file
17
ext/opcache/tests/gh17422/003.phpt
Normal file
|
@ -0,0 +1,17 @@
|
|||
--TEST--
|
||||
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Fatal Error
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
|
||||
function fatal_error() {}
|
||||
function fatal_error() {}
|
||||
});
|
||||
|
||||
require __DIR__ . "/warning.inc";
|
||||
|
||||
warning();
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Cannot redeclare function fatal_error() (previously declared in %s:%d) in %s on line %d
|
22
ext/opcache/tests/gh17422/004.phpt
Normal file
22
ext/opcache/tests/gh17422/004.phpt
Normal file
|
@ -0,0 +1,22 @@
|
|||
--TEST--
|
||||
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - eval
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
|
||||
eval(
|
||||
<<<'PHP'
|
||||
function warning() {
|
||||
echo "NOK", PHP_EOL;
|
||||
}
|
||||
PHP
|
||||
);
|
||||
});
|
||||
|
||||
require __DIR__ . "/warning.inc";
|
||||
|
||||
warning();
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Cannot redeclare function warning() %s
|
16
ext/opcache/tests/gh17422/005.phpt
Normal file
16
ext/opcache/tests/gh17422/005.phpt
Normal file
|
@ -0,0 +1,16 @@
|
|||
--TEST--
|
||||
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - require
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
|
||||
require_once __DIR__ . "/dummy.inc";
|
||||
});
|
||||
|
||||
require __DIR__ . "/warning.inc";
|
||||
|
||||
dummy();
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
OK: dummy
|
25
ext/opcache/tests/gh17422/006.phpt
Normal file
25
ext/opcache/tests/gh17422/006.phpt
Normal file
|
@ -0,0 +1,25 @@
|
|||
--TEST--
|
||||
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - File cache
|
||||
--INI--
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.file_cache="{TMP}"
|
||||
opcache.file_cache_only=1
|
||||
opcache.record_warnings=1
|
||||
--EXTENSIONS--
|
||||
opcache
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
|
||||
echo "set_error_handler: {$errstr}", PHP_EOL;
|
||||
});
|
||||
|
||||
require __DIR__ . "/warning.inc";
|
||||
|
||||
warning();
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
set_error_handler: "continue" targeting switch is equivalent to "break"
|
||||
OK: warning
|
18
ext/opcache/tests/gh17422/007.phpt
Normal file
18
ext/opcache/tests/gh17422/007.phpt
Normal file
|
@ -0,0 +1,18 @@
|
|||
--TEST--
|
||||
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Fatal after warning
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
|
||||
echo "set_error_handler: {$errstr}", PHP_EOL;
|
||||
});
|
||||
|
||||
require __DIR__ . "/warning-fatal.inc";
|
||||
|
||||
warning();
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Warning: "continue" targeting switch is equivalent to "break" in %s on line %d
|
||||
|
||||
Fatal error: Cannot redeclare function warning() (previously declared in %s:%d) in %s on line %d
|
14
ext/opcache/tests/gh17422/008.phpt
Normal file
14
ext/opcache/tests/gh17422/008.phpt
Normal file
|
@ -0,0 +1,14 @@
|
|||
--TEST--
|
||||
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Early binding warning
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
|
||||
echo "set_error_handler: {$errstr}", PHP_EOL;
|
||||
});
|
||||
|
||||
require __DIR__ . "/early-bind-warning.inc";
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
set_error_handler: Return type of C::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
|
16
ext/opcache/tests/gh17422/009.phpt
Normal file
16
ext/opcache/tests/gh17422/009.phpt
Normal file
|
@ -0,0 +1,16 @@
|
|||
--TEST--
|
||||
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Early binding error after warning
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
|
||||
echo "set_error_handler: {$errstr}", PHP_EOL;
|
||||
});
|
||||
|
||||
require __DIR__ . "/early-bind-warning-error.inc";
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Deprecated: Return type of C::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in %s on line %d
|
||||
|
||||
Fatal error: Declaration of C::getTimestamp(C $arg): int must be compatible with DateTime::getTimestamp(): int in %s on line %d
|
14
ext/opcache/tests/gh17422/010.phpt
Normal file
14
ext/opcache/tests/gh17422/010.phpt
Normal file
|
@ -0,0 +1,14 @@
|
|||
--TEST--
|
||||
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Inheritance warning
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
|
||||
echo "set_error_handler: {$errstr}", PHP_EOL;
|
||||
});
|
||||
|
||||
require __DIR__ . "/link-warning.inc";
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
set_error_handler: Return type of C::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
|
16
ext/opcache/tests/gh17422/011.phpt
Normal file
16
ext/opcache/tests/gh17422/011.phpt
Normal file
|
@ -0,0 +1,16 @@
|
|||
--TEST--
|
||||
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Inheritance error after warning
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
|
||||
echo "set_error_handler: {$errstr}", PHP_EOL;
|
||||
});
|
||||
|
||||
require __DIR__ . "/link-warning-error.inc";
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Deprecated: Return type of C::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in %s on line %d
|
||||
|
||||
Fatal error: Declaration of C::getTimestamp(C $arg): int must be compatible with DateTime::getTimestamp(): int %s on line %d
|
4
ext/opcache/tests/gh17422/dummy.inc
Normal file
4
ext/opcache/tests/gh17422/dummy.inc
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
function dummy() {
|
||||
echo "OK: ", __FUNCTION__, PHP_EOL;
|
||||
}
|
6
ext/opcache/tests/gh17422/early-bind-warning-error.inc
Normal file
6
ext/opcache/tests/gh17422/early-bind-warning-error.inc
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
|
||||
class C extends DateTime {
|
||||
public function getTimezone() {}
|
||||
public function getTimestamp(C $arg): int {}
|
||||
}
|
5
ext/opcache/tests/gh17422/early-bind-warning.inc
Normal file
5
ext/opcache/tests/gh17422/early-bind-warning.inc
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
class C extends DateTime {
|
||||
public function getTimezone() {}
|
||||
}
|
8
ext/opcache/tests/gh17422/link-warning-error.inc
Normal file
8
ext/opcache/tests/gh17422/link-warning-error.inc
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
interface DisableEarlyBinding {}
|
||||
|
||||
class C extends DateTime implements DisableEarlyBinding {
|
||||
public function getTimezone() {}
|
||||
public function getTimestamp(C $arg): int {}
|
||||
}
|
7
ext/opcache/tests/gh17422/link-warning.inc
Normal file
7
ext/opcache/tests/gh17422/link-warning.inc
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
interface DisableEarlyBinding {}
|
||||
|
||||
class C extends DateTime implements DisableEarlyBinding {
|
||||
public function getTimezone() {}
|
||||
}
|
9
ext/opcache/tests/gh17422/warning-fatal.inc
Normal file
9
ext/opcache/tests/gh17422/warning-fatal.inc
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
function warning() {
|
||||
switch (1) {
|
||||
case 1:
|
||||
echo "OK: ", __FUNCTION__, PHP_EOL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
function warning() {}
|
8
ext/opcache/tests/gh17422/warning.inc
Normal file
8
ext/opcache/tests/gh17422/warning.inc
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
function warning() {
|
||||
switch (1) {
|
||||
case 1:
|
||||
echo "OK: ", __FUNCTION__, PHP_EOL;
|
||||
continue;
|
||||
}
|
||||
}
|
|
@ -1394,11 +1394,11 @@ static void zend_accel_persist_class_table(HashTable *class_table)
|
|||
|
||||
zend_error_info **zend_persist_warnings(uint32_t num_warnings, zend_error_info **warnings) {
|
||||
if (warnings) {
|
||||
warnings = zend_shared_memdup_free(warnings, num_warnings * sizeof(zend_error_info *));
|
||||
warnings = zend_shared_memdup(warnings, num_warnings * sizeof(zend_error_info *));
|
||||
for (uint32_t i = 0; i < num_warnings; i++) {
|
||||
warnings[i] = zend_shared_memdup_free(warnings[i], sizeof(zend_error_info));
|
||||
zend_accel_store_string(warnings[i]->filename);
|
||||
zend_accel_store_string(warnings[i]->message);
|
||||
warnings[i] = zend_shared_memdup(warnings[i], sizeof(zend_error_info));
|
||||
}
|
||||
}
|
||||
return warnings;
|
||||
|
|
|
@ -2054,6 +2054,7 @@ function generate_tmp_php_ini()
|
|||
|
||||
/* Fallback is implied, if filecache is enabled. */
|
||||
INI.WriteLine("opcache.file_cache=" + dir);
|
||||
INI.WriteLine("opcache.record_warnings=1");
|
||||
INI.WriteLine("opcache.enable=1");
|
||||
INI.WriteLine("opcache.enable_cli=1");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue