From 731db8eba79175bdc32de504104c5cae909c3df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 28 Jan 2025 21:16:54 +0100 Subject: [PATCH 01/11] opcache: Do not disable userland error handlers in `opcache_compile_file()` 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/php-src#17422. --- ext/opcache/ZendAccelerator.c | 4 --- ext/opcache/tests/gh17422/001.phpt | 32 +++++++++++++++++++++++ ext/opcache/tests/gh17422/002.phpt | 36 ++++++++++++++++++++++++++ ext/opcache/tests/gh17422/003.phpt | 30 +++++++++++++++++++++ ext/opcache/tests/gh17422/004.phpt | 36 ++++++++++++++++++++++++++ ext/opcache/tests/gh17422/005.phpt | 34 ++++++++++++++++++++++++ ext/opcache/tests/gh17422/dummy.inc | 4 +++ ext/opcache/tests/gh17422/shutdown.inc | 6 +++++ ext/opcache/tests/gh17422/warning.inc | 8 ++++++ 9 files changed, 186 insertions(+), 4 deletions(-) create mode 100644 ext/opcache/tests/gh17422/001.phpt create mode 100644 ext/opcache/tests/gh17422/002.phpt create mode 100644 ext/opcache/tests/gh17422/003.phpt create mode 100644 ext/opcache/tests/gh17422/004.phpt create mode 100644 ext/opcache/tests/gh17422/005.phpt create mode 100644 ext/opcache/tests/gh17422/dummy.inc create mode 100644 ext/opcache/tests/gh17422/shutdown.inc create mode 100644 ext/opcache/tests/gh17422/warning.inc diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 704846c4a860f..6f14520884e24 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -1744,7 +1744,6 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl 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; @@ -1812,10 +1811,8 @@ 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(); } @@ -1848,7 +1845,6 @@ 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) { diff --git a/ext/opcache/tests/gh17422/001.phpt b/ext/opcache/tests/gh17422/001.phpt new file mode 100644 index 0000000000000..f8bc8ca12896f --- /dev/null +++ b/ext/opcache/tests/gh17422/001.phpt @@ -0,0 +1,32 @@ +--TEST-- +GH-17422 (OPcache bypasses the user-defined error handler for deprecations) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECT-- +set_error_handler: "continue" targeting switch is equivalent to "break" +OK: warning +array(3) { + [0]=> + string(7) "001.php" + [1]=> + string(12) "shutdown.inc" + [2]=> + string(11) "warning.inc" +} diff --git a/ext/opcache/tests/gh17422/002.phpt b/ext/opcache/tests/gh17422/002.phpt new file mode 100644 index 0000000000000..1fc8af33b92a0 --- /dev/null +++ b/ext/opcache/tests/gh17422/002.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Throwing error handler +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +--EXTENSIONS-- +opcache +--FILE-- +getMessage(), PHP_EOL; +} + +warning(); + +?> +--EXPECT-- +Caught: "continue" targeting switch is equivalent to "break" +OK: warning +array(3) { + [0]=> + string(7) "002.php" + [1]=> + string(12) "shutdown.inc" + [2]=> + string(11) "warning.inc" +} diff --git a/ext/opcache/tests/gh17422/003.phpt b/ext/opcache/tests/gh17422/003.phpt new file mode 100644 index 0000000000000..6862579d3563b --- /dev/null +++ b/ext/opcache/tests/gh17422/003.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Fatal Error +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +memory_limit=2M +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECTF-- +Fatal error: Allowed memory size of 2097152 bytes exhausted %s on line 6 +array(2) { + [0]=> + string(7) "003.php" + [1]=> + string(12) "shutdown.inc" +} diff --git a/ext/opcache/tests/gh17422/004.phpt b/ext/opcache/tests/gh17422/004.phpt new file mode 100644 index 0000000000000..30d0d32a70d26 --- /dev/null +++ b/ext/opcache/tests/gh17422/004.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - eval +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +memory_limit=2M +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECTF-- +Fatal error: Cannot redeclare function warning() (previously declared in %s(8) : eval()'d code:1) in %swarning.inc on line 2 +array(2) { + [0]=> + string(7) "004.php" + [1]=> + string(12) "shutdown.inc" +} diff --git a/ext/opcache/tests/gh17422/005.phpt b/ext/opcache/tests/gh17422/005.phpt new file mode 100644 index 0000000000000..a22461d889205 --- /dev/null +++ b/ext/opcache/tests/gh17422/005.phpt @@ -0,0 +1,34 @@ +--TEST-- +GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - require +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +memory_limit=2M +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECT-- +OK: dummy +array(4) { + [0]=> + string(7) "005.php" + [1]=> + string(9) "dummy.inc" + [2]=> + string(12) "shutdown.inc" + [3]=> + string(11) "warning.inc" +} diff --git a/ext/opcache/tests/gh17422/dummy.inc b/ext/opcache/tests/gh17422/dummy.inc new file mode 100644 index 0000000000000..0bb253556124a --- /dev/null +++ b/ext/opcache/tests/gh17422/dummy.inc @@ -0,0 +1,4 @@ + Date: Wed, 29 Jan 2025 21:42:21 +0100 Subject: [PATCH 02/11] Make test expectation compatible with PHP 8.3 and PHP 8.4 --- ext/opcache/tests/gh17422/004.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opcache/tests/gh17422/004.phpt b/ext/opcache/tests/gh17422/004.phpt index 30d0d32a70d26..443797d55a9fc 100644 --- a/ext/opcache/tests/gh17422/004.phpt +++ b/ext/opcache/tests/gh17422/004.phpt @@ -27,7 +27,7 @@ warning(); ?> --EXPECTF-- -Fatal error: Cannot redeclare function warning() (previously declared in %s(8) : eval()'d code:1) in %swarning.inc on line 2 +Fatal error: Cannot redeclare %Swarning() (previously declared in %s(8) : eval()'d code:1) in %swarning.inc on line 2 array(2) { [0]=> string(7) "004.php" From 279831c3f627378f05725cfb3411a11e195932ce Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 9 May 2025 18:22:40 +0200 Subject: [PATCH 03/11] Always record errors during compilation --- ...ation_to_exception_during_inheritance.phpt | 7 +-- Zend/tests/inheritance/gh15907.phpt | 7 ++- Zend/zend.c | 43 +++++++++++++-- Zend/zend.h | 1 + Zend/zend_compile.c | 1 - Zend/zend_globals.h | 3 +- Zend/zend_inheritance.c | 6 +-- Zend/zend_vm_def.h | 4 +- ext/opcache/ZendAccelerator.c | 52 ++++++++++--------- ext/opcache/tests/gh17422/003.phpt | 4 +- ext/opcache/tests/gh17422/004.phpt | 4 +- ext/opcache/zend_persist.c | 4 +- 12 files changed, 91 insertions(+), 45 deletions(-) diff --git a/Zend/tests/inheritance/deprecation_to_exception_during_inheritance.phpt b/Zend/tests/inheritance/deprecation_to_exception_during_inheritance.phpt index 6e7e81cd2957c..4bdf4b5d1b956 100644 --- a/Zend/tests/inheritance/deprecation_to_exception_during_inheritance.phpt +++ b/Zend/tests/inheritance/deprecation_to_exception_during_inheritance.phpt @@ -1,5 +1,5 @@ --TEST-- -Deprecation promoted to exception should result in fatal error during inheritance +Deprecation promoted to exception during inheritance --SKIPIF-- --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 diff --git a/Zend/tests/inheritance/gh15907.phpt b/Zend/tests/inheritance/gh15907.phpt index 8d6dada36ad08..cd21e91db8a61 100644 --- a/Zend/tests/inheritance/gh15907.phpt +++ b/Zend/tests/inheritance/gh15907.phpt @@ -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...', '/home/arnaud/de...', 7) +#1 {main} + thrown in %s on line %d diff --git a/Zend/zend.c b/Zend/zend.c index 2d8a0f455f8b4..35e2ce70a92bb 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1452,6 +1452,31 @@ 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)) { + if (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 +1489,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,15 +1782,20 @@ ZEND_API void zend_begin_record_errors(void) EG(errors) = NULL; } -ZEND_API void zend_emit_recorded_errors(void) +ZEND_API void zend_emit_recorded_errors_ex(uint32_t num_errors, zend_error_info **errors) { - EG(record_errors) = false; - for (uint32_t i = 0; i < EG(num_errors); i++) { - zend_error_info *error = EG(errors)[i]; + 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; + zend_emit_recorded_errors_ex(EG(num_errors), EG(errors)); +} + ZEND_API void zend_free_recorded_errors(void) { if (!EG(num_errors)) { diff --git a/Zend/zend.h b/Zend/zend.h index 0cf1faeb653fe..b103faf5ab59f 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -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() diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 0669d106f15e9..216becdef51c3 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1333,7 +1333,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; } diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 079bfb99caccf..b0214dbb2b8ae 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -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; diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index e718eb5684e65..ed7a8213bcade 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -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), @@ -3759,6 +3756,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) { diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 9c2ba0038b4a4..47b0dfe469a3a 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -7926,7 +7926,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(); } } @@ -7950,7 +7950,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(); } } diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 6f14520884e24..9bd88c5cc6181 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -1732,13 +1732,6 @@ 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; @@ -1812,11 +1805,6 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl orig_functions_count = EG(function_table)->nNumUsed; orig_class_count = EG(class_table)->nNumUsed; - /* Override them with ours */ - if (ZCG(accel_directives).record_warnings) { - zend_begin_record_errors(); - } - zend_try { orig_compiler_options = CG(compiler_options); CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY; @@ -1845,12 +1833,12 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl /* Restore originals */ CG(active_op_array) = orig_active_op_array; - 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; @@ -1865,10 +1853,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 */ @@ -1950,7 +1934,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)); @@ -1959,11 +1943,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); } @@ -2162,6 +2157,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); @@ -2173,6 +2170,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); @@ -2185,6 +2187,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) { @@ -2198,6 +2202,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 @@ -2240,7 +2247,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; } @@ -2307,7 +2314,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)); } @@ -2461,9 +2468,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(); diff --git a/ext/opcache/tests/gh17422/003.phpt b/ext/opcache/tests/gh17422/003.phpt index 6862579d3563b..f24448ddf6eeb 100644 --- a/ext/opcache/tests/gh17422/003.phpt +++ b/ext/opcache/tests/gh17422/003.phpt @@ -22,9 +22,11 @@ warning(); ?> --EXPECTF-- Fatal error: Allowed memory size of 2097152 bytes exhausted %s on line 6 -array(2) { +array(3) { [0]=> string(7) "003.php" [1]=> string(12) "shutdown.inc" + [2]=> + string(11) "warning.inc" } diff --git a/ext/opcache/tests/gh17422/004.phpt b/ext/opcache/tests/gh17422/004.phpt index 443797d55a9fc..d2b6d8e5f70c0 100644 --- a/ext/opcache/tests/gh17422/004.phpt +++ b/ext/opcache/tests/gh17422/004.phpt @@ -28,9 +28,11 @@ warning(); ?> --EXPECTF-- Fatal error: Cannot redeclare %Swarning() (previously declared in %s(8) : eval()'d code:1) in %swarning.inc on line 2 -array(2) { +array(3) { [0]=> string(7) "004.php" [1]=> string(12) "shutdown.inc" + [2]=> + string(11) "warning.inc" } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 202cd73c90422..9386dfc64c0bb 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -1355,11 +1355,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; From 6b0cabc7a97880bde6c5f74faa17592171a4abb4 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Mon, 12 May 2025 16:27:02 +0200 Subject: [PATCH 04/11] Generated file --- Zend/zend_vm_execute.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 226e0446abbd1..3a0b606448b44 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3207,7 +3207,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(); } } @@ -8002,7 +8002,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(); } } From 4338079d4da2f3b651aec2d0c1b1898b5ce8d276 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 13 May 2025 16:13:51 +0200 Subject: [PATCH 05/11] Unify non-opcache behavior --- ...tion_during_inheritance_can_be_caught.phpt | 35 +++++++++++++++++++ Zend/zend_inheritance.c | 32 +++++++++++------ Zend/zend_language_scanner.l | 10 ++++++ ext/opcache/tests/gh17422/001.phpt | 15 -------- ext/opcache/tests/gh17422/002.phpt | 15 -------- ext/opcache/tests/gh17422/003.phpt | 21 ++--------- ext/opcache/tests/gh17422/004.phpt | 18 +--------- ext/opcache/tests/gh17422/005.phpt | 18 ---------- ext/opcache/tests/gh17422/006.phpt | 25 +++++++++++++ ext/opcache/tests/gh17422/007.phpt | 18 ++++++++++ ext/opcache/tests/gh17422/008.phpt | 14 ++++++++ ext/opcache/tests/gh17422/009.phpt | 16 +++++++++ ext/opcache/tests/gh17422/010.phpt | 14 ++++++++ ext/opcache/tests/gh17422/011.phpt | 16 +++++++++ .../gh17422/early-bind-warning-error.inc | 6 ++++ .../tests/gh17422/early-bind-warning.inc | 5 +++ .../tests/gh17422/link-warning-error.inc | 8 +++++ ext/opcache/tests/gh17422/link-warning.inc | 7 ++++ ext/opcache/tests/gh17422/warning-fatal.inc | 9 +++++ 19 files changed, 208 insertions(+), 94 deletions(-) create mode 100644 Zend/tests/inheritance/deprecation_to_exception_during_inheritance_can_be_caught.phpt create mode 100644 ext/opcache/tests/gh17422/006.phpt create mode 100644 ext/opcache/tests/gh17422/007.phpt create mode 100644 ext/opcache/tests/gh17422/008.phpt create mode 100644 ext/opcache/tests/gh17422/009.phpt create mode 100644 ext/opcache/tests/gh17422/010.phpt create mode 100644 ext/opcache/tests/gh17422/011.phpt create mode 100644 ext/opcache/tests/gh17422/early-bind-warning-error.inc create mode 100644 ext/opcache/tests/gh17422/early-bind-warning.inc create mode 100644 ext/opcache/tests/gh17422/link-warning-error.inc create mode 100644 ext/opcache/tests/gh17422/link-warning.inc create mode 100644 ext/opcache/tests/gh17422/warning-fatal.inc diff --git a/Zend/tests/inheritance/deprecation_to_exception_during_inheritance_can_be_caught.phpt b/Zend/tests/inheritance/deprecation_to_exception_during_inheritance_can_be_caught.phpt new file mode 100644 index 0000000000000..7a59cca70bd62 --- /dev/null +++ b/Zend/tests/inheritance/deprecation_to_exception_during_inheritance_can_be_caught.phpt @@ -0,0 +1,35 @@ +--TEST-- +Deprecation promoted to exception during inheritance +--SKIPIF-- + +--FILE-- +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" +} diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index ed7a8213bcade..d3ca2fc1d4ddb 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -3543,8 +3543,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); @@ -3556,16 +3554,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 */ @@ -3917,10 +3920,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(); } @@ -3942,13 +3947,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; @@ -3966,6 +3971,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); } diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 4c883b81c5f7d..1bbb19df7f9b5 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -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); diff --git a/ext/opcache/tests/gh17422/001.phpt b/ext/opcache/tests/gh17422/001.phpt index f8bc8ca12896f..04a8bc38a9efa 100644 --- a/ext/opcache/tests/gh17422/001.phpt +++ b/ext/opcache/tests/gh17422/001.phpt @@ -1,15 +1,8 @@ --TEST-- GH-17422 (OPcache bypasses the user-defined error handler for deprecations) ---INI-- -opcache.enable=1 -opcache.enable_cli=1 ---EXTENSIONS-- -opcache --FILE-- - string(7) "001.php" - [1]=> - string(12) "shutdown.inc" - [2]=> - string(11) "warning.inc" -} diff --git a/ext/opcache/tests/gh17422/002.phpt b/ext/opcache/tests/gh17422/002.phpt index 1fc8af33b92a0..6145f71a8e587 100644 --- a/ext/opcache/tests/gh17422/002.phpt +++ b/ext/opcache/tests/gh17422/002.phpt @@ -1,15 +1,8 @@ --TEST-- GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Throwing error handler ---INI-- -opcache.enable=1 -opcache.enable_cli=1 ---EXTENSIONS-- -opcache --FILE-- - string(7) "002.php" - [1]=> - string(12) "shutdown.inc" - [2]=> - string(11) "warning.inc" -} diff --git a/ext/opcache/tests/gh17422/003.phpt b/ext/opcache/tests/gh17422/003.phpt index f24448ddf6eeb..a1330eb88afab 100644 --- a/ext/opcache/tests/gh17422/003.phpt +++ b/ext/opcache/tests/gh17422/003.phpt @@ -1,18 +1,11 @@ --TEST-- GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Fatal Error ---INI-- -opcache.enable=1 -opcache.enable_cli=1 -memory_limit=2M ---EXTENSIONS-- -opcache --FILE-- --EXPECTF-- -Fatal error: Allowed memory size of 2097152 bytes exhausted %s on line 6 -array(3) { - [0]=> - string(7) "003.php" - [1]=> - string(12) "shutdown.inc" - [2]=> - string(11) "warning.inc" -} +Fatal error: Cannot redeclare function fatal_error() (previously declared in %s:%d) in %s on line %d diff --git a/ext/opcache/tests/gh17422/004.phpt b/ext/opcache/tests/gh17422/004.phpt index d2b6d8e5f70c0..4fa659a763e8d 100644 --- a/ext/opcache/tests/gh17422/004.phpt +++ b/ext/opcache/tests/gh17422/004.phpt @@ -1,16 +1,8 @@ --TEST-- GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - eval ---INI-- -opcache.enable=1 -opcache.enable_cli=1 -memory_limit=2M ---EXTENSIONS-- -opcache --FILE-- --EXPECTF-- -Fatal error: Cannot redeclare %Swarning() (previously declared in %s(8) : eval()'d code:1) in %swarning.inc on line 2 -array(3) { - [0]=> - string(7) "004.php" - [1]=> - string(12) "shutdown.inc" - [2]=> - string(11) "warning.inc" -} +Fatal error: Cannot redeclare function warning() %s diff --git a/ext/opcache/tests/gh17422/005.phpt b/ext/opcache/tests/gh17422/005.phpt index a22461d889205..1b4818d91cc98 100644 --- a/ext/opcache/tests/gh17422/005.phpt +++ b/ext/opcache/tests/gh17422/005.phpt @@ -1,16 +1,8 @@ --TEST-- GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - require ---INI-- -opcache.enable=1 -opcache.enable_cli=1 -memory_limit=2M ---EXTENSIONS-- -opcache --FILE-- --EXPECT-- OK: dummy -array(4) { - [0]=> - string(7) "005.php" - [1]=> - string(9) "dummy.inc" - [2]=> - string(12) "shutdown.inc" - [3]=> - string(11) "warning.inc" -} diff --git a/ext/opcache/tests/gh17422/006.phpt b/ext/opcache/tests/gh17422/006.phpt new file mode 100644 index 0000000000000..b9d1991718438 --- /dev/null +++ b/ext/opcache/tests/gh17422/006.phpt @@ -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-- + +--EXPECT-- +set_error_handler: "continue" targeting switch is equivalent to "break" +OK: warning diff --git a/ext/opcache/tests/gh17422/007.phpt b/ext/opcache/tests/gh17422/007.phpt new file mode 100644 index 0000000000000..59b2306f52d89 --- /dev/null +++ b/ext/opcache/tests/gh17422/007.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Fatal after warning +--FILE-- + +--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 diff --git a/ext/opcache/tests/gh17422/008.phpt b/ext/opcache/tests/gh17422/008.phpt new file mode 100644 index 0000000000000..bcabb071e6502 --- /dev/null +++ b/ext/opcache/tests/gh17422/008.phpt @@ -0,0 +1,14 @@ +--TEST-- +GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Early binding warning +--FILE-- + +--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 diff --git a/ext/opcache/tests/gh17422/009.phpt b/ext/opcache/tests/gh17422/009.phpt new file mode 100644 index 0000000000000..b071ad9478ded --- /dev/null +++ b/ext/opcache/tests/gh17422/009.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Early binding error after warning +--FILE-- + +--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 diff --git a/ext/opcache/tests/gh17422/010.phpt b/ext/opcache/tests/gh17422/010.phpt new file mode 100644 index 0000000000000..4e94b5e779410 --- /dev/null +++ b/ext/opcache/tests/gh17422/010.phpt @@ -0,0 +1,14 @@ +--TEST-- +GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Inheritance warning +--FILE-- + +--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 diff --git a/ext/opcache/tests/gh17422/011.phpt b/ext/opcache/tests/gh17422/011.phpt new file mode 100644 index 0000000000000..7034e5b2a4612 --- /dev/null +++ b/ext/opcache/tests/gh17422/011.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Inheritance error after warning +--FILE-- + +--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 diff --git a/ext/opcache/tests/gh17422/early-bind-warning-error.inc b/ext/opcache/tests/gh17422/early-bind-warning-error.inc new file mode 100644 index 0000000000000..9104a832d6636 --- /dev/null +++ b/ext/opcache/tests/gh17422/early-bind-warning-error.inc @@ -0,0 +1,6 @@ + Date: Tue, 13 May 2025 16:31:40 +0200 Subject: [PATCH 06/11] Fix test --- Zend/tests/inheritance/gh15907.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/tests/inheritance/gh15907.phpt b/Zend/tests/inheritance/gh15907.phpt index cd21e91db8a61..c92e40a4ba30b 100644 --- a/Zend/tests/inheritance/gh15907.phpt +++ b/Zend/tests/inheritance/gh15907.phpt @@ -16,6 +16,6 @@ class C implements Serializable { --EXPECTF-- 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...', '/home/arnaud/de...', 7) +#0 %s(%d): {closure:%s:%d}(8192, 'C implements th...', '%s', 7) #1 {main} thrown in %s on line %d From 3ec6d9d781db7340984d52f0006a841abc048cb1 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 13 May 2025 16:34:12 +0200 Subject: [PATCH 07/11] Remove file --- ext/opcache/tests/gh17422/shutdown.inc | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 ext/opcache/tests/gh17422/shutdown.inc diff --git a/ext/opcache/tests/gh17422/shutdown.inc b/ext/opcache/tests/gh17422/shutdown.inc deleted file mode 100644 index c1a4ca173fd1f..0000000000000 --- a/ext/opcache/tests/gh17422/shutdown.inc +++ /dev/null @@ -1,6 +0,0 @@ - Date: Tue, 13 May 2025 16:34:43 +0200 Subject: [PATCH 08/11] Typo --- ext/opcache/tests/gh17422/link-warning.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opcache/tests/gh17422/link-warning.inc b/ext/opcache/tests/gh17422/link-warning.inc index 4549d09bbfe84..9e8bb66ea7a3c 100644 --- a/ext/opcache/tests/gh17422/link-warning.inc +++ b/ext/opcache/tests/gh17422/link-warning.inc @@ -1,7 +1,7 @@ Date: Tue, 13 May 2025 16:36:30 +0200 Subject: [PATCH 09/11] Simplify --- Zend/zend.c | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index 35e2ce70a92bb..2f5a21ef24040 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1453,28 +1453,26 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( } /* Emit any delayed error before handling fatal error */ - if ((type & E_FATAL_ERRORS) && !(type & E_DONT_BAIL)) { - if (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 ((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)) { From 167e45ea8ed5728e5605281eb6f1d18358cdfa28 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 14 May 2025 14:14:32 +0200 Subject: [PATCH 10/11] Enable opcache.record_warnings when opcache.file_cache is set, in the generated tmp-php.ini --- .github/scripts/windows/test_task.bat | 1 + win32/build/confutils.js | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/scripts/windows/test_task.bat b/.github/scripts/windows/test_task.bat index 43e7763e70294..503337bfb67f3 100644 --- a/.github/scripts/windows/test_task.bat +++ b/.github/scripts/windows/test_task.bat @@ -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 if "%OPCACHE%" equ "1" echo zend_extension=php_opcache.dll >> %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 diff --git a/win32/build/confutils.js b/win32/build/confutils.js index 0f97a1a2d29c6..ff35e1230be78 100644 --- a/win32/build/confutils.js +++ b/win32/build/confutils.js @@ -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"); } From 0127e1a802fbca041a4e201a4548ece12a798ad1 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 14 May 2025 18:15:03 +0200 Subject: [PATCH 11/11] Quotes --- ext/opcache/tests/gh17422/006.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opcache/tests/gh17422/006.phpt b/ext/opcache/tests/gh17422/006.phpt index b9d1991718438..3c1303dfa8444 100644 --- a/ext/opcache/tests/gh17422/006.phpt +++ b/ext/opcache/tests/gh17422/006.phpt @@ -3,7 +3,7 @@ GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Fi --INI-- opcache.enable=1 opcache.enable_cli=1 -opcache.file_cache={TMP} +opcache.file_cache="{TMP}" opcache.file_cache_only=1 opcache.record_warnings=1 --EXTENSIONS--