From 4803d4336a73b81efe3cddc8b92f6e0a8e0136da Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Fri, 26 Jun 2026 06:29:53 -0400 Subject: [PATCH 1/8] Defer user error handlers emitted from user-code opcodes A user set_error_handler() invoked synchronously mid-opcode can free or mutate state the opcode still holds a raw pointer into, causing a use-after-free or assertion failure (GH-16792, GH-17416, GH-18274, GH-20042, GH-21245, GH-22419). Buffer diagnostics raised from a user-code opcode and run the handler at the next VM safepoint, after the opcode completes, reusing the existing vm_interrupt mechanism. Diagnostics from internal functions still run synchronously, since such a function may observe the handler's effect on EG(exception) or throw immediately after emitting the diagnostic. A deferred diagnostic that surfaces while another error handler is running is delivered to the user handler first, before the handler that triggered the flush. php_sort() and the internal-pointer functions separate the array and pin the object across the now-synchronous deprecation they raise on a by-ref array or object argument, so a handler cannot free it. Save the opline before dispatching a pending interrupt. The deferral arms vm_interrupt mid-statement, so the interrupt can fire at a point such as a forward jump after an opcode already freed a temporary. The tracing-JIT trace recorder runs CALL-convention handlers whose opline is a local register distinct from EX(opline); ZEND_VM_SET_OPCODE updated the register but not EX(opline), so the interrupt helper reloaded a stale EX(opline) and re-executed the freed temporary's opcode under tracing JIT. Saving the opline before the dispatch, gated on the existing vm_interrupt check, resumes at the correct instruction without affecting the common path. The inlined tracing-JIT function-leave flushes pending diagnostics before tearing down the frame, so a handler that throws there leaves a pending exception and a live return temporary the teardown then double-frees. Emit the leave's EG(exception) guard unconditionally, since the flush can always throw, rather than only when an unused return value's destruction could. Run a pending deferred error's handler at the next call boundary instead of the JIT's coarser safepoint: flush EG(deferred_errors) at the top of the tracing/function-JIT DO_*CALL codegen, so a handler that inspects or restores state runs while that state is still live, matching the interpreter's per-call ZEND_VM_ENTER safepoint. Keep diagnostics raised by the call-execution opcodes (DO_FCALL, DO_FCALL_BY_NAME, DO_UCALL) synchronous, like the INIT_* opcodes: a #[\Deprecated] function's deprecation is emitted as the function is invoked and its handler may throw to suppress the call, so deferring it would run the callee body first. --- Zend/tests/ArrayAccess/bug41209.phpt | 3 +- .../bitwise_not_precision_exception.phpt | 1 + Zend/tests/bug63206.phpt | 8 +- Zend/tests/bug70662.phpt | 2 +- Zend/tests/bug70785.phpt | 25 +- Zend/tests/bug72101.phpt | 11 +- Zend/tests/bug76534.phpt | 5 +- Zend/tests/bug78598.phpt | 32 ++- Zend/tests/bug79784.phpt | 18 +- Zend/tests/bug79793.phpt | 4 +- Zend/tests/closures/closure_031.phpt | 2 +- Zend/tests/compound_assign_failure.phpt | 11 +- Zend/tests/concat/bug81705.phpt | 4 +- Zend/tests/dynamic_prop_deprecation_002.phpt | 1 - Zend/tests/exception_in_nested_rope.phpt | 7 +- Zend/tests/exception_in_rope_end.phpt | 8 +- .../exceptions/exception_before_fatal.phpt | 8 +- Zend/tests/falsetoarray_002.phpt | 10 +- Zend/tests/falsetoarray_003.phpt | 2 +- .../error_static_call_trait_method_002.phpt | 9 + Zend/tests/get_class_basic.phpt | 1 + Zend/tests/gh16792.phpt | 16 ++ Zend/tests/gh17416.phpt | 30 +++ Zend/tests/gh21245.phpt | 21 ++ Zend/tests/gh22419.phpt | 19 ++ .../in-de-crement/incdec_bool_exception.phpt | 11 +- .../incdec_strings_exception.phpt | 31 ++- Zend/tests/in-de-crement/incdec_undef.phpt | 4 +- .../increment_diagnostic_change_type.phpt | 2 +- ...nt_diagnostic_change_type_do_operator.phpt | 2 +- ...709_globals_unset_after_undef_warning.phpt | 8 +- ...94_globals_unset_after_string_warning.phpt | 8 +- .../unset_globals_in_error_handler.phpt | 32 +-- ...nset_object_property_in_error_handler.phpt | 34 +-- ...rty_converted_to_obj_in_error_handler.phpt | 1 - Zend/tests/offsets/array_offset_002.phpt | 4 +- .../offsets/null_offset_dep_promoted.phpt | 8 +- Zend/tests/offsets/null_offset_no_uaf.phpt | 2 +- .../null_offset_unset_via_error_handler.phpt | 3 +- Zend/tests/operator_unsupported_types.phpt | 123 +-------- Zend/tests/oss_fuzz_54325.phpt | 3 +- Zend/tests/oss_fuzz_61712.phpt | 2 +- Zend/tests/oss_fuzz_61712b.phpt | 2 +- Zend/tests/str_offset_006.phpt | 10 +- Zend/tests/str_offset_007.phpt | 5 +- Zend/tests/str_offset_008.phpt | 2 +- .../strlen_deprecation_to_exception.phpt | 8 +- .../temporary_cleaning_015.phpt | 10 +- .../temporary_cleaning_016.phpt | 7 +- .../non-rep-float-as-int-extra1.phpt | 2 +- .../non-rep-float-as-int-extra3.phpt | 2 +- .../non-rep-float-as-int-extra4.phpt | 2 +- Zend/tests/type_coercion/gh22112.phpt | 2 + Zend/tests/undef_index_to_exception.phpt | 15 +- Zend/tests/undef_var_in_verify_return.phpt | 7 +- Zend/zend.c | 251 +++++++++++++----- Zend/zend.h | 2 + Zend/zend_execute.c | 8 +- Zend/zend_execute_API.c | 3 + Zend/zend_globals.h | 5 + Zend/zend_vm_def.h | 40 ++- Zend/zend_vm_execute.h | 84 ++++-- ext/opcache/jit/zend_jit.c | 2 +- ext/opcache/jit/zend_jit_ir.c | 57 ++-- ext/opcache/jit/zend_jit_trace.c | 8 +- ext/opcache/jit/zend_jit_vm_helpers.c | 4 + ext/opcache/tests/jit/assign_043.phpt | 7 +- ext/opcache/tests/jit/assign_dim_005.phpt | 7 +- ext/opcache/tests/jit/assign_dim_007.phpt | 4 +- ext/opcache/tests/jit/assign_dim_011.phpt | 10 +- ext/opcache/tests/jit/assign_dim_014.phpt | 3 +- ext/opcache/tests/jit/assign_dim_015.phpt | 2 +- ext/opcache/tests/jit/assign_dim_op_006.phpt | 5 +- ext/opcache/tests/jit/fetch_dim_rw_004.phpt | 9 +- ext/opcache/tests/jit/gh18262-002.phpt | 6 +- ext/opcache/tests/jit/gh18262-003.phpt | 1 + .../tests/jit/qm_assign_undef_exception.phpt | 8 +- ext/opcache/tests/jit/type_check_001.phpt | 7 +- ext/spl/tests/gh18274.phpt | 21 ++ ext/standard/array.c | 98 ++++--- ext/standard/tests/array/gh20042.phpt | 18 ++ .../sort/array_multisort_variation2.phpt | 4 +- .../get_class_methods_variation_001.phpt | 5 - .../get_parent_class_variation_002.phpt | 5 - .../is_subclass_of_variation_001.phpt | 14 +- .../is_subclass_of_variation_004.phpt | 14 +- .../method_exists_variation_001.phpt | 7 - .../file/file_put_contents_variation2.phpt | 2 + .../tests/image/getimagesize_variation2.phpt | 4 +- ext/zend_test/tests/observer_error_05.phpt | 14 +- tests/lang/bug22592.phpt | 4 +- tests/lang/bug25547.phpt | 2 +- tests/lang/bug25922.phpt | 2 +- 93 files changed, 847 insertions(+), 510 deletions(-) create mode 100644 Zend/tests/gh16792.phpt create mode 100644 Zend/tests/gh17416.phpt create mode 100644 Zend/tests/gh21245.phpt create mode 100644 Zend/tests/gh22419.phpt create mode 100644 ext/spl/tests/gh18274.phpt create mode 100644 ext/standard/tests/array/gh20042.phpt diff --git a/Zend/tests/ArrayAccess/bug41209.phpt b/Zend/tests/ArrayAccess/bug41209.phpt index 1a7ee8aa515c..f1bc4267d762 100644 --- a/Zend/tests/ArrayAccess/bug41209.phpt +++ b/Zend/tests/ArrayAccess/bug41209.phpt @@ -42,5 +42,6 @@ echo "Done\n"; Fatal error: Uncaught ErrorException: Undefined variable $id in %s:%d Stack trace: #0 %s(%d): env::errorHandler(2, 'Undefined varia...', '%s', %d) -#1 {main} +#1 %s(%d): cache->offsetExists(NULL) +#2 {main} thrown in %s on line %d diff --git a/Zend/tests/bitwise_not_precision_exception.phpt b/Zend/tests/bitwise_not_precision_exception.phpt index e28bf8f4e17b..87631788d561 100644 --- a/Zend/tests/bitwise_not_precision_exception.phpt +++ b/Zend/tests/bitwise_not_precision_exception.phpt @@ -12,4 +12,5 @@ try { } ?> --EXPECT-- +int(-1) The float INF is not representable as an int, cast occurred diff --git a/Zend/tests/bug63206.phpt b/Zend/tests/bug63206.phpt index 6aba55eca1ba..9cb056d48b5c 100644 --- a/Zend/tests/bug63206.phpt +++ b/Zend/tests/bug63206.phpt @@ -22,8 +22,10 @@ set_error_handler(function() { $triggerNotice1++; $triggerNotice2++; ?> ---EXPECT-- +--EXPECTF-- Second handler -Internal handler + +Warning: Undefined variable $triggerInternalNotice in %s on line %d Second handler -Internal handler + +Warning: Undefined variable $triggerInternalNotice in %s on line %d diff --git a/Zend/tests/bug70662.phpt b/Zend/tests/bug70662.phpt index ab540c9d16e5..2bda8141bab4 100644 --- a/Zend/tests/bug70662.phpt +++ b/Zend/tests/bug70662.phpt @@ -14,5 +14,5 @@ var_dump($a); --EXPECT-- array(1) { ["b"]=> - int(2) + int(1) } diff --git a/Zend/tests/bug70785.phpt b/Zend/tests/bug70785.phpt index 05b1b6afa0a0..16789a436ab0 100644 --- a/Zend/tests/bug70785.phpt +++ b/Zend/tests/bug70785.phpt @@ -7,19 +7,32 @@ set_error_handler(function($no, $msg) { throw new Exception($msg); }); -try { +function smart_branch() { if ($a === null) { // ZEND_VM_SMART_BRANCH - undefined_function('Null'); + return 'branch'; } -} catch (Exception $e) { + return 'no branch'; } -try { +function next_opcode() { $c === 3; // ZEND_VM_NEXT_OPCODE - undefined_function(); + return 'done'; +} + +try { + smart_branch(); } catch (Exception $e) { + echo $e->getMessage(), PHP_EOL; } + +try { + next_opcode(); +} catch (Exception $e) { + echo $e->getMessage(), PHP_EOL; +} +echo "okey\n"; ?> -okey --EXPECT-- +Undefined variable $a +Undefined variable $c okey diff --git a/Zend/tests/bug72101.phpt b/Zend/tests/bug72101.phpt index 6362a7ccb303..64393f07ba98 100644 --- a/Zend/tests/bug72101.phpt +++ b/Zend/tests/bug72101.phpt @@ -79,9 +79,10 @@ $foo->bar($a, $b, $c); Fatal error: Uncaught Error: Class "DoesNotExists" not found in %s:%d Stack trace: #0 %s(%d): {closure:%s:%d}(2, 'MethodCallbackB...', '%s', 8) -#1 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Stub_ReturnCallback->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static)) -#2 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Matcher->invoked(Object(PHPUnit_Framework_MockObject_Invocation_Static)) -#3 %sbug72101.php(%d): PHPUnit_Framework_MockObject_InvocationMocker->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static)) -#4 %sbug72101.php(%d): Mock_MethodCallbackByReference_7b180d26->bar(0, 0, 0) -#5 {main} +#1 %sbug72101.php(%d): MethodCallbackByReference->callback(0, 0, 0) +#2 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Stub_ReturnCallback->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static)) +#3 %sbug72101.php(%d): PHPUnit_Framework_MockObject_Matcher->invoked(Object(PHPUnit_Framework_MockObject_Invocation_Static)) +#4 %sbug72101.php(%d): PHPUnit_Framework_MockObject_InvocationMocker->invoke(Object(PHPUnit_Framework_MockObject_Invocation_Static)) +#5 %sbug72101.php(%d): Mock_MethodCallbackByReference_7b180d26->bar(0, 0, 0) +#6 {main} thrown in %sbug72101.php on line %d diff --git a/Zend/tests/bug76534.phpt b/Zend/tests/bug76534.phpt index c9c897110d6c..becbad3c0405 100644 --- a/Zend/tests/bug76534.phpt +++ b/Zend/tests/bug76534.phpt @@ -10,8 +10,7 @@ $x = "foo"; $y = &$x["2bar"]; ?> --EXPECTF-- -Fatal error: Uncaught Exception: Illegal string offset "2bar" in %s:%d +Fatal error: Uncaught Error: Cannot create references to/from string offsets in %s:%d Stack trace: -#0 %s(%d): {closure:%s:%d}(2, 'Illegal string ...', '%s', 7) -#1 {main} +#0 {main} thrown in %sbug76534.php on line %d diff --git a/Zend/tests/bug78598.phpt b/Zend/tests/bug78598.phpt index 7e3559f27c26..976040204865 100644 --- a/Zend/tests/bug78598.phpt +++ b/Zend/tests/bug78598.phpt @@ -25,7 +25,31 @@ var_dump($my_var); ?> --EXPECT-- -int(0) -int(0) -int(0) -int(0) +array(1) { + [0]=> + string(3) "xyz" +} +array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + string(3) "xyz" + } + } +} +array(1) { + ["foo"]=> + string(3) "xyz" +} +array(1) { + ["foo"]=> + array(1) { + ["bar"]=> + array(1) { + ["baz"]=> + string(3) "xyz" + } + } +} diff --git a/Zend/tests/bug79784.phpt b/Zend/tests/bug79784.phpt index be1cd729e969..83e64626bba7 100644 --- a/Zend/tests/bug79784.phpt +++ b/Zend/tests/bug79784.phpt @@ -15,6 +15,18 @@ var_dump($a); ?> --EXPECT-- -NULL -NULL -NULL +array(1) { + [""]=> + string(1) "x" +} +array(1) { + [""]=> + string(1) "x" +} +array(1) { + [""]=> + array(1) { + [""]=> + string(1) "x" + } +} diff --git a/Zend/tests/bug79793.phpt b/Zend/tests/bug79793.phpt index 5491a2669ed9..4af55ffd07cb 100644 --- a/Zend/tests/bug79793.phpt +++ b/Zend/tests/bug79793.phpt @@ -18,15 +18,15 @@ var_dump($ary); ?> --EXPECT-- -Undefined array key "foobar" array(1) { ["foobar"]=> int(1) } -Undefined array key "foobarbaz" +Undefined array key "foobar" array(2) { ["foobar"]=> int(1) ["foobarbaz"]=> int(1) } +Undefined array key "foobarbaz" diff --git a/Zend/tests/closures/closure_031.phpt b/Zend/tests/closures/closure_031.phpt index 19f3dc6e3212..7f9d3780e4ff 100644 --- a/Zend/tests/closures/closure_031.phpt +++ b/Zend/tests/closures/closure_031.phpt @@ -15,5 +15,5 @@ try { } ?> --EXPECT-- -Warning: Undefined property: Closure::$a NULL +Warning: Undefined property: Closure::$a diff --git a/Zend/tests/compound_assign_failure.phpt b/Zend/tests/compound_assign_failure.phpt index 0dc9af85f253..4ff85da4561c 100644 --- a/Zend/tests/compound_assign_failure.phpt +++ b/Zend/tests/compound_assign_failure.phpt @@ -22,14 +22,16 @@ try { set_error_handler(function($type, $msg) { throw new Exception($msg); }); +function concat(&$a, $b) { $a .= $b; } + try { $a = []; - $a .= "foo"; + concat($a, "foo"); } catch (Throwable $e) { var_dump($a); } try { $a = "foo"; - $a .= []; + concat($a, []); } catch (Throwable $e) { var_dump($a); } $x = new stdClass; @@ -202,9 +204,8 @@ var_dump($x); int(1) int(1) int(1) -array(0) { -} -string(3) "foo" +string(8) "Arrayfoo" +string(8) "fooArray" object(stdClass)#%d (0) { } int(1) diff --git a/Zend/tests/concat/bug81705.phpt b/Zend/tests/concat/bug81705.phpt index 1c00b1c77d4b..10eea6c0a35c 100644 --- a/Zend/tests/concat/bug81705.phpt +++ b/Zend/tests/concat/bug81705.phpt @@ -15,5 +15,5 @@ $my_var .= $GLOBALS["arr"]; var_dump($my_var); ?> --EXPECT-- -error -string(6) "aArray" \ No newline at end of file +string(6) "aArray" +error \ No newline at end of file diff --git a/Zend/tests/dynamic_prop_deprecation_002.phpt b/Zend/tests/dynamic_prop_deprecation_002.phpt index bd0d3aa5a7df..20182d2a8923 100644 --- a/Zend/tests/dynamic_prop_deprecation_002.phpt +++ b/Zend/tests/dynamic_prop_deprecation_002.phpt @@ -15,4 +15,3 @@ try { ?> --EXPECT-- Err: Creation of dynamic property class@anonymous::$y is deprecated -Exception: Cannot create dynamic property class@anonymous::$y diff --git a/Zend/tests/exception_in_nested_rope.phpt b/Zend/tests/exception_in_nested_rope.phpt index 7afd1412354a..27cfe6d77168 100644 --- a/Zend/tests/exception_in_nested_rope.phpt +++ b/Zend/tests/exception_in_nested_rope.phpt @@ -15,4 +15,9 @@ try { ?> --EXPECTF-- Deprecated: Using ${expr} (variable variables) in strings is deprecated, use {${expr}} instead in %s on line %d -Exception + +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/exception_in_rope_end.phpt b/Zend/tests/exception_in_rope_end.phpt index 3e6402c60f42..da6fa6a61903 100644 --- a/Zend/tests/exception_in_rope_end.phpt +++ b/Zend/tests/exception_in_rope_end.phpt @@ -13,5 +13,9 @@ try { } ?> ---EXPECT-- -Exception +--EXPECTF-- +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/exceptions/exception_before_fatal.phpt b/Zend/tests/exceptions/exception_before_fatal.phpt index edc63b2394cd..541ea22212db 100644 --- a/Zend/tests/exceptions/exception_before_fatal.phpt +++ b/Zend/tests/exceptions/exception_before_fatal.phpt @@ -56,9 +56,9 @@ try { ?> --EXPECT-- string(23) "Undefined variable $foo" +string(45) "Class name must be a valid object or a string" +string(22) "Can only throw objects" string(23) "Undefined variable $foo" -string(23) "Undefined variable $foo" -string(23) "Undefined variable $foo" -string(23) "Undefined variable $foo" -string(23) "Undefined variable $foo" +string(45) "Class name must be a valid object or a string" +string(65) "clone(): Argument #1 ($object) must be of type object, null given" string(23) "Undefined variable $foo" diff --git a/Zend/tests/falsetoarray_002.phpt b/Zend/tests/falsetoarray_002.phpt index c01b79954596..639cd51b3661 100644 --- a/Zend/tests/falsetoarray_002.phpt +++ b/Zend/tests/falsetoarray_002.phpt @@ -11,5 +11,13 @@ $a[0][$d]='b'; var_dump($a); ?> --EXPECT-- +array(1) { + [0]=> + array(1) { + [""]=> + string(1) "b" + } +} Err: Automatic conversion of false to array is deprecated -string(0) "" +Err: Undefined variable $d +Err: Using null as an array offset is deprecated, use an empty string instead diff --git a/Zend/tests/falsetoarray_003.phpt b/Zend/tests/falsetoarray_003.phpt index 117e443ef958..d62dff41941c 100644 --- a/Zend/tests/falsetoarray_003.phpt +++ b/Zend/tests/falsetoarray_003.phpt @@ -11,6 +11,6 @@ $a=[]; ?> DONE --EXPECTF-- +DONE Err: The float %f is not representable as an int, cast occurred Err: Undefined array key %i -DONE diff --git a/Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_002.phpt b/Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_002.phpt index 97831a8d65f0..51c22102bddd 100644 --- a/Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_002.phpt +++ b/Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_002.phpt @@ -28,4 +28,13 @@ try { ?> --EXPECT-- +object(Closure)#2 (2) { + ["function"]=> + string(13) "Foo::myMethod" + ["parameter"]=> + array(1) { + ["$foo"]=> + string(10) "" + } +} Caught: Calling static trait method Foo::myMethod is deprecated, it should only be called on a class using the trait diff --git a/Zend/tests/get_class_basic.phpt b/Zend/tests/get_class_basic.phpt index f6dcbccb6916..d8809ba9d9cf 100644 --- a/Zend/tests/get_class_basic.phpt +++ b/Zend/tests/get_class_basic.phpt @@ -54,6 +54,7 @@ $f1->testNull(); echo "Done\n"; ?> --EXPECTF-- +string(3) "foo" Calling get_class() without arguments is deprecated Deprecated: Calling get_class() without arguments is deprecated in %s on line %d diff --git a/Zend/tests/gh16792.phpt b/Zend/tests/gh16792.phpt new file mode 100644 index 000000000000..ed2eae538d3d --- /dev/null +++ b/Zend/tests/gh16792.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-16792 (Assertion failure when an error handler mutates $GLOBALS during a by-ref sort) +--FILE-- + +--EXPECT-- +ok diff --git a/Zend/tests/gh17416.phpt b/Zend/tests/gh17416.phpt new file mode 100644 index 000000000000..1fd0e7d5ce04 --- /dev/null +++ b/Zend/tests/gh17416.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-17416 (Assertion failure when a throwing error handler runs while an array is unset during foreach) +--FILE-- + $val) { + if ($val === 2) { + unset($a[$fusion]); + } + } +} +function foo($a, bool $b): bool { + do { + $res = bar($a); + } while ($res === null && $n !== $n2); +} +foo([2, 'a' => 5], false); +?> +--EXPECTF-- +Fatal error: Uncaught Exception: Undefined variable $fusion in %s:%d +Stack trace: +#0 %s(%d): Error2Exception(2, 'Undefined varia...', '%s', %d) +#1 %s(%d): bar(Array) +#2 %s(%d): foo(Array, false) +#3 {main} + thrown in %s on line %d diff --git a/Zend/tests/gh21245.phpt b/Zend/tests/gh21245.phpt new file mode 100644 index 000000000000..c922b3865c3c --- /dev/null +++ b/Zend/tests/gh21245.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-21245 (Use-after-free in ASSIGN_DIM when the error handler grows the array holding a typed-property reference) +--FILE-- +x = ""; +$a[0] =& $test->x; +var_dump($a[0] = $v); +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: Cannot assign null to reference held by property Test::$x of type string in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/tests/gh22419.phpt b/Zend/tests/gh22419.phpt new file mode 100644 index 000000000000..f6e3241801d3 --- /dev/null +++ b/Zend/tests/gh22419.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-22419 (UAF/refcount assertion in array-offset concat when the error handler replaces the LHS) +--FILE-- + +--EXPECT-- +ok diff --git a/Zend/tests/in-de-crement/incdec_bool_exception.phpt b/Zend/tests/in-de-crement/incdec_bool_exception.phpt index f819af1e2395..d7ccf55e406f 100644 --- a/Zend/tests/in-de-crement/incdec_bool_exception.phpt +++ b/Zend/tests/in-de-crement/incdec_bool_exception.phpt @@ -7,15 +7,22 @@ set_error_handler(function($severity, $m) { throw new Exception($m, $severity); }); +function inc(&$v) { + $v++; +} +function dec(&$v) { + $v--; +} + $values = [false, true]; foreach ($values as $value) { try { - $value++; + inc($value); } catch (\Exception $e) { echo $e->getMessage(), PHP_EOL; } try { - $value--; + dec($value); } catch (\Exception $e) { echo $e->getMessage(), PHP_EOL; } diff --git a/Zend/tests/in-de-crement/incdec_strings_exception.phpt b/Zend/tests/in-de-crement/incdec_strings_exception.phpt index 5acbb1041222..b5b00e6c00d6 100644 --- a/Zend/tests/in-de-crement/incdec_strings_exception.phpt +++ b/Zend/tests/in-de-crement/incdec_strings_exception.phpt @@ -13,6 +13,13 @@ set_error_handler(function($severity, $m) { throw new Exception($m, $severity); }); +function inc(&$v) { + $v++; +} +function dec(&$v) { + $v--; +} + $values = [ '', ' ', @@ -26,47 +33,49 @@ $values = [ '🐘' ]; foreach ($values as $value) { + $v = $value; try { - $value++; + inc($v); } catch (\Exception $e) { echo $e->getMessage(), PHP_EOL; } - var_dump($value); + var_dump($v); + $v = $value; try { - $value--; + dec($v); } catch (\Exception $e) { echo $e->getMessage(), PHP_EOL; } - var_dump($value); + var_dump($v); } ?> --EXPECT-- Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead -string(0) "" +string(1) "1" Deprecated: Decrement on empty string is deprecated as non-numeric -string(0) "" +int(-1) Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead string(1) " " Deprecated: Decrement on non-numeric string has no effect and is deprecated string(1) " " Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead -string(4) "199A" +string(4) "199B" Deprecated: Decrement on non-numeric string has no effect and is deprecated string(4) "199A" Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead -string(4) "A199" +string(4) "A200" Deprecated: Decrement on non-numeric string has no effect and is deprecated string(4) "A199" Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead -string(4) "199Z" +string(4) "200A" Deprecated: Decrement on non-numeric string has no effect and is deprecated string(4) "199Z" Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead -string(4) "Z199" +string(4) "Z200" Deprecated: Decrement on non-numeric string has no effect and is deprecated string(4) "Z199" Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead -string(11) "Hello world" +string(11) "Hello worle" Deprecated: Decrement on non-numeric string has no effect and is deprecated string(11) "Hello world" Deprecated: Increment on non-numeric string is deprecated, use str_increment() instead diff --git a/Zend/tests/in-de-crement/incdec_undef.phpt b/Zend/tests/in-de-crement/incdec_undef.phpt index db560e31c99f..549c64fea87e 100644 --- a/Zend/tests/in-de-crement/incdec_undef.phpt +++ b/Zend/tests/in-de-crement/incdec_undef.phpt @@ -14,6 +14,7 @@ unset($x); var_dump(++$x); ?> --EXPECT-- +NULL Undefined variable $x Decrement on type null has no effect, this will change in the next major version of PHP NULL @@ -21,6 +22,5 @@ Undefined variable $x NULL Undefined variable $x Decrement on type null has no effect, this will change in the next major version of PHP -NULL -Undefined variable $x int(1) +Undefined variable $x diff --git a/Zend/tests/in-de-crement/increment_diagnostic_change_type.phpt b/Zend/tests/in-de-crement/increment_diagnostic_change_type.phpt index 11a0edac1917..b55c8c6b811e 100644 --- a/Zend/tests/in-de-crement/increment_diagnostic_change_type.phpt +++ b/Zend/tests/in-de-crement/increment_diagnostic_change_type.phpt @@ -31,8 +31,8 @@ var_dump($x); DONE --EXPECT-- string(1) "1" -string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(4) "foo!" string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(1) "!" +string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" DONE diff --git a/Zend/tests/in-de-crement/increment_diagnostic_change_type_do_operator.phpt b/Zend/tests/in-de-crement/increment_diagnostic_change_type_do_operator.phpt index 8e63ad6aeed4..9308a1a7bd3d 100644 --- a/Zend/tests/in-de-crement/increment_diagnostic_change_type_do_operator.phpt +++ b/Zend/tests/in-de-crement/increment_diagnostic_change_type_do_operator.phpt @@ -23,8 +23,8 @@ var_dump($x); ?> DONE --EXPECT-- -string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(4) "foo!" string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(1) "!" +string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" DONE diff --git a/Zend/tests/in-de-crement/oss-fuzz-60709_globals_unset_after_undef_warning.phpt b/Zend/tests/in-de-crement/oss-fuzz-60709_globals_unset_after_undef_warning.phpt index e981800eeac1..1f1fc7228134 100644 --- a/Zend/tests/in-de-crement/oss-fuzz-60709_globals_unset_after_undef_warning.phpt +++ b/Zend/tests/in-de-crement/oss-fuzz-60709_globals_unset_after_undef_warning.phpt @@ -21,16 +21,16 @@ var_dump(++$x); ?> --EXPECT-- POST DEC +NULL Undefined variable $x Decrement on type null has no effect, this will change in the next major version of PHP -NULL POST INC -Undefined variable $x NULL +Undefined variable $x PRE DEC +NULL Undefined variable $x Decrement on type null has no effect, this will change in the next major version of PHP -NULL PRE INC -Undefined variable $x int(1) +Undefined variable $x diff --git a/Zend/tests/in-de-crement/oss-fuzz-62294_globals_unset_after_string_warning.phpt b/Zend/tests/in-de-crement/oss-fuzz-62294_globals_unset_after_string_warning.phpt index f52dff52e631..e39184e68154 100644 --- a/Zend/tests/in-de-crement/oss-fuzz-62294_globals_unset_after_string_warning.phpt +++ b/Zend/tests/in-de-crement/oss-fuzz-62294_globals_unset_after_string_warning.phpt @@ -25,14 +25,14 @@ var_dump(++$x); ?> --EXPECT-- POST DEC -Decrement on non-numeric string has no effect and is deprecated string(1) " " -PRE DEC Decrement on non-numeric string has no effect and is deprecated +PRE DEC string(1) " " +Decrement on non-numeric string has no effect and is deprecated POST INC -Increment on non-numeric string is deprecated, use str_increment() instead string(1) " " -PRE INC Increment on non-numeric string is deprecated, use str_increment() instead +PRE INC string(1) " " +Increment on non-numeric string is deprecated, use str_increment() instead diff --git a/Zend/tests/in-de-crement/unset_globals_in_error_handler.phpt b/Zend/tests/in-de-crement/unset_globals_in_error_handler.phpt index 31ffea22467b..88302517ce05 100644 --- a/Zend/tests/in-de-crement/unset_globals_in_error_handler.phpt +++ b/Zend/tests/in-de-crement/unset_globals_in_error_handler.phpt @@ -78,53 +78,53 @@ unset($x); --EXPECT-- NULL (only --) POST DEC -Decrement on type null has no effect, this will change in the next major version of PHP NULL -PRE DEC Decrement on type null has no effect, this will change in the next major version of PHP +PRE DEC NULL +Decrement on type null has no effect, this will change in the next major version of PHP Empty string POST INC -Increment on non-numeric string is deprecated, use str_increment() instead string(0) "" +Increment on non-numeric string is deprecated, use str_increment() instead POST DEC -Decrement on empty string is deprecated as non-numeric string(0) "" +Decrement on empty string is deprecated as non-numeric PRE INC -Increment on non-numeric string is deprecated, use str_increment() instead string(1) "1" +Increment on non-numeric string is deprecated, use str_increment() instead PRE DEC -Decrement on empty string is deprecated as non-numeric int(-1) +Decrement on empty string is deprecated as non-numeric Non fill ASCII (only ++) POST INC -Increment on non-numeric string is deprecated, use str_increment() instead string(4) " ad " -PRE INC Increment on non-numeric string is deprecated, use str_increment() instead +PRE INC string(4) " ad " +Increment on non-numeric string is deprecated, use str_increment() instead Bool POST INC -Increment on type bool has no effect, this will change in the next major version of PHP bool(false) +Increment on type bool has no effect, this will change in the next major version of PHP POST DEC -Decrement on type bool has no effect, this will change in the next major version of PHP bool(false) +Decrement on type bool has no effect, this will change in the next major version of PHP PRE INC -Increment on type bool has no effect, this will change in the next major version of PHP bool(false) +Increment on type bool has no effect, this will change in the next major version of PHP PRE DEC -Decrement on type bool has no effect, this will change in the next major version of PHP bool(false) +Decrement on type bool has no effect, this will change in the next major version of PHP POST INC -Increment on type bool has no effect, this will change in the next major version of PHP bool(true) +Increment on type bool has no effect, this will change in the next major version of PHP POST DEC -Decrement on type bool has no effect, this will change in the next major version of PHP bool(true) +Decrement on type bool has no effect, this will change in the next major version of PHP PRE INC -Increment on type bool has no effect, this will change in the next major version of PHP bool(true) +Increment on type bool has no effect, this will change in the next major version of PHP PRE DEC -Decrement on type bool has no effect, this will change in the next major version of PHP bool(true) +Decrement on type bool has no effect, this will change in the next major version of PHP diff --git a/Zend/tests/in-de-crement/unset_object_property_in_error_handler.phpt b/Zend/tests/in-de-crement/unset_object_property_in_error_handler.phpt index 4d2a4705588a..b00ba707cecf 100644 --- a/Zend/tests/in-de-crement/unset_object_property_in_error_handler.phpt +++ b/Zend/tests/in-de-crement/unset_object_property_in_error_handler.phpt @@ -87,57 +87,57 @@ var_dump(--$c->a); unset($c->a); ?> --EXPECT-- -string(87) "Decrement on type null has no effect, this will change in the next major version of PHP" NULL +string(87) "Decrement on type null has no effect, this will change in the next major version of PHP" NULL (only --) POST DEC -string(87) "Decrement on type null has no effect, this will change in the next major version of PHP" NULL -PRE DEC string(87) "Decrement on type null has no effect, this will change in the next major version of PHP" +PRE DEC NULL +string(87) "Decrement on type null has no effect, this will change in the next major version of PHP" Empty string POST INC -string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(0) "" +string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" POST DEC -string(54) "Decrement on empty string is deprecated as non-numeric" string(0) "" +string(54) "Decrement on empty string is deprecated as non-numeric" PRE INC -string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(1) "1" +string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" PRE DEC -string(54) "Decrement on empty string is deprecated as non-numeric" int(-1) +string(54) "Decrement on empty string is deprecated as non-numeric" Non fill ASCII (only ++) POST INC -string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" string(4) " ad " -PRE INC string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" +PRE INC string(4) " ad " +string(74) "Increment on non-numeric string is deprecated, use str_increment() instead" Bool POST INC -string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" bool(false) +string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" POST DEC -string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" bool(false) +string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" PRE INC -string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" bool(false) +string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" PRE DEC -string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" bool(false) +string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" POST INC -string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" bool(true) +string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" POST DEC -string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" bool(true) +string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" PRE INC -string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" bool(true) +string(87) "Increment on type bool has no effect, this will change in the next major version of PHP" PRE DEC -string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" bool(true) +string(87) "Decrement on type bool has no effect, this will change in the next major version of PHP" diff --git a/Zend/tests/in-de-crement/unset_property_converted_to_obj_in_error_handler.phpt b/Zend/tests/in-de-crement/unset_property_converted_to_obj_in_error_handler.phpt index d435c9fb4029..55d3283ead91 100644 --- a/Zend/tests/in-de-crement/unset_property_converted_to_obj_in_error_handler.phpt +++ b/Zend/tests/in-de-crement/unset_property_converted_to_obj_in_error_handler.phpt @@ -20,6 +20,5 @@ try { var_dump($c->a); ?> --EXPECT-- -Cannot increment stdClass object(stdClass)#2 (0) { } diff --git a/Zend/tests/offsets/array_offset_002.phpt b/Zend/tests/offsets/array_offset_002.phpt index 4d56258ef8b8..1511eb5dc73f 100644 --- a/Zend/tests/offsets/array_offset_002.phpt +++ b/Zend/tests/offsets/array_offset_002.phpt @@ -14,5 +14,7 @@ var_dump($y); ?> --EXPECT-- Err: The float 1.0E+20 is not representable as an int, cast occurred -array(0) { +array(1) { + [7766279631452241920]=> + int(1) } diff --git a/Zend/tests/offsets/null_offset_dep_promoted.phpt b/Zend/tests/offsets/null_offset_dep_promoted.phpt index c26095d4f745..55145c88837a 100644 --- a/Zend/tests/offsets/null_offset_dep_promoted.phpt +++ b/Zend/tests/offsets/null_offset_dep_promoted.phpt @@ -13,5 +13,9 @@ try { echo $e::class, ': ', $e->getMessage(), PHP_EOL; } ?> ---EXPECT-- -Exception: Using null as an array offset is deprecated, use an empty string instead +--EXPECTF-- +Fatal error: Uncaught Exception: Using null as an array offset is deprecated, use an empty string instead in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(%d, 'Using null as a...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/offsets/null_offset_no_uaf.phpt b/Zend/tests/offsets/null_offset_no_uaf.phpt index 38a1b9868345..3352438688fc 100644 --- a/Zend/tests/offsets/null_offset_no_uaf.phpt +++ b/Zend/tests/offsets/null_offset_no_uaf.phpt @@ -13,5 +13,5 @@ $ary[null] = 1; echo "\nSuccess\n"; ?> --EXPECTF-- -Using null as an array offset is deprecated, use an empty string instead Success +Using null as an array offset is deprecated, use an empty string instead diff --git a/Zend/tests/offsets/null_offset_unset_via_error_handler.phpt b/Zend/tests/offsets/null_offset_unset_via_error_handler.phpt index cfaa2b3b20d9..1dd363bab30b 100644 --- a/Zend/tests/offsets/null_offset_unset_via_error_handler.phpt +++ b/Zend/tests/offsets/null_offset_unset_via_error_handler.phpt @@ -14,6 +14,5 @@ $b = [0, null => $a]; echo "\nSuccess\n"; ?> --EXPECTF-- -string(72) "Using null as an array offset is deprecated, use an empty string instead" - Success +string(72) "Using null as an array offset is deprecated, use an empty string instead" diff --git a/Zend/tests/operator_unsupported_types.phpt b/Zend/tests/operator_unsupported_types.phpt index 904ac402b372..0bc7ed2a78fe 100644 --- a/Zend/tests/operator_unsupported_types.phpt +++ b/Zend/tests/operator_unsupported_types.phpt @@ -155,7 +155,6 @@ Unsupported operand types: float + array Unsupported operand types: array + string Unsupported operand types: string + array Unsupported operand types: array + string -Warning: A non-numeric value encountered Unsupported operand types: string + array Unsupported operand types: stdClass + null Unsupported operand types: null + stdClass @@ -170,7 +169,6 @@ Unsupported operand types: float + stdClass Unsupported operand types: stdClass + string Unsupported operand types: string + stdClass Unsupported operand types: stdClass + string -Warning: A non-numeric value encountered Unsupported operand types: string + stdClass Unsupported operand types: resource + null Unsupported operand types: null + resource @@ -185,7 +183,6 @@ Unsupported operand types: float + resource Unsupported operand types: resource + string Unsupported operand types: string + resource Unsupported operand types: resource + string -Warning: A non-numeric value encountered Unsupported operand types: string + resource Unsupported operand types: string + null Unsupported operand types: null + string @@ -200,7 +197,6 @@ Unsupported operand types: float + string Unsupported operand types: string + string Unsupported operand types: string + string Unsupported operand types: string + string -Warning: A non-numeric value encountered Unsupported operand types: string + string Unsupported operand types: array - array Unsupported operand types: array - stdClass @@ -231,7 +227,6 @@ Unsupported operand types: float - array Unsupported operand types: array - string Unsupported operand types: string - array Unsupported operand types: array - string -Warning: A non-numeric value encountered Unsupported operand types: string - array Unsupported operand types: stdClass - null Unsupported operand types: null - stdClass @@ -246,7 +241,6 @@ Unsupported operand types: float - stdClass Unsupported operand types: stdClass - string Unsupported operand types: string - stdClass Unsupported operand types: stdClass - string -Warning: A non-numeric value encountered Unsupported operand types: string - stdClass Unsupported operand types: resource - null Unsupported operand types: null - resource @@ -261,7 +255,6 @@ Unsupported operand types: float - resource Unsupported operand types: resource - string Unsupported operand types: string - resource Unsupported operand types: resource - string -Warning: A non-numeric value encountered Unsupported operand types: string - resource Unsupported operand types: string - null Unsupported operand types: null - string @@ -276,7 +269,6 @@ Unsupported operand types: float - string Unsupported operand types: string - string Unsupported operand types: string - string Unsupported operand types: string - string -Warning: A non-numeric value encountered Unsupported operand types: string - string Unsupported operand types: array * array Unsupported operand types: stdClass * array @@ -307,7 +299,6 @@ Unsupported operand types: float * array Unsupported operand types: array * string Unsupported operand types: string * array Unsupported operand types: array * string -Warning: A non-numeric value encountered Unsupported operand types: string * array Unsupported operand types: stdClass * null Unsupported operand types: stdClass * null @@ -350,7 +341,6 @@ Unsupported operand types: float * string Unsupported operand types: string * string Unsupported operand types: string * string Unsupported operand types: string * string -Warning: A non-numeric value encountered Unsupported operand types: string * string Unsupported operand types: array / array Unsupported operand types: array / stdClass @@ -381,7 +371,6 @@ Unsupported operand types: float / array Unsupported operand types: array / string Unsupported operand types: string / array Unsupported operand types: array / string -Warning: A non-numeric value encountered Unsupported operand types: string / array Unsupported operand types: stdClass / null Unsupported operand types: null / stdClass @@ -396,7 +385,6 @@ Unsupported operand types: float / stdClass Unsupported operand types: stdClass / string Unsupported operand types: string / stdClass Unsupported operand types: stdClass / string -Warning: A non-numeric value encountered Unsupported operand types: string / stdClass Unsupported operand types: resource / null Unsupported operand types: null / resource @@ -411,7 +399,6 @@ Unsupported operand types: float / resource Unsupported operand types: resource / string Unsupported operand types: string / resource Unsupported operand types: resource / string -Warning: A non-numeric value encountered Unsupported operand types: string / resource Unsupported operand types: string / null Unsupported operand types: null / string @@ -426,7 +413,6 @@ Unsupported operand types: float / string Unsupported operand types: string / string Unsupported operand types: string / string Unsupported operand types: string / string -Warning: A non-numeric value encountered Unsupported operand types: string / string Unsupported operand types: array % array Unsupported operand types: array % stdClass @@ -453,12 +439,10 @@ Unsupported operand types: bool % array Unsupported operand types: array % int Unsupported operand types: int % array Unsupported operand types: array % float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float % array Unsupported operand types: array % string Unsupported operand types: string % array Unsupported operand types: array % string -Warning: A non-numeric value encountered Unsupported operand types: string % array Unsupported operand types: stdClass % null Unsupported operand types: null % stdClass @@ -469,12 +453,10 @@ Unsupported operand types: bool % stdClass Unsupported operand types: stdClass % int Unsupported operand types: int % stdClass Unsupported operand types: stdClass % float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float % stdClass Unsupported operand types: stdClass % string Unsupported operand types: string % stdClass Unsupported operand types: stdClass % string -Warning: A non-numeric value encountered Unsupported operand types: string % stdClass Unsupported operand types: resource % null Unsupported operand types: null % resource @@ -485,12 +467,10 @@ Unsupported operand types: bool % resource Unsupported operand types: resource % int Unsupported operand types: int % resource Unsupported operand types: resource % float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float % resource Unsupported operand types: resource % string Unsupported operand types: string % resource Unsupported operand types: resource % string -Warning: A non-numeric value encountered Unsupported operand types: string % resource Unsupported operand types: string % null Unsupported operand types: null % string @@ -501,12 +481,10 @@ Unsupported operand types: bool % string Unsupported operand types: string % int Unsupported operand types: int % string Unsupported operand types: string % float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float % string Unsupported operand types: string % string Unsupported operand types: string % string Unsupported operand types: string % string -Warning: A non-numeric value encountered Unsupported operand types: string % string Unsupported operand types: array ** array Unsupported operand types: array ** stdClass @@ -537,7 +515,6 @@ Unsupported operand types: float ** array Unsupported operand types: array ** string Unsupported operand types: string ** array Unsupported operand types: array ** string -Warning: A non-numeric value encountered Unsupported operand types: string ** array Unsupported operand types: stdClass ** null Unsupported operand types: null ** stdClass @@ -552,7 +529,6 @@ Unsupported operand types: float ** stdClass Unsupported operand types: stdClass ** string Unsupported operand types: string ** stdClass Unsupported operand types: stdClass ** string -Warning: A non-numeric value encountered Unsupported operand types: string ** stdClass Unsupported operand types: resource ** null Unsupported operand types: null ** resource @@ -567,7 +543,6 @@ Unsupported operand types: float ** resource Unsupported operand types: resource ** string Unsupported operand types: string ** resource Unsupported operand types: resource ** string -Warning: A non-numeric value encountered Unsupported operand types: string ** resource Unsupported operand types: string ** null Unsupported operand types: null ** string @@ -582,7 +557,6 @@ Unsupported operand types: float ** string Unsupported operand types: string ** string Unsupported operand types: string ** string Unsupported operand types: string ** string -Warning: A non-numeric value encountered Unsupported operand types: string ** string Unsupported operand types: array << array Unsupported operand types: array << stdClass @@ -609,12 +583,10 @@ Unsupported operand types: bool << array Unsupported operand types: array << int Unsupported operand types: int << array Unsupported operand types: array << float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float << array Unsupported operand types: array << string Unsupported operand types: string << array Unsupported operand types: array << string -Warning: A non-numeric value encountered Unsupported operand types: string << array Unsupported operand types: stdClass << null Unsupported operand types: null << stdClass @@ -625,12 +597,10 @@ Unsupported operand types: bool << stdClass Unsupported operand types: stdClass << int Unsupported operand types: int << stdClass Unsupported operand types: stdClass << float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float << stdClass Unsupported operand types: stdClass << string Unsupported operand types: string << stdClass Unsupported operand types: stdClass << string -Warning: A non-numeric value encountered Unsupported operand types: string << stdClass Unsupported operand types: resource << null Unsupported operand types: null << resource @@ -641,12 +611,10 @@ Unsupported operand types: bool << resource Unsupported operand types: resource << int Unsupported operand types: int << resource Unsupported operand types: resource << float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float << resource Unsupported operand types: resource << string Unsupported operand types: string << resource Unsupported operand types: resource << string -Warning: A non-numeric value encountered Unsupported operand types: string << resource Unsupported operand types: string << null Unsupported operand types: null << string @@ -657,12 +625,10 @@ Unsupported operand types: bool << string Unsupported operand types: string << int Unsupported operand types: int << string Unsupported operand types: string << float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float << string Unsupported operand types: string << string Unsupported operand types: string << string Unsupported operand types: string << string -Warning: A non-numeric value encountered Unsupported operand types: string << string Unsupported operand types: array >> array Unsupported operand types: array >> stdClass @@ -689,12 +655,10 @@ Unsupported operand types: bool >> array Unsupported operand types: array >> int Unsupported operand types: int >> array Unsupported operand types: array >> float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float >> array Unsupported operand types: array >> string Unsupported operand types: string >> array Unsupported operand types: array >> string -Warning: A non-numeric value encountered Unsupported operand types: string >> array Unsupported operand types: stdClass >> null Unsupported operand types: null >> stdClass @@ -705,12 +669,10 @@ Unsupported operand types: bool >> stdClass Unsupported operand types: stdClass >> int Unsupported operand types: int >> stdClass Unsupported operand types: stdClass >> float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float >> stdClass Unsupported operand types: stdClass >> string Unsupported operand types: string >> stdClass Unsupported operand types: stdClass >> string -Warning: A non-numeric value encountered Unsupported operand types: string >> stdClass Unsupported operand types: resource >> null Unsupported operand types: null >> resource @@ -721,12 +683,10 @@ Unsupported operand types: bool >> resource Unsupported operand types: resource >> int Unsupported operand types: int >> resource Unsupported operand types: resource >> float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float >> resource Unsupported operand types: resource >> string Unsupported operand types: string >> resource Unsupported operand types: resource >> string -Warning: A non-numeric value encountered Unsupported operand types: string >> resource Unsupported operand types: string >> null Unsupported operand types: null >> string @@ -737,12 +697,10 @@ Unsupported operand types: bool >> string Unsupported operand types: string >> int Unsupported operand types: int >> string Unsupported operand types: string >> float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float >> string Unsupported operand types: string >> string Unsupported operand types: string >> string Unsupported operand types: string >> string -Warning: A non-numeric value encountered Unsupported operand types: string >> string Unsupported operand types: array & array Unsupported operand types: stdClass & array @@ -769,12 +727,10 @@ Unsupported operand types: bool & array Unsupported operand types: array & int Unsupported operand types: int & array Unsupported operand types: array & float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float & array Unsupported operand types: array & string Unsupported operand types: string & array Unsupported operand types: array & string -Warning: A non-numeric value encountered Unsupported operand types: string & array Unsupported operand types: stdClass & null Unsupported operand types: stdClass & null @@ -813,7 +769,6 @@ Unsupported operand types: bool & string Unsupported operand types: string & int Unsupported operand types: int & string Unsupported operand types: string & float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float & string No error for "foo" & "123" No error for "123" & "foo" @@ -844,12 +799,10 @@ Unsupported operand types: bool | array Unsupported operand types: array | int Unsupported operand types: int | array Unsupported operand types: array | float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float | array Unsupported operand types: array | string Unsupported operand types: string | array Unsupported operand types: array | string -Warning: A non-numeric value encountered Unsupported operand types: string | array Unsupported operand types: stdClass | null Unsupported operand types: stdClass | null @@ -888,7 +841,6 @@ Unsupported operand types: bool | string Unsupported operand types: string | int Unsupported operand types: int | string Unsupported operand types: string | float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float | string No error for "foo" | "123" No error for "123" | "foo" @@ -919,12 +871,10 @@ Unsupported operand types: bool ^ array Unsupported operand types: array ^ int Unsupported operand types: int ^ array Unsupported operand types: array ^ float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float ^ array Unsupported operand types: array ^ string Unsupported operand types: string ^ array Unsupported operand types: array ^ string -Warning: A non-numeric value encountered Unsupported operand types: string ^ array Unsupported operand types: stdClass ^ null Unsupported operand types: stdClass ^ null @@ -963,7 +913,6 @@ Unsupported operand types: bool ^ string Unsupported operand types: string ^ int Unsupported operand types: int ^ string Unsupported operand types: string ^ float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float ^ string No error for "foo" ^ "123" No error for "123" ^ "foo" @@ -1044,13 +993,11 @@ No error for "123foo" xor "foo" Warning: Array to string conversion Warning: Array to string conversion No error for [] . [] -Warning: Array to string conversion Object of class stdClass could not be converted to string Warning: Array to string conversion No error for [] . STDOUT Warning: Array to string conversion No error for [] . "foo" -Warning: Array to string conversion Object of class stdClass could not be converted to string Object of class stdClass could not be converted to string Object of class stdClass could not be converted to string @@ -1167,7 +1114,6 @@ Unsupported operand types: float + array Unsupported operand types: array + string Unsupported operand types: string + array Unsupported operand types: array + string -Warning: A non-numeric value encountered Unsupported operand types: string + array Unsupported operand types: stdClass + null Unsupported operand types: null + stdClass @@ -1182,7 +1128,6 @@ Unsupported operand types: float + stdClass Unsupported operand types: stdClass + string Unsupported operand types: string + stdClass Unsupported operand types: stdClass + string -Warning: A non-numeric value encountered Unsupported operand types: string + stdClass Unsupported operand types: resource + null Unsupported operand types: null + resource @@ -1197,7 +1142,6 @@ Unsupported operand types: float + resource Unsupported operand types: resource + string Unsupported operand types: string + resource Unsupported operand types: resource + string -Warning: A non-numeric value encountered Unsupported operand types: string + resource Unsupported operand types: string + null Unsupported operand types: null + string @@ -1212,7 +1156,6 @@ Unsupported operand types: float + string Unsupported operand types: string + string Unsupported operand types: string + string Unsupported operand types: string + string -Warning: A non-numeric value encountered Unsupported operand types: string + string Unsupported operand types: array - array Unsupported operand types: array - stdClass @@ -1243,7 +1186,6 @@ Unsupported operand types: float - array Unsupported operand types: array - string Unsupported operand types: string - array Unsupported operand types: array - string -Warning: A non-numeric value encountered Unsupported operand types: string - array Unsupported operand types: stdClass - null Unsupported operand types: null - stdClass @@ -1258,7 +1200,6 @@ Unsupported operand types: float - stdClass Unsupported operand types: stdClass - string Unsupported operand types: string - stdClass Unsupported operand types: stdClass - string -Warning: A non-numeric value encountered Unsupported operand types: string - stdClass Unsupported operand types: resource - null Unsupported operand types: null - resource @@ -1273,7 +1214,6 @@ Unsupported operand types: float - resource Unsupported operand types: resource - string Unsupported operand types: string - resource Unsupported operand types: resource - string -Warning: A non-numeric value encountered Unsupported operand types: string - resource Unsupported operand types: string - null Unsupported operand types: null - string @@ -1288,7 +1228,6 @@ Unsupported operand types: float - string Unsupported operand types: string - string Unsupported operand types: string - string Unsupported operand types: string - string -Warning: A non-numeric value encountered Unsupported operand types: string - string Unsupported operand types: array * array Unsupported operand types: array * stdClass @@ -1319,7 +1258,6 @@ Unsupported operand types: float * array Unsupported operand types: array * string Unsupported operand types: string * array Unsupported operand types: array * string -Warning: A non-numeric value encountered Unsupported operand types: string * array Unsupported operand types: stdClass * null Unsupported operand types: null * stdClass @@ -1334,7 +1272,6 @@ Unsupported operand types: float * stdClass Unsupported operand types: stdClass * string Unsupported operand types: string * stdClass Unsupported operand types: stdClass * string -Warning: A non-numeric value encountered Unsupported operand types: string * stdClass Unsupported operand types: resource * null Unsupported operand types: null * resource @@ -1349,7 +1286,6 @@ Unsupported operand types: float * resource Unsupported operand types: resource * string Unsupported operand types: string * resource Unsupported operand types: resource * string -Warning: A non-numeric value encountered Unsupported operand types: string * resource Unsupported operand types: string * null Unsupported operand types: null * string @@ -1364,7 +1300,6 @@ Unsupported operand types: float * string Unsupported operand types: string * string Unsupported operand types: string * string Unsupported operand types: string * string -Warning: A non-numeric value encountered Unsupported operand types: string * string Unsupported operand types: array / array Unsupported operand types: array / stdClass @@ -1395,7 +1330,6 @@ Unsupported operand types: float / array Unsupported operand types: array / string Unsupported operand types: string / array Unsupported operand types: array / string -Warning: A non-numeric value encountered Unsupported operand types: string / array Unsupported operand types: stdClass / null Unsupported operand types: null / stdClass @@ -1410,7 +1344,6 @@ Unsupported operand types: float / stdClass Unsupported operand types: stdClass / string Unsupported operand types: string / stdClass Unsupported operand types: stdClass / string -Warning: A non-numeric value encountered Unsupported operand types: string / stdClass Unsupported operand types: resource / null Unsupported operand types: null / resource @@ -1425,7 +1358,6 @@ Unsupported operand types: float / resource Unsupported operand types: resource / string Unsupported operand types: string / resource Unsupported operand types: resource / string -Warning: A non-numeric value encountered Unsupported operand types: string / resource Unsupported operand types: string / null Unsupported operand types: null / string @@ -1440,7 +1372,6 @@ Unsupported operand types: float / string Unsupported operand types: string / string Unsupported operand types: string / string Unsupported operand types: string / string -Warning: A non-numeric value encountered Unsupported operand types: string / string Unsupported operand types: array % array Unsupported operand types: array % stdClass @@ -1467,12 +1398,10 @@ Unsupported operand types: bool % array Unsupported operand types: array % int Unsupported operand types: int % array Unsupported operand types: array % float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float % array Unsupported operand types: array % string Unsupported operand types: string % array Unsupported operand types: array % string -Warning: A non-numeric value encountered Unsupported operand types: string % array Unsupported operand types: stdClass % null Unsupported operand types: null % stdClass @@ -1483,12 +1412,10 @@ Unsupported operand types: bool % stdClass Unsupported operand types: stdClass % int Unsupported operand types: int % stdClass Unsupported operand types: stdClass % float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float % stdClass Unsupported operand types: stdClass % string Unsupported operand types: string % stdClass Unsupported operand types: stdClass % string -Warning: A non-numeric value encountered Unsupported operand types: string % stdClass Unsupported operand types: resource % null Unsupported operand types: null % resource @@ -1499,12 +1426,10 @@ Unsupported operand types: bool % resource Unsupported operand types: resource % int Unsupported operand types: int % resource Unsupported operand types: resource % float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float % resource Unsupported operand types: resource % string Unsupported operand types: string % resource Unsupported operand types: resource % string -Warning: A non-numeric value encountered Unsupported operand types: string % resource Unsupported operand types: string % null Unsupported operand types: null % string @@ -1515,12 +1440,10 @@ Unsupported operand types: bool % string Unsupported operand types: string % int Unsupported operand types: int % string Unsupported operand types: string % float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float % string Unsupported operand types: string % string Unsupported operand types: string % string Unsupported operand types: string % string -Warning: A non-numeric value encountered Unsupported operand types: string % string Unsupported operand types: array ** array Unsupported operand types: array ** stdClass @@ -1551,7 +1474,6 @@ Unsupported operand types: float ** array Unsupported operand types: array ** string Unsupported operand types: string ** array Unsupported operand types: array ** string -Warning: A non-numeric value encountered Unsupported operand types: string ** array Unsupported operand types: stdClass ** null Unsupported operand types: null ** stdClass @@ -1566,7 +1488,6 @@ Unsupported operand types: float ** stdClass Unsupported operand types: stdClass ** string Unsupported operand types: string ** stdClass Unsupported operand types: stdClass ** string -Warning: A non-numeric value encountered Unsupported operand types: string ** stdClass Unsupported operand types: resource ** null Unsupported operand types: null ** resource @@ -1581,7 +1502,6 @@ Unsupported operand types: float ** resource Unsupported operand types: resource ** string Unsupported operand types: string ** resource Unsupported operand types: resource ** string -Warning: A non-numeric value encountered Unsupported operand types: string ** resource Unsupported operand types: string ** null Unsupported operand types: null ** string @@ -1596,7 +1516,6 @@ Unsupported operand types: float ** string Unsupported operand types: string ** string Unsupported operand types: string ** string Unsupported operand types: string ** string -Warning: A non-numeric value encountered Unsupported operand types: string ** string Unsupported operand types: array << array Unsupported operand types: array << stdClass @@ -1623,12 +1542,10 @@ Unsupported operand types: bool << array Unsupported operand types: array << int Unsupported operand types: int << array Unsupported operand types: array << float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float << array Unsupported operand types: array << string Unsupported operand types: string << array Unsupported operand types: array << string -Warning: A non-numeric value encountered Unsupported operand types: string << array Unsupported operand types: stdClass << null Unsupported operand types: null << stdClass @@ -1639,12 +1556,10 @@ Unsupported operand types: bool << stdClass Unsupported operand types: stdClass << int Unsupported operand types: int << stdClass Unsupported operand types: stdClass << float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float << stdClass Unsupported operand types: stdClass << string Unsupported operand types: string << stdClass Unsupported operand types: stdClass << string -Warning: A non-numeric value encountered Unsupported operand types: string << stdClass Unsupported operand types: resource << null Unsupported operand types: null << resource @@ -1655,12 +1570,10 @@ Unsupported operand types: bool << resource Unsupported operand types: resource << int Unsupported operand types: int << resource Unsupported operand types: resource << float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float << resource Unsupported operand types: resource << string Unsupported operand types: string << resource Unsupported operand types: resource << string -Warning: A non-numeric value encountered Unsupported operand types: string << resource Unsupported operand types: string << null Unsupported operand types: null << string @@ -1671,12 +1584,10 @@ Unsupported operand types: bool << string Unsupported operand types: string << int Unsupported operand types: int << string Unsupported operand types: string << float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float << string Unsupported operand types: string << string Unsupported operand types: string << string Unsupported operand types: string << string -Warning: A non-numeric value encountered Unsupported operand types: string << string Unsupported operand types: array >> array Unsupported operand types: array >> stdClass @@ -1703,12 +1614,10 @@ Unsupported operand types: bool >> array Unsupported operand types: array >> int Unsupported operand types: int >> array Unsupported operand types: array >> float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float >> array Unsupported operand types: array >> string Unsupported operand types: string >> array Unsupported operand types: array >> string -Warning: A non-numeric value encountered Unsupported operand types: string >> array Unsupported operand types: stdClass >> null Unsupported operand types: null >> stdClass @@ -1719,12 +1628,10 @@ Unsupported operand types: bool >> stdClass Unsupported operand types: stdClass >> int Unsupported operand types: int >> stdClass Unsupported operand types: stdClass >> float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float >> stdClass Unsupported operand types: stdClass >> string Unsupported operand types: string >> stdClass Unsupported operand types: stdClass >> string -Warning: A non-numeric value encountered Unsupported operand types: string >> stdClass Unsupported operand types: resource >> null Unsupported operand types: null >> resource @@ -1735,12 +1642,10 @@ Unsupported operand types: bool >> resource Unsupported operand types: resource >> int Unsupported operand types: int >> resource Unsupported operand types: resource >> float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float >> resource Unsupported operand types: resource >> string Unsupported operand types: string >> resource Unsupported operand types: resource >> string -Warning: A non-numeric value encountered Unsupported operand types: string >> resource Unsupported operand types: string >> null Unsupported operand types: null >> string @@ -1751,12 +1656,10 @@ Unsupported operand types: bool >> string Unsupported operand types: string >> int Unsupported operand types: int >> string Unsupported operand types: string >> float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float >> string Unsupported operand types: string >> string Unsupported operand types: string >> string Unsupported operand types: string >> string -Warning: A non-numeric value encountered Unsupported operand types: string >> string Unsupported operand types: array & array Unsupported operand types: array & stdClass @@ -1783,12 +1686,10 @@ Unsupported operand types: bool & array Unsupported operand types: array & int Unsupported operand types: int & array Unsupported operand types: array & float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float & array Unsupported operand types: array & string Unsupported operand types: string & array Unsupported operand types: array & string -Warning: A non-numeric value encountered Unsupported operand types: string & array Unsupported operand types: stdClass & null Unsupported operand types: null & stdClass @@ -1799,12 +1700,10 @@ Unsupported operand types: bool & stdClass Unsupported operand types: stdClass & int Unsupported operand types: int & stdClass Unsupported operand types: stdClass & float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float & stdClass Unsupported operand types: stdClass & string Unsupported operand types: string & stdClass Unsupported operand types: stdClass & string -Warning: A non-numeric value encountered Unsupported operand types: string & stdClass Unsupported operand types: resource & null Unsupported operand types: null & resource @@ -1815,12 +1714,10 @@ Unsupported operand types: bool & resource Unsupported operand types: resource & int Unsupported operand types: int & resource Unsupported operand types: resource & float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float & resource Unsupported operand types: resource & string Unsupported operand types: string & resource Unsupported operand types: resource & string -Warning: A non-numeric value encountered Unsupported operand types: string & resource Unsupported operand types: string & null Unsupported operand types: null & string @@ -1831,7 +1728,6 @@ Unsupported operand types: bool & string Unsupported operand types: string & int Unsupported operand types: int & string Unsupported operand types: string & float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float & string No error for "foo" &= "123" No error for "123" &= "foo" @@ -1862,12 +1758,10 @@ Unsupported operand types: bool | array Unsupported operand types: array | int Unsupported operand types: int | array Unsupported operand types: array | float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float | array Unsupported operand types: array | string Unsupported operand types: string | array Unsupported operand types: array | string -Warning: A non-numeric value encountered Unsupported operand types: string | array Unsupported operand types: stdClass | null Unsupported operand types: null | stdClass @@ -1878,12 +1772,10 @@ Unsupported operand types: bool | stdClass Unsupported operand types: stdClass | int Unsupported operand types: int | stdClass Unsupported operand types: stdClass | float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float | stdClass Unsupported operand types: stdClass | string Unsupported operand types: string | stdClass Unsupported operand types: stdClass | string -Warning: A non-numeric value encountered Unsupported operand types: string | stdClass Unsupported operand types: resource | null Unsupported operand types: null | resource @@ -1894,12 +1786,10 @@ Unsupported operand types: bool | resource Unsupported operand types: resource | int Unsupported operand types: int | resource Unsupported operand types: resource | float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float | resource Unsupported operand types: resource | string Unsupported operand types: string | resource Unsupported operand types: resource | string -Warning: A non-numeric value encountered Unsupported operand types: string | resource Unsupported operand types: string | null Unsupported operand types: null | string @@ -1910,7 +1800,6 @@ Unsupported operand types: bool | string Unsupported operand types: string | int Unsupported operand types: int | string Unsupported operand types: string | float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float | string No error for "foo" |= "123" No error for "123" |= "foo" @@ -1941,12 +1830,10 @@ Unsupported operand types: bool ^ array Unsupported operand types: array ^ int Unsupported operand types: int ^ array Unsupported operand types: array ^ float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float ^ array Unsupported operand types: array ^ string Unsupported operand types: string ^ array Unsupported operand types: array ^ string -Warning: A non-numeric value encountered Unsupported operand types: string ^ array Unsupported operand types: stdClass ^ null Unsupported operand types: null ^ stdClass @@ -1957,12 +1844,10 @@ Unsupported operand types: bool ^ stdClass Unsupported operand types: stdClass ^ int Unsupported operand types: int ^ stdClass Unsupported operand types: stdClass ^ float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float ^ stdClass Unsupported operand types: stdClass ^ string Unsupported operand types: string ^ stdClass Unsupported operand types: stdClass ^ string -Warning: A non-numeric value encountered Unsupported operand types: string ^ stdClass Unsupported operand types: resource ^ null Unsupported operand types: null ^ resource @@ -1973,12 +1858,10 @@ Unsupported operand types: bool ^ resource Unsupported operand types: resource ^ int Unsupported operand types: int ^ resource Unsupported operand types: resource ^ float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float ^ resource Unsupported operand types: resource ^ string Unsupported operand types: string ^ resource Unsupported operand types: resource ^ string -Warning: A non-numeric value encountered Unsupported operand types: string ^ resource Unsupported operand types: string ^ null Unsupported operand types: null ^ string @@ -1989,7 +1872,6 @@ Unsupported operand types: bool ^ string Unsupported operand types: string ^ int Unsupported operand types: int ^ string Unsupported operand types: string ^ float -Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float ^ string No error for "foo" ^= "123" No error for "123" ^= "foo" @@ -1998,7 +1880,6 @@ No error for "123foo" ^= "foo" Warning: Array to string conversion Warning: Array to string conversion No error for [] .= [] -Warning: Array to string conversion Object of class stdClass could not be converted to string Warning: Array to string conversion No error for [] .= STDOUT @@ -2104,7 +1985,7 @@ Cannot increment stdClass Cannot decrement stdClass Cannot increment resource Cannot decrement resource -Warning: Increment on non-numeric string is deprecated, use str_increment() instead No error for fop++ -Warning: Decrement on non-numeric string has no effect and is deprecated +Warning: Increment on non-numeric string is deprecated, use str_increment() instead No error for foo-- +Warning: Decrement on non-numeric string has no effect and is deprecated \ No newline at end of file diff --git a/Zend/tests/oss_fuzz_54325.phpt b/Zend/tests/oss_fuzz_54325.phpt index d998acf1ffed..18b108681c2d 100644 --- a/Zend/tests/oss_fuzz_54325.phpt +++ b/Zend/tests/oss_fuzz_54325.phpt @@ -14,6 +14,5 @@ $$x++; var_dump($x); ?> --EXPECT-- +string(3) "oof" string(23) "Undefined variable $oof" -object(stdClass)#2 (0) { -} diff --git a/Zend/tests/oss_fuzz_61712.phpt b/Zend/tests/oss_fuzz_61712.phpt index 5e3aa9060fde..655fb56c1421 100644 --- a/Zend/tests/oss_fuzz_61712.phpt +++ b/Zend/tests/oss_fuzz_61712.phpt @@ -16,5 +16,5 @@ $c->a %= 10; var_dump($c->a); ?> --EXPECT-- -Undefined property: C::$a int(0) +Undefined property: C::$a diff --git a/Zend/tests/oss_fuzz_61712b.phpt b/Zend/tests/oss_fuzz_61712b.phpt index 8ee93b97b432..bfb3d2757366 100644 --- a/Zend/tests/oss_fuzz_61712b.phpt +++ b/Zend/tests/oss_fuzz_61712b.phpt @@ -16,5 +16,5 @@ $c->a %= 10; var_dump($c->a); ?> --EXPECT-- +int(0) Undefined property: C::$a -int(5) diff --git a/Zend/tests/str_offset_006.phpt b/Zend/tests/str_offset_006.phpt index 3cdf91656bd2..fa35c8d34b9c 100644 --- a/Zend/tests/str_offset_006.phpt +++ b/Zend/tests/str_offset_006.phpt @@ -9,8 +9,8 @@ set_error_handler(function($code, $msg) { $a[$y]=$a.=($y); var_dump($a); ?> ---EXPECT-- -Err: Undefined variable $y -Err: Undefined variable $y -Err: String offset cast occurred -NULL +--EXPECTF-- +Fatal error: Uncaught Error: Cannot assign an empty string to a string offset in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/tests/str_offset_007.phpt b/Zend/tests/str_offset_007.phpt index 3998ac401f63..09d254cac3bf 100644 --- a/Zend/tests/str_offset_007.phpt +++ b/Zend/tests/str_offset_007.phpt @@ -11,6 +11,9 @@ $a[0][$d]='b'; var_dump($a); ?> --EXPECT-- +array(1) { + [0]=> + string(1) "b" +} Err: Undefined variable $d Err: String offset cast occurred -string(0) "" diff --git a/Zend/tests/str_offset_008.phpt b/Zend/tests/str_offset_008.phpt index e99e46e59e74..7a88136d5298 100644 --- a/Zend/tests/str_offset_008.phpt +++ b/Zend/tests/str_offset_008.phpt @@ -12,7 +12,7 @@ var_dump($a[0][$b]); var_dump($a); ?> --EXPECT-- +string(1) "x" Err: Undefined variable $b Err: String offset cast occurred -string(1) "x" int(8) diff --git a/Zend/tests/strlen_deprecation_to_exception.phpt b/Zend/tests/strlen_deprecation_to_exception.phpt index 7b616f3b6432..48cba977a06a 100644 --- a/Zend/tests/strlen_deprecation_to_exception.phpt +++ b/Zend/tests/strlen_deprecation_to_exception.phpt @@ -13,5 +13,9 @@ try { } ?> ---EXPECT-- -strlen(): Passing null to parameter #1 ($string) of type string is deprecated +--EXPECTF-- +Fatal error: Uncaught Exception: strlen(): Passing null to parameter #1 ($string) of type string is deprecated in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(%s) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/temporary_cleaning/temporary_cleaning_015.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_015.phpt index f5e115faa970..8f0246dee1d6 100644 --- a/Zend/tests/temporary_cleaning/temporary_cleaning_015.phpt +++ b/Zend/tests/temporary_cleaning/temporary_cleaning_015.phpt @@ -13,5 +13,11 @@ try { } ?> DONE ---EXPECT-- -DONE +--EXPECTF-- +Array + +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(2, 'Array to string...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/temporary_cleaning/temporary_cleaning_016.phpt b/Zend/tests/temporary_cleaning/temporary_cleaning_016.phpt index 8dc4c8ea3148..1a920b50d22d 100644 --- a/Zend/tests/temporary_cleaning/temporary_cleaning_016.phpt +++ b/Zend/tests/temporary_cleaning/temporary_cleaning_016.phpt @@ -13,4 +13,9 @@ try { DONE --EXPECTF-- Deprecated: Using ${expr} (variable variables) in strings is deprecated, use {${expr}} instead in %s on line %d -DONE + +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(2, 'Array to string...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra1.phpt b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra1.phpt index 42b91f4a656d..1880b8f3b3a6 100644 --- a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra1.phpt +++ b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra1.phpt @@ -14,5 +14,5 @@ var_dump($b | 1); ?> --EXPECTF-- +int(9223372036854775807) Implicit conversion from float-string "1.0E+4%d" to int loses precision -int(%d) diff --git a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra3.phpt b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra3.phpt index 0bfbeb01a2d0..5d44d8021910 100644 --- a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra3.phpt +++ b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra3.phpt @@ -14,5 +14,5 @@ var_dump(isset($ary[1.0E+42])); ?> --EXPECT-- +bool(true) The float 1.0E+42 is not representable as an int, cast occurred -bool(false) diff --git a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra4.phpt b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra4.phpt index 8134ce08d2ee..3f49fcea85ba 100644 --- a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra4.phpt +++ b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra4.phpt @@ -14,5 +14,5 @@ var_dump(\array_key_exists(1.0E+42, $ary)); ?> --EXPECT-- +bool(true) The float 1.0E+42 is not representable as an int, cast occurred -bool(false) diff --git a/Zend/tests/type_coercion/gh22112.phpt b/Zend/tests/type_coercion/gh22112.phpt index 84fdc393a828..dbbaf66757b9 100644 --- a/Zend/tests/type_coercion/gh22112.phpt +++ b/Zend/tests/type_coercion/gh22112.phpt @@ -31,5 +31,7 @@ try { ?> --EXPECT-- +take_bool entered bool: unexpected NAN value was coerced to bool +take_string entered string: unexpected NAN value was coerced to string diff --git a/Zend/tests/undef_index_to_exception.phpt b/Zend/tests/undef_index_to_exception.phpt index bbe13c0e71d0..5ed70b1f1494 100644 --- a/Zend/tests/undef_index_to_exception.phpt +++ b/Zend/tests/undef_index_to_exception.phpt @@ -35,12 +35,9 @@ try { } ?> ---EXPECT-- -Undefined array key 0 -array(0) { -} -Undefined array key "key" -array(0) { -} -Undefined global variable $test -Undefined variable $test +--EXPECTF-- +Fatal error: Uncaught Exception: Undefined array key 0 in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(2, 'Undefined array...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/undef_var_in_verify_return.phpt b/Zend/tests/undef_var_in_verify_return.phpt index f3966c6992d6..ef37694a7dba 100644 --- a/Zend/tests/undef_var_in_verify_return.phpt +++ b/Zend/tests/undef_var_in_verify_return.phpt @@ -15,9 +15,8 @@ test(); ?> --EXPECTF-- -Fatal error: Uncaught ErrorException: Undefined variable $test in %s:%d +Fatal error: Uncaught TypeError: test(): Return value must be of type string, null returned in %s:%d Stack trace: -#0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', 8) -#1 %s(%d): test() -#2 {main} +#0 %s(%d): test() +#1 {main} thrown in %s on line %d diff --git a/Zend/zend.c b/Zend/zend.c index f16b1a30dbbc..2c360f079939 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1433,9 +1433,10 @@ ZEND_API zval *zend_get_configuration_directive(zend_string *name) /* {{{ */ } \ } while (0) -ZEND_API ZEND_COLD void zend_error_zstr_at( +static void zend_call_user_error_handler( int orig_type, zend_string *error_filename, uint32_t error_lineno, zend_string *message) { + int type = orig_type & E_ALL; zval params[4]; zval retval; zval orig_user_error_handler; @@ -1443,11 +1444,188 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( zend_class_entry *saved_class_entry = NULL; zend_stack loop_var_stack; zend_stack delayed_oplines_stack; - int type = orig_type & E_ALL; bool orig_record_errors; zend_err_buf orig_errors_buf; zend_result res; + if (Z_TYPE(EG(user_error_handler)) == IS_UNDEF + || !(EG(user_error_handler_error_reporting) & type) + || EG(error_handling) != EH_NORMAL) { + zend_error_cb(orig_type, error_filename, error_lineno, message); + return; + } + + zend_flush_deferred_errors(); + + ZVAL_STR_COPY(¶ms[1], message); + ZVAL_LONG(¶ms[0], type); + + if (error_filename) { + ZVAL_STR_COPY(¶ms[2], error_filename); + } else { + ZVAL_NULL(¶ms[2]); + } + + ZVAL_LONG(¶ms[3], error_lineno); + + ZVAL_COPY_VALUE(&orig_user_error_handler, &EG(user_error_handler)); + ZVAL_UNDEF(&EG(user_error_handler)); + + /* User error handler may include() additional PHP files. + * If an error was generated during compilation PHP will compile + * such scripts recursively, but some CG() variables may be + * inconsistent. */ + + in_compilation = CG(in_compilation); + if (in_compilation) { + saved_class_entry = CG(active_class_entry); + CG(active_class_entry) = NULL; + SAVE_STACK(loop_var_stack); + SAVE_STACK(delayed_oplines_stack); + CG(in_compilation) = 0; + } + + orig_record_errors = EG(record_errors); + EG(record_errors) = false; + + orig_errors_buf = EG(errors); + memset(&EG(errors), 0, sizeof(EG(errors))); + + res = call_user_function(CG(function_table), NULL, &orig_user_error_handler, &retval, 4, params); + + EG(record_errors) = orig_record_errors; + EG(errors) = orig_errors_buf; + + if (res == SUCCESS) { + if (Z_TYPE(retval) != IS_UNDEF) { + if (Z_TYPE(retval) == IS_FALSE) { + zend_error_cb(orig_type, error_filename, error_lineno, message); + } + zval_ptr_dtor(&retval); + } + } else if (!EG(exception)) { + /* The user error handler failed, use built-in error handler */ + zend_error_cb(orig_type, error_filename, error_lineno, message); + } + + if (in_compilation) { + CG(active_class_entry) = saved_class_entry; + RESTORE_STACK(loop_var_stack); + RESTORE_STACK(delayed_oplines_stack); + CG(in_compilation) = 1; + } + + zval_ptr_dtor(¶ms[2]); + zval_ptr_dtor(¶ms[1]); + + if (Z_TYPE(EG(user_error_handler)) == IS_UNDEF) { + ZVAL_COPY_VALUE(&EG(user_error_handler), &orig_user_error_handler); + } else { + zval_ptr_dtor(&orig_user_error_handler); + } +} + +static zend_always_inline bool zend_can_defer_error(int type) +{ + if (type == E_USER_ERROR || type == E_RECOVERABLE_ERROR) { + return false; + } + if (CG(in_compilation) || !EG(current_execute_data) || !EG(current_execute_data)->func) { + return false; + } + zend_execute_data *ex = EG(current_execute_data); + if (!ZEND_USER_CODE(ex->func->type) || !ex->opline) { + return false; + } + switch (ex->opline->opcode) { + case ZEND_DECLARE_CLASS: + case ZEND_DECLARE_CLASS_DELAYED: + case ZEND_DECLARE_ANON_CLASS: + case ZEND_INIT_FCALL: + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_INIT_USER_CALL: + case ZEND_INIT_DYNAMIC_CALL: + case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL: + case ZEND_DO_FCALL: + case ZEND_DO_FCALL_BY_NAME: + case ZEND_DO_UCALL: + case ZEND_FETCH_CONSTANT: + case ZEND_FETCH_CLASS_CONSTANT: + case ZEND_INCLUDE_OR_EVAL: + return false; + } + return true; +} + +static void zend_defer_error( + int orig_type, zend_string *error_filename, uint32_t error_lineno, zend_string *message) +{ + zend_error_info *info = emalloc(sizeof(zend_error_info)); + info->type = orig_type; + info->lineno = error_lineno; + info->filename = error_filename ? zend_string_copy(error_filename) : NULL; + info->message = zend_string_copy(message); + + EG(deferred_errors).size++; + if (EG(deferred_errors).size > EG(deferred_errors).capacity) { + uint32_t capacity = EG(deferred_errors).capacity + ? EG(deferred_errors).capacity + (EG(deferred_errors).capacity >> 1) : 2; + EG(deferred_errors).errors = erealloc(EG(deferred_errors).errors, sizeof(zend_error_info *) * capacity); + EG(deferred_errors).capacity = capacity; + } + EG(deferred_errors).errors[EG(deferred_errors).size - 1] = info; + + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); +} + +ZEND_API void zend_flush_deferred_errors(void) +{ + if (!EG(deferred_errors).size) { + return; + } + + zend_err_buf buf = EG(deferred_errors); + memset(&EG(deferred_errors), 0, sizeof(EG(deferred_errors))); + + for (uint32_t i = 0; i < buf.size; i++) { + zend_error_info *info = buf.errors[i]; + if (!EG(exception)) { + zend_call_user_error_handler(info->type, info->filename, info->lineno, info->message); + } + if (info->filename) { + zend_string_release(info->filename); + } + zend_string_release(info->message); + efree_size(info, sizeof(zend_error_info)); + } + efree(buf.errors); +} + +ZEND_API void zend_free_deferred_errors(void) +{ + if (!EG(deferred_errors).size) { + return; + } + for (uint32_t i = 0; i < EG(deferred_errors).size; i++) { + zend_error_info *info = EG(deferred_errors).errors[i]; + if (info->filename) { + zend_string_release(info->filename); + } + zend_string_release(info->message); + efree_size(info, sizeof(zend_error_info)); + } + efree(EG(deferred_errors).errors); + memset(&EG(deferred_errors), 0, sizeof(EG(deferred_errors))); +} + +ZEND_API ZEND_COLD void zend_error_zstr_at( + int orig_type, zend_string *error_filename, uint32_t error_lineno, zend_string *message) +{ + int type = orig_type & E_ALL; + /* If we're executing a function during SCCP, count any warnings that may be emitted, * but don't perform any other error handling. */ if (EG(capture_warnings_during_sccp)) { @@ -1541,72 +1719,10 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( zend_error_cb(orig_type, error_filename, error_lineno, message); break; default: - /* Handle the error in user space */ - ZVAL_STR_COPY(¶ms[1], message); - ZVAL_LONG(¶ms[0], type); - - if (error_filename) { - ZVAL_STR_COPY(¶ms[2], error_filename); - } else { - ZVAL_NULL(¶ms[2]); - } - - ZVAL_LONG(¶ms[3], error_lineno); - - ZVAL_COPY_VALUE(&orig_user_error_handler, &EG(user_error_handler)); - ZVAL_UNDEF(&EG(user_error_handler)); - - /* User error handler may include() additional PHP files. - * If an error was generated during compilation PHP will compile - * such scripts recursively, but some CG() variables may be - * inconsistent. */ - - in_compilation = CG(in_compilation); - if (in_compilation) { - saved_class_entry = CG(active_class_entry); - CG(active_class_entry) = NULL; - SAVE_STACK(loop_var_stack); - SAVE_STACK(delayed_oplines_stack); - CG(in_compilation) = 0; - } - - orig_record_errors = EG(record_errors); - EG(record_errors) = false; - - orig_errors_buf = EG(errors); - memset(&EG(errors), 0, sizeof(EG(errors))); - - res = call_user_function(CG(function_table), NULL, &orig_user_error_handler, &retval, 4, params); - - EG(record_errors) = orig_record_errors; - EG(errors) = orig_errors_buf; - - if (res == SUCCESS) { - if (Z_TYPE(retval) != IS_UNDEF) { - if (Z_TYPE(retval) == IS_FALSE) { - zend_error_cb(orig_type, error_filename, error_lineno, message); - } - zval_ptr_dtor(&retval); - } - } else if (!EG(exception)) { - /* The user error handler failed, use built-in error handler */ - zend_error_cb(orig_type, error_filename, error_lineno, message); - } - - if (in_compilation) { - CG(active_class_entry) = saved_class_entry; - RESTORE_STACK(loop_var_stack); - RESTORE_STACK(delayed_oplines_stack); - CG(in_compilation) = 1; - } - - zval_ptr_dtor(¶ms[2]); - zval_ptr_dtor(¶ms[1]); - - if (Z_TYPE(EG(user_error_handler)) == IS_UNDEF) { - ZVAL_COPY_VALUE(&EG(user_error_handler), &orig_user_error_handler); + if (zend_can_defer_error(type)) { + zend_defer_error(orig_type, error_filename, error_lineno, message); } else { - zval_ptr_dtor(&orig_user_error_handler); + zend_call_user_error_handler(orig_type, error_filename, error_lineno, message); } break; } @@ -1970,6 +2086,7 @@ ZEND_API zend_result zend_execute_script(int type, zval *retval, zend_file_handl zend_result ret = SUCCESS; if (op_array) { zend_execute(op_array, retval); + zend_flush_deferred_errors(); if (UNEXPECTED(EG(exception))) { if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) { zend_user_exception_handler(); diff --git a/Zend/zend.h b/Zend/zend.h index 0d5303192b57..969801a291e5 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -453,6 +453,8 @@ 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); +ZEND_API void zend_flush_deferred_errors(void); +ZEND_API void zend_free_deferred_errors(void); END_EXTERN_C() #define DEBUG_BACKTRACE_PROVIDE_OBJECT (1<<0) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 1b28ce25fe37..928bc3395e90 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -4316,13 +4316,17 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_fcall_interrupt(zend_execute_data *ca zend_atomic_bool_store_ex(&EG(vm_interrupt), false); if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); - } else if (zend_interrupt_function) { - zend_interrupt_function(call); + } else { + zend_flush_deferred_errors(); + if (zend_interrupt_function) { + zend_interrupt_function(call); + } } } #define ZEND_VM_INTERRUPT_CHECK() do { \ if (UNEXPECTED(zend_atomic_bool_load_ex(&EG(vm_interrupt)))) { \ + SAVE_OPLINE(); \ ZEND_VM_INTERRUPT(); \ } \ } while (0) diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 71e0c56a51c8..87dbe9ac6c4e 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -192,6 +192,7 @@ void init_executor(void) /* {{{ */ EG(record_errors) = false; memset(&EG(errors), 0, sizeof(EG(errors))); + memset(&EG(deferred_errors), 0, sizeof(EG(deferred_errors))); EG(filename_override) = NULL; EG(lineno_override) = -1; @@ -445,6 +446,8 @@ void shutdown_executor(void) /* {{{ */ bool fast_shutdown = is_zend_mm() && !EG(full_tables_cleanup); #endif + zend_free_deferred_errors(); + zend_try { zend_stream_shutdown(); } zend_end_try(); diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 8257df32e831..bb522ff111a7 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -306,6 +306,11 @@ struct _zend_executor_globals { bool record_errors; zend_err_buf errors; + /* Diagnostics whose user error handler is deferred to the next VM + * safepoint, so the handler cannot run mid-opcode and corrupt a + * structure the opcode still holds a pointer into. */ + zend_err_buf deferred_errors; + /* Override filename or line number of thrown errors and exceptions */ zend_string *filename_override; zend_long lineno_override; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index a0bfad488f85..0c9b6c3cc3e6 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2992,6 +2992,10 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) SAVE_OPLINE(); #endif + if (UNEXPECTED(EG(deferred_errors).size)) { + zend_flush_deferred_errors(); + } + if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); i_free_compiled_variables(execute_data); @@ -10635,23 +10639,33 @@ ZEND_VM_HELPER(zend_interrupt_helper, ANY, ANY) #endif if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); - } else if (zend_interrupt_function) { - zend_interrupt_function(execute_data); - if (EG(exception)) { - /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ - const zend_op *throw_op = EG(opline_before_exception); + } else { + bool entered = false; + if (EG(deferred_errors).size) { + zend_flush_deferred_errors(); + entered = true; + } + if (zend_interrupt_function) { + zend_interrupt_function(execute_data); + entered = true; + } + if (entered) { + if (EG(exception)) { + /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ + const zend_op *throw_op = EG(opline_before_exception); - if (throw_op - && throw_op->result_type & (IS_TMP_VAR|IS_VAR) - && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT - && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK - && throw_op->opcode != ZEND_ROPE_INIT - && throw_op->opcode != ZEND_ROPE_ADD) { - ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + if (throw_op + && throw_op->result_type & (IS_TMP_VAR|IS_VAR) + && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT + && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK + && throw_op->opcode != ZEND_ROPE_INIT + && throw_op->opcode != ZEND_ROPE_ADD) { + ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + } } + ZEND_VM_ENTER(); } - ZEND_VM_ENTER(); } ZEND_VM_CONTINUE(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index cedc735bbb1e..6642f4248bbf 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1157,6 +1157,10 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV SAVE_OPLINE(); #endif + if (UNEXPECTED(EG(deferred_errors).size)) { + zend_flush_deferred_errors(); + } + if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); i_free_compiled_variables(execute_data); @@ -4040,23 +4044,33 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV #endif if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); - } else if (zend_interrupt_function) { - zend_interrupt_function(execute_data); - if (EG(exception)) { - /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ - const zend_op *throw_op = EG(opline_before_exception); + } else { + bool entered = false; + if (EG(deferred_errors).size) { + zend_flush_deferred_errors(); + entered = true; + } + if (zend_interrupt_function) { + zend_interrupt_function(execute_data); + entered = true; + } + if (entered) { + if (EG(exception)) { + /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ + const zend_op *throw_op = EG(opline_before_exception); - if (throw_op - && throw_op->result_type & (IS_TMP_VAR|IS_VAR) - && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT - && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK - && throw_op->opcode != ZEND_ROPE_INIT - && throw_op->opcode != ZEND_ROPE_ADD) { - ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + if (throw_op + && throw_op->result_type & (IS_TMP_VAR|IS_VAR) + && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT + && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK + && throw_op->opcode != ZEND_ROPE_INIT + && throw_op->opcode != ZEND_ROPE_ADD) { + ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + } } + ZEND_VM_ENTER(); } - ZEND_VM_ENTER(); } ZEND_VM_CONTINUE(); } @@ -53962,6 +53976,10 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend SAVE_OPLINE(); #endif + if (UNEXPECTED(EG(deferred_errors).size)) { + zend_flush_deferred_errors(); + } + if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); i_free_compiled_variables(execute_data); @@ -56729,23 +56747,33 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend #endif if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); - } else if (zend_interrupt_function) { - zend_interrupt_function(execute_data); - if (EG(exception)) { - /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ - const zend_op *throw_op = EG(opline_before_exception); + } else { + bool entered = false; + if (EG(deferred_errors).size) { + zend_flush_deferred_errors(); + entered = true; + } + if (zend_interrupt_function) { + zend_interrupt_function(execute_data); + entered = true; + } + if (entered) { + if (EG(exception)) { + /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */ + const zend_op *throw_op = EG(opline_before_exception); - if (throw_op - && throw_op->result_type & (IS_TMP_VAR|IS_VAR) - && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT - && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK - && throw_op->opcode != ZEND_ROPE_INIT - && throw_op->opcode != ZEND_ROPE_ADD) { - ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + if (throw_op + && throw_op->result_type & (IS_TMP_VAR|IS_VAR) + && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT + && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK + && throw_op->opcode != ZEND_ROPE_INIT + && throw_op->opcode != ZEND_ROPE_ADD) { + ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var)); + } } + ZEND_VM_ENTER(); } - ZEND_VM_ENTER(); } ZEND_VM_CONTINUE(); } @@ -110304,6 +110332,10 @@ ZEND_API void execute_ex(zend_execute_data *ex) SAVE_OPLINE(); #endif + if (UNEXPECTED(EG(deferred_errors).size)) { + zend_flush_deferred_errors(); + } + if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) { EG(current_execute_data) = EX(prev_execute_data); i_free_compiled_variables(execute_data); diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index fbbfab6b243c..f93379349c78 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -2934,7 +2934,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op } } if (!zend_jit_leave_func(&ctx, op_array, NULL, MAY_BE_ANY, left_frame, - NULL, NULL, (ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0, 1)) { + NULL, NULL, (ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0)) { goto jit_failure; } } diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 224f6f7237eb..7fafe86ffda0 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -2073,16 +2073,17 @@ static int zend_jit_interrupt_handler_stub(zend_jit_ctx *jit) ir_CALL(IR_VOID, ir_CONST_FUNC(zend_timeout)); ir_MERGE_WITH_EMPTY_TRUE(if_timeout); + ir_CALL(IR_VOID, ir_CONST_FUNC(zend_flush_deferred_errors)); if (zend_interrupt_function) { ir_CALL_1(IR_VOID, ir_CONST_FUNC(zend_interrupt_function), jit_FP(jit)); - if_exception = ir_IF(ir_LOAD_A(jit_EG(exception))); - ir_IF_TRUE(if_exception); - ir_CALL(IR_VOID, ir_CONST_FUNC(zend_jit_exception_in_interrupt_handler_helper)); - ir_MERGE_WITH_EMPTY_FALSE(if_exception); - - jit_STORE_FP(jit, ir_LOAD_A(jit_EG(current_execute_data))); - jit_STORE_IP(jit, ir_LOAD_A(jit_EX(opline))); } + if_exception = ir_IF(ir_LOAD_A(jit_EG(exception))); + ir_IF_TRUE(if_exception); + ir_CALL(IR_VOID, ir_CONST_FUNC(zend_jit_exception_in_interrupt_handler_helper)); + ir_MERGE_WITH_EMPTY_FALSE(if_exception); + + jit_STORE_FP(jit, ir_LOAD_A(jit_EG(current_execute_data))); + jit_STORE_IP(jit, ir_LOAD_A(jit_EX(opline))); if (GCC_GLOBAL_REGS || ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL) { zend_jit_tailcall_handler(jit, ir_LOAD_A(jit_IP(jit))); @@ -2528,9 +2529,6 @@ static int zend_jit_trace_exit_stub(zend_jit_ctx *jit) ref = ir_LOAD_A(jit_EX(opline)); jit_STORE_IP(jit, ref); - // check for interrupt (try to avoid this ???) - zend_jit_check_timeout(jit, NULL, NULL); - addr = zend_jit_orig_opline_handler(jit); if (GCC_GLOBAL_REGS || ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL) { zend_jit_tailcall_handler(jit, addr); @@ -10046,6 +10044,15 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen const zend_op *prev_opline; ir_ref rx, func_ref = IR_UNUSED, if_user = IR_UNUSED, user_path = IR_UNUSED; + { + ir_ref if_deferred = ir_IF(ir_LOAD_U32(jit_EG(deferred_errors.size))); + ir_IF_TRUE_cold(if_deferred); + ir_STORE(jit_EG(current_execute_data), jit_FP(jit)); + ir_CALL(IR_VOID, ir_CONST_FUNC(zend_flush_deferred_errors)); + ir_GUARD_NOT(ir_LOAD_A(jit_EG(exception)), jit_STUB_ADDR(jit, jit_stub_exception_handler)); + ir_MERGE_WITH_EMPTY_FALSE(if_deferred); + } + prev_opline = opline - 1; while (prev_opline->opcode == ZEND_EXT_FCALL_BEGIN || prev_opline->opcode == ZEND_TICKS) { prev_opline--; @@ -10723,7 +10730,7 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen */ if (!zend_interrupt_function) { // TODO: Can we avoid checking for interrupts after each call ??? - if (trace && jit->last_valid_opline != opline) { + if (trace) { int32_t exit_point = zend_jit_trace_get_exit_point(opline + 1, ZEND_JIT_EXIT_TO_VM); exit_addr = zend_jit_trace_get_exit_addr(exit_point); @@ -11064,8 +11071,7 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, bool left_frame, zend_jit_trace_rec *trace, zend_jit_trace_info *trace_info, - int indirect_var_access, - int may_throw) + int indirect_var_access) { bool may_be_top_frame = JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || @@ -11089,6 +11095,14 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, !TRACE_FRAME_NO_NEED_RELEASE_THIS(JIT_G(current_frame))); ir_ref call_info = IR_UNUSED, ref, cold_path = IR_UNUSED; + { + ir_ref if_deferred = ir_IF(ir_LOAD_U32(jit_EG(deferred_errors.size))); + ir_IF_TRUE_cold(if_deferred); + ir_STORE(jit_EG(current_execute_data), jit_FP(jit)); + ir_CALL(IR_VOID, ir_CONST_FUNC(zend_flush_deferred_errors)); + ir_MERGE_WITH_EMPTY_FALSE(if_deferred); + } + if (may_need_call_helper) { if (!left_frame) { left_frame = true; @@ -11170,8 +11184,6 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, if (fast_path) { ir_MERGE_WITH(fast_path); } - // TODO: avoid EG(excption) check for $this->foo() calls - may_throw = 1; } // JIT: EG(vm_stack_top) = (zval*)execute_data @@ -11214,13 +11226,8 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, && (!JIT_G(current_frame) || TRACE_FRAME_IS_UNKNOWN_RETURN(JIT_G(current_frame)))) { const zend_op *next_opline = trace->opline; - if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) - && (op1_info & MAY_BE_RC1) - && (op1_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) { - /* exception might be thrown during destruction of unused return value */ - // JIT: if (EG(exception)) - ir_GUARD_NOT(ir_LOAD_A(jit_EG(exception)), jit_STUB_ADDR(jit, jit_stub_leave_throw)); - } + // JIT: if (EG(exception)) + ir_GUARD_NOT(ir_LOAD_A(jit_EG(exception)), jit_STUB_ADDR(jit, jit_stub_leave_throw)); do { trace++; } while (trace->op == ZEND_JIT_TRACE_INIT_CALL); @@ -11252,11 +11259,7 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, zend_jit_set_last_valid_opline(jit, trace->opline); return 1; - } else if (may_throw || - (((opline->op1_type & (IS_VAR|IS_TMP_VAR)) - && (op1_info & MAY_BE_RC1) - && (op1_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) - && (!JIT_G(current_frame) || TRACE_FRAME_IS_RETURN_VALUE_UNUSED(JIT_G(current_frame))))) { + } else { // JIT: if (EG(exception)) ir_GUARD_NOT(ir_LOAD_A(jit_EG(exception)), jit_STUB_ADDR(jit, jit_stub_leave_throw)); } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 024a5d0e194d..81333f427bbd 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -5596,7 +5596,6 @@ static zend_vm_opcode_handler_t zend_jit_trace(zend_jit_trace_rec *trace_buffer, } } else { int j; - int may_throw = 0; bool left_frame = 0; if (!zend_jit_return(&ctx, opline, op_array, @@ -5638,17 +5637,12 @@ static zend_vm_opcode_handler_t zend_jit_trace(zend_jit_trace_rec *trace_buffer, if (!zend_jit_free_cv(&ctx, info, j)) { goto jit_failure; } - if (info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_RESOURCE)) { - if (info & MAY_BE_RC1) { - may_throw = 1; - } - } } } } if (!zend_jit_leave_func(&ctx, op_array, opline, op1_info, left_frame, p + 1, &zend_jit_traces[ZEND_JIT_TRACE_NUM], - (op_array_ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0, may_throw)) { + (op_array_ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0)) { goto jit_failure; } } diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 271d923598d9..e0bfb5cbbd2b 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -102,6 +102,10 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_nested_func_helper(ZEND_OPC { zend_execute_data *old_execute_data; + if (UNEXPECTED(EG(deferred_errors).size)) { + zend_flush_deferred_errors(); + } + if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) { zend_clean_and_cache_symbol_table(EX(symbol_table)); } diff --git a/ext/opcache/tests/jit/assign_043.phpt b/ext/opcache/tests/jit/assign_043.phpt index edb0cb212ab0..b6dab34be9de 100644 --- a/ext/opcache/tests/jit/assign_043.phpt +++ b/ext/opcache/tests/jit/assign_043.phpt @@ -17,5 +17,8 @@ try { echo $e->getMessage(), "\n"; } ?> ---EXPECT-- -Undefined variable $b +--EXPECTF-- +Fatal error: Uncaught Error: Undefined constant "NOT_REACHED" in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/ext/opcache/tests/jit/assign_dim_005.phpt b/ext/opcache/tests/jit/assign_dim_005.phpt index e7bfdabacc4f..b839739574bb 100644 --- a/ext/opcache/tests/jit/assign_dim_005.phpt +++ b/ext/opcache/tests/jit/assign_dim_005.phpt @@ -16,6 +16,11 @@ $a[$c] = 'x' ; var_dump($a); ?> --EXPECT-- +array(1) { + [""]=> + string(1) "x" +} Error: Undefined variable $c +Error: Using null as an array offset is deprecated, use an empty string instead Error: Undefined variable $c -NULL +Error: Using null as an array offset is deprecated, use an empty string instead diff --git a/ext/opcache/tests/jit/assign_dim_007.phpt b/ext/opcache/tests/jit/assign_dim_007.phpt index 0df4e291ae93..590ab14b6670 100644 --- a/ext/opcache/tests/jit/assign_dim_007.phpt +++ b/ext/opcache/tests/jit/assign_dim_007.phpt @@ -18,7 +18,9 @@ x($y); var_dump($x,$y); ?> --EXPECT-- -array(0) { +array(1) { + [0]=> + int(1) } array(1) { [0]=> diff --git a/ext/opcache/tests/jit/assign_dim_011.phpt b/ext/opcache/tests/jit/assign_dim_011.phpt index 5df415c89e6c..daad27634196 100644 --- a/ext/opcache/tests/jit/assign_dim_011.phpt +++ b/ext/opcache/tests/jit/assign_dim_011.phpt @@ -18,7 +18,11 @@ try { } ?> DONE ---EXPECT-- +--EXPECTF-- Err: Automatic conversion of false to array is deprecated -Exception: Cannot use a scalar value as an array -DONE + +Fatal error: Uncaught Error: Cannot use a scalar value as an array in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(8192, 'Automatic conve...', '%s', %d) +#1 {main} + thrown in %s on line %d diff --git a/ext/opcache/tests/jit/assign_dim_014.phpt b/ext/opcache/tests/jit/assign_dim_014.phpt index 574b05258421..45958d82edab 100644 --- a/ext/opcache/tests/jit/assign_dim_014.phpt +++ b/ext/opcache/tests/jit/assign_dim_014.phpt @@ -14,5 +14,6 @@ $a[$y] = function(){}; ?> DONE --EXPECT-- -Error: Undefined variable $y DONE +Error: Undefined variable $y +Error: Using null as an array offset is deprecated, use an empty string instead diff --git a/ext/opcache/tests/jit/assign_dim_015.phpt b/ext/opcache/tests/jit/assign_dim_015.phpt index 2a5b6d46d37a..3cfb75a31b74 100644 --- a/ext/opcache/tests/jit/assign_dim_015.phpt +++ b/ext/opcache/tests/jit/assign_dim_015.phpt @@ -14,5 +14,5 @@ $my_var[] = $y; ?> DONE --EXPECT-- -Error: Undefined variable $y DONE +Error: Undefined variable $y diff --git a/ext/opcache/tests/jit/assign_dim_op_006.phpt b/ext/opcache/tests/jit/assign_dim_op_006.phpt index 8bfb9197177a..b2b606245be8 100644 --- a/ext/opcache/tests/jit/assign_dim_op_006.phpt +++ b/ext/opcache/tests/jit/assign_dim_op_006.phpt @@ -15,4 +15,7 @@ $my_var[000000000000000000000100000000000000000000000000000000000000000000000000 var_dump($my_var); ?> --EXPECT-- -int(0) +array(1) { + [0]=> + string(3) "xyz" +} diff --git a/ext/opcache/tests/jit/fetch_dim_rw_004.phpt b/ext/opcache/tests/jit/fetch_dim_rw_004.phpt index ae639c8f56ba..7816923bb79b 100644 --- a/ext/opcache/tests/jit/fetch_dim_rw_004.phpt +++ b/ext/opcache/tests/jit/fetch_dim_rw_004.phpt @@ -12,12 +12,7 @@ $k=[]; $y[$k]++; ?> --EXPECTF-- -Fatal error: Uncaught TypeError: {closure:%s:%d}(): Argument #1 ($y) must be of type y, int given, called in %s on line %d and defined in %s:%d -Stack trace: -#0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', 5) -#1 {main} - -Next TypeError: Cannot access offset of type array on array in %sfetch_dim_rw_004.php:5 +Fatal error: Uncaught TypeError: Cannot access offset of type array on array in %sfetch_dim_rw_004.php:%d Stack trace: #0 {main} - thrown in %sfetch_dim_rw_004.php on line 5 + thrown in %sfetch_dim_rw_004.php on line %d diff --git a/ext/opcache/tests/jit/gh18262-002.phpt b/ext/opcache/tests/jit/gh18262-002.phpt index b82723abaa29..c4de9ff0375a 100644 --- a/ext/opcache/tests/jit/gh18262-002.phpt +++ b/ext/opcache/tests/jit/gh18262-002.phpt @@ -26,9 +26,11 @@ foreach ($tests as $obj) { ?> --EXPECTF-- int(1) +NULL Fatal error: Uncaught Exception: Undefined property: B::$b in %s:%d Stack trace: -#0 %s(%d): {closure:%s:%d}(2, 'Undefined prope...', '%s', %d) -#1 {main} +#0 [internal function]: {closure:%s:%d}(2, 'Undefined prope...', '%s', %d) +#1 %s(%d): var_dump(NULL) +#2 {main} thrown in %s on line %d diff --git a/ext/opcache/tests/jit/gh18262-003.phpt b/ext/opcache/tests/jit/gh18262-003.phpt index a3c3e037632f..9f3f8c57ad55 100644 --- a/ext/opcache/tests/jit/gh18262-003.phpt +++ b/ext/opcache/tests/jit/gh18262-003.phpt @@ -32,4 +32,5 @@ foreach ($tests as $obj) { --EXPECT-- int(1) string(3) "str" +NULL Exception: Undefined property: B::$b diff --git a/ext/opcache/tests/jit/qm_assign_undef_exception.phpt b/ext/opcache/tests/jit/qm_assign_undef_exception.phpt index 4e93e2ed7e56..23650cb595c5 100644 --- a/ext/opcache/tests/jit/qm_assign_undef_exception.phpt +++ b/ext/opcache/tests/jit/qm_assign_undef_exception.phpt @@ -17,5 +17,9 @@ try { echo $e->getMessage(), "\n"; } ?> ---EXPECT-- -Undefined variable $b +--EXPECTF-- +Fatal error: Uncaught Error: Undefined constant "X" in %s:%d +Stack trace: +#0 %s(%d): test() +#1 {main} + thrown in %s on line %d diff --git a/ext/opcache/tests/jit/type_check_001.phpt b/ext/opcache/tests/jit/type_check_001.phpt index 028d2e8637e2..bf9bac572191 100644 --- a/ext/opcache/tests/jit/type_check_001.phpt +++ b/ext/opcache/tests/jit/type_check_001.phpt @@ -20,5 +20,8 @@ try { echo "Exception: " . $e->getMessage() . "\n"; } ?> ---EXPECT-- -Exception: Undefined variable $a +--EXPECTF-- +Fatal error: Uncaught Error: Call to undefined function undefined_function() in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/ext/spl/tests/gh18274.phpt b/ext/spl/tests/gh18274.phpt new file mode 100644 index 000000000000..82dd29b331f6 --- /dev/null +++ b/ext/spl/tests/gh18274.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-18274 (Assertion failure in SplFixedArray when an error handler runs during an undefined-variable write) +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught ArgumentCountError: func_get_args() expects exactly 0 arguments, 4 given in %s:%d +Stack trace: +#0 %s(%d): func_get_args(%d, %s, %s, %d) +#1 %s(%d): foo() +#2 {main} + thrown in %s on line %d diff --git a/ext/standard/array.c b/ext/standard/array.c index 3b17ca3f7942..102ddbe79d91 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -680,19 +680,20 @@ PHP_FUNCTION(natcasesort) typedef bucket_compare_func_t(*get_compare_function)(zend_long); static zend_always_inline void php_sort(INTERNAL_FUNCTION_PARAMETERS, get_compare_function get_cmp, bool renumber) { - HashTable *array; + zval *array; zend_long sort_type = PHP_SORT_REGULAR; bucket_compare_func_t cmp; ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_ARRAY_HT_EX(array, 0, 1) + Z_PARAM_ARRAY_EX(array, 0, 1) Z_PARAM_OPTIONAL Z_PARAM_LONG(sort_type) ZEND_PARSE_PARAMETERS_END(); cmp = get_cmp(sort_type); - zend_array_sort(array, cmp, renumber); + SEPARATE_ARRAY(array); + zend_array_sort(Z_ARRVAL_P(array), cmp, renumber); RETURN_TRUE; } @@ -911,22 +912,25 @@ PHP_FUNCTION(uksort) } /* }}} */ -static inline HashTable *get_ht_for_iap(zval *zv, bool separate) { +static inline HashTable *get_ht_for_iap(zval *zv, bool separate, zend_object **objp) { + *objp = NULL; if (EXPECTED(Z_TYPE_P(zv) == IS_ARRAY)) { return Z_ARRVAL_P(zv); } ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT); + zend_object *zobj = Z_OBJ_P(zv); + GC_ADDREF(zobj); php_error_docref(NULL, E_DEPRECATED, "Calling %s() on an object is deprecated", get_active_function_name()); - zend_object *zobj = Z_OBJ_P(zv); if (separate && zobj->properties && UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) { if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) { GC_DELREF(zobj->properties); } zobj->properties = zend_array_dup(zobj->properties); } + *objp = zobj; return zobj->handlers->get_properties(zobj); } @@ -979,15 +983,19 @@ PHP_FUNCTION(end) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) ZEND_PARSE_PARAMETERS_END(); - HashTable *array = get_ht_for_iap(array_zv, /* separate */ true); - if (zend_hash_num_elements(array) == 0) { + zend_object *iap_obj; + HashTable *array = get_ht_for_iap(array_zv, /* separate */ true, &iap_obj); + if (zend_hash_num_elements(array) != 0) { + zend_hash_internal_pointer_end(array); + if (USED_RET()) { + php_array_iter_return_current(return_value, array, false); + } + } else { /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */ - RETURN_FALSE; + RETVAL_FALSE; } - zend_hash_internal_pointer_end(array); - - if (USED_RET()) { - php_array_iter_return_current(return_value, array, false); + if (iap_obj) { + OBJ_RELEASE(iap_obj); } } /* }}} */ @@ -1001,15 +1009,19 @@ PHP_FUNCTION(prev) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) ZEND_PARSE_PARAMETERS_END(); - HashTable *array = get_ht_for_iap(array_zv, /* separate */ true); - if (zend_hash_num_elements(array) == 0) { + zend_object *iap_obj; + HashTable *array = get_ht_for_iap(array_zv, /* separate */ true, &iap_obj); + if (zend_hash_num_elements(array) != 0) { + zend_hash_move_backwards(array); + if (USED_RET()) { + php_array_iter_return_current(return_value, array, false); + } + } else { /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */ - RETURN_FALSE; + RETVAL_FALSE; } - zend_hash_move_backwards(array); - - if (USED_RET()) { - php_array_iter_return_current(return_value, array, false); + if (iap_obj) { + OBJ_RELEASE(iap_obj); } } /* }}} */ @@ -1023,15 +1035,19 @@ PHP_FUNCTION(next) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) ZEND_PARSE_PARAMETERS_END(); - HashTable *array = get_ht_for_iap(array_zv, /* separate */ true); - if (zend_hash_num_elements(array) == 0) { + zend_object *iap_obj; + HashTable *array = get_ht_for_iap(array_zv, /* separate */ true, &iap_obj); + if (zend_hash_num_elements(array) != 0) { + zend_hash_move_forward(array); + if (USED_RET()) { + php_array_iter_return_current(return_value, array, true); + } + } else { /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */ - RETURN_FALSE; + RETVAL_FALSE; } - zend_hash_move_forward(array); - - if (USED_RET()) { - php_array_iter_return_current(return_value, array, true); + if (iap_obj) { + OBJ_RELEASE(iap_obj); } } /* }}} */ @@ -1045,15 +1061,19 @@ PHP_FUNCTION(reset) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) ZEND_PARSE_PARAMETERS_END(); - HashTable *array = get_ht_for_iap(array_zv, /* separate */ true); - if (zend_hash_num_elements(array) == 0) { + zend_object *iap_obj; + HashTable *array = get_ht_for_iap(array_zv, /* separate */ true, &iap_obj); + if (zend_hash_num_elements(array) != 0) { + zend_hash_internal_pointer_reset(array); + if (USED_RET()) { + php_array_iter_return_current(return_value, array, true); + } + } else { /* array->nInternalPointer is already 0 if the array is empty, even after removing elements */ - RETURN_FALSE; + RETVAL_FALSE; } - zend_hash_internal_pointer_reset(array); - - if (USED_RET()) { - php_array_iter_return_current(return_value, array, true); + if (iap_obj) { + OBJ_RELEASE(iap_obj); } } /* }}} */ @@ -1067,8 +1087,12 @@ PHP_FUNCTION(current) Z_PARAM_ARRAY_OR_OBJECT(array_zv) ZEND_PARSE_PARAMETERS_END(); - HashTable *array = get_ht_for_iap(array_zv, /* separate */ false); + zend_object *iap_obj; + HashTable *array = get_ht_for_iap(array_zv, /* separate */ false, &iap_obj); php_array_iter_return_current(return_value, array, true); + if (iap_obj) { + OBJ_RELEASE(iap_obj); + } } /* }}} */ @@ -1081,11 +1105,15 @@ PHP_FUNCTION(key) Z_PARAM_ARRAY_OR_OBJECT(array_zv) ZEND_PARSE_PARAMETERS_END(); - HashTable *array = get_ht_for_iap(array_zv, /* separate */ false); + zend_object *iap_obj; + HashTable *array = get_ht_for_iap(array_zv, /* separate */ false, &iap_obj); zval *entry = php_array_iter_seek_current(array, true); if (EXPECTED(entry)) { zend_hash_get_current_key_zval(array, return_value); } + if (iap_obj) { + OBJ_RELEASE(iap_obj); + } } /* }}} */ diff --git a/ext/standard/tests/array/gh20042.phpt b/ext/standard/tests/array/gh20042.phpt new file mode 100644 index 000000000000..34ca982b2ae5 --- /dev/null +++ b/ext/standard/tests/array/gh20042.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-20042 (SEGV in array.c when the error handler reassigns the array during prev()) +--FILE-- + +--EXPECT-- +OK survived diff --git a/ext/standard/tests/array/sort/array_multisort_variation2.phpt b/ext/standard/tests/array/sort/array_multisort_variation2.phpt index b491b87bfda8..de08be4e0916 100644 --- a/ext/standard/tests/array/sort/array_multisort_variation2.phpt +++ b/ext/standard/tests/array/sort/array_multisort_variation2.phpt @@ -107,10 +107,12 @@ foreach($inputs as $key =>$value) { }; ?> ---EXPECT-- +--EXPECTF-- *** Testing array_multisort() : usage variation *** --int 0-- +Error: 2 - Undefined variable $undefined_var, %s(%d) +Error: 2 - Undefined variable $unset_var, %s(%d) bool(true) --int 1-- diff --git a/ext/standard/tests/class_object/get_class_methods_variation_001.phpt b/ext/standard/tests/class_object/get_class_methods_variation_001.phpt index f7b86a2977ac..430529b79547 100644 --- a/ext/standard/tests/class_object/get_class_methods_variation_001.phpt +++ b/ext/standard/tests/class_object/get_class_methods_variation_001.phpt @@ -109,23 +109,18 @@ get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid Arg value 0.5 get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, float given -Error: 2 - Array to string conversion Arg value Array get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given -Error: 2 - Array to string conversion Arg value Array get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given -Error: 2 - Array to string conversion Arg value Array get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given -Error: 2 - Array to string conversion Arg value Array get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given -Error: 2 - Array to string conversion Arg value Array get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given diff --git a/ext/standard/tests/class_object/get_parent_class_variation_002.phpt b/ext/standard/tests/class_object/get_parent_class_variation_002.phpt index 8faeff9dd8dc..82274edbe60e 100644 --- a/ext/standard/tests/class_object/get_parent_class_variation_002.phpt +++ b/ext/standard/tests/class_object/get_parent_class_variation_002.phpt @@ -112,23 +112,18 @@ get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid Arg value 0.5 get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, float given -Error: 2 - Array to string conversion Arg value Array get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given -Error: 2 - Array to string conversion Arg value Array get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given -Error: 2 - Array to string conversion Arg value Array get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given -Error: 2 - Array to string conversion Arg value Array get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given -Error: 2 - Array to string conversion Arg value Array get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given diff --git a/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt b/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt index f03841dbac7d..c5755a8e7eeb 100644 --- a/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt +++ b/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt @@ -81,10 +81,10 @@ echo "Done"; ?> --EXPECT-- *** Testing is_subclass_of() : usage variations *** -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var Arg value 0 +Error: 2 - Undefined variable $undefined_var +Error: 2 - Undefined variable $unset_var bool(false) Arg value 1 @@ -110,25 +110,25 @@ bool(false) Arg value 0.5 bool(false) -Error: 2 - Array to string conversion Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array +Error: 2 - Array to string conversion bool(false) Arg value diff --git a/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt b/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt index d045a4c1a915..8bc453876de6 100644 --- a/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt +++ b/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt @@ -81,10 +81,10 @@ echo "Done"; ?> --EXPECT-- *** Testing is_subclass_of() : usage variations *** -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var Arg value 0 +Error: 2 - Undefined variable $undefined_var +Error: 2 - Undefined variable $unset_var bool(false) Arg value 1 @@ -110,25 +110,25 @@ bool(false) Arg value 0.5 bool(false) -Error: 2 - Array to string conversion Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array -bool(false) Error: 2 - Array to string conversion +bool(false) Arg value Array +Error: 2 - Array to string conversion bool(false) Arg value diff --git a/ext/standard/tests/class_object/method_exists_variation_001.phpt b/ext/standard/tests/class_object/method_exists_variation_001.phpt index d45d2652d11c..ee96d3e7c5b8 100644 --- a/ext/standard/tests/class_object/method_exists_variation_001.phpt +++ b/ext/standard/tests/class_object/method_exists_variation_001.phpt @@ -83,8 +83,6 @@ echo "Done"; ?> --EXPECT-- *** Testing method_exists() : usage variations *** -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var Arg value 0 method_exists(): Argument #1 ($object_or_class) must be of type object|string, int given @@ -112,23 +110,18 @@ method_exists(): Argument #1 ($object_or_class) must be of type object|string, f Arg value 0.5 method_exists(): Argument #1 ($object_or_class) must be of type object|string, float given -Error: 2 - Array to string conversion Arg value Array method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given -Error: 2 - Array to string conversion Arg value Array method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given -Error: 2 - Array to string conversion Arg value Array method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given -Error: 2 - Array to string conversion Arg value Array method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given -Error: 2 - Array to string conversion Arg value Array method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given diff --git a/ext/standard/tests/file/file_put_contents_variation2.phpt b/ext/standard/tests/file/file_put_contents_variation2.phpt index 3d41a1f3c190..d02905c44470 100644 --- a/ext/standard/tests/file/file_put_contents_variation2.phpt +++ b/ext/standard/tests/file/file_put_contents_variation2.phpt @@ -111,6 +111,8 @@ unlink($filename); *** Testing file_put_contents() : usage variation *** --int 0-- +Error: 2 - Undefined variable $undefined_var +Error: 2 - Undefined variable $unset_var 0 --int 1-- 1 diff --git a/ext/standard/tests/image/getimagesize_variation2.phpt b/ext/standard/tests/image/getimagesize_variation2.phpt index 045d9141a6bd..fd47a45f4847 100644 --- a/ext/standard/tests/image/getimagesize_variation2.phpt +++ b/ext/standard/tests/image/getimagesize_variation2.phpt @@ -74,10 +74,10 @@ foreach($values as $key => $value) { ?> --EXPECT-- *** Testing getimagesize() : usage variations *** -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var -- Arg value 0 -- +Error: 2 - Undefined variable $undefined_var +Error: 2 - Undefined variable $unset_var string(28) "4a46494600010201006000600000" -- Arg value 1 -- diff --git a/ext/zend_test/tests/observer_error_05.phpt b/ext/zend_test/tests/observer_error_05.phpt index d672e6405ec8..378bb00589ab 100644 --- a/ext/zend_test/tests/observer_error_05.phpt +++ b/ext/zend_test/tests/observer_error_05.phpt @@ -30,15 +30,15 @@ echo 'You should not see this.'; - - <{closure:%s:%d}> - - + + + <{closure:%s:%d}> + + Deprecated: Passing E_USER_ERROR to trigger_error() is deprecated since 8.4, throw an exception or call exit with a string message instead in %s on line %d Fatal error: Foo error in %s on line %d - - - + + diff --git a/tests/lang/bug22592.phpt b/tests/lang/bug22592.phpt index 24b5f50fdf5a..2505b2ff7212 100644 --- a/tests/lang/bug22592.phpt +++ b/tests/lang/bug22592.phpt @@ -39,12 +39,12 @@ var_dump($result); string(5) "* *-*" string(7) "* *-* *" string(7) "*4*-* *" -[Only the first byte will be assigned to the string offset] string(7) "*4*s* *" +[Only the first byte will be assigned to the string offset] string(8) "*4*s* *0" string(8) "*-*-* *0" -[Only the first byte will be assigned to the string offset] string(8) "*-*s*s*0" +[Only the first byte will be assigned to the string offset] string(8) "4-4s4s*0" string(9) "4-4s4s505" string(9) "454s4s505" diff --git a/tests/lang/bug25547.phpt b/tests/lang/bug25547.phpt index 53b951fd4543..49bdb5e75115 100644 --- a/tests/lang/bug25547.phpt +++ b/tests/lang/bug25547.phpt @@ -22,9 +22,9 @@ print_r($output); echo "Done"; ?> --EXPECT-- -handler(Undefined array key "foo") Array ( [foo] => 1 ) +handler(Undefined array key "foo") Done diff --git a/tests/lang/bug25922.phpt b/tests/lang/bug25922.phpt index 4927bfea776c..41aa72ae35d6 100644 --- a/tests/lang/bug25922.phpt +++ b/tests/lang/bug25922.phpt @@ -19,6 +19,6 @@ function test() test(); ?> --EXPECT-- +Undefined index here: '' Undefined variable $data Trying to access array offset on null -Undefined index here: '' From 2fb25d1e4c9a2bbd5c65cf032921dc4681a6adcf Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sat, 27 Jun 2026 21:06:56 -0400 Subject: [PATCH 2/8] JIT: Flush deferred errors at interpreter granularity in function JIT Function JIT only checked for pending deferred errors at calls, leaves and loop headers, much coarser than the interpreter, which flushes at every jump (ZEND_VM_SET_OPCODE), call, leave and on function entry (ZEND_VM_ENTER). This left diagnostics emitted in JIT-compiled code surfacing at a different point than under the interpreter and tracing JIT. Add a materializing deopt at jump-target block starts and at the first opcode of the function (before RECV): when a deferred error is pending, spill the live register-allocated SSA vars (phi results and non-phi values live into the block) back to their EX_VAR slots and route to the interrupt handler, which flushes and resumes the interpreter for the rest of the function. Skip e-SSA Pi nodes when materializing: a Pi node aliases its source (for example a loop increment not yet computed at the block entry), so storing it writes a stale value into the CV slot the interpreter resumes on. Run the post-call interrupt check with the callee frame still on top, matching the interpreter's ZEND_VM_FCALL_INTERRUPT_CHECK, so a diagnostic deferred from an argument expression captures the same backtrace under function JIT as under the interpreter. Set EX(opline) from IP before the leave-helper flush so a backtrace through a still-suspended JIT frame reads a valid opline instead of NULL. Record EG(error_reporting) when a diagnostic is deferred and restore it around the handler at flush time, so a warning silenced by @ stays silenced regardless of which safepoint flushes it: the handler observes the error_reporting active when the diagnostic was raised, matching synchronous behaviour. --- Zend/tests/offsets/array_offset_002.phpt | 4 +- .../non-rep-float-as-int-extra1.phpt | 2 +- Zend/zend.c | 5 ++ Zend/zend.h | 1 + ext/opcache/jit/zend_jit.c | 14 ++++ ext/opcache/jit/zend_jit_ir.c | 76 ++++++++++++++++--- ext/opcache/jit/zend_jit_vm_helpers.c | 1 + ext/spl/tests/gh18274.phpt | 5 +- .../sort/array_multisort_variation2.phpt | 2 - .../is_subclass_of_variation_001.phpt | 10 +-- .../is_subclass_of_variation_004.phpt | 10 +-- .../file/file_put_contents_variation2.phpt | 2 - .../tests/image/getimagesize_variation2.phpt | 10 +-- 13 files changed, 106 insertions(+), 36 deletions(-) diff --git a/Zend/tests/offsets/array_offset_002.phpt b/Zend/tests/offsets/array_offset_002.phpt index 1511eb5dc73f..ece8a53c7215 100644 --- a/Zend/tests/offsets/array_offset_002.phpt +++ b/Zend/tests/offsets/array_offset_002.phpt @@ -12,9 +12,9 @@ function x(&$s){ x($y); var_dump($y); ?> ---EXPECT-- +--EXPECTF-- Err: The float 1.0E+20 is not representable as an int, cast occurred array(1) { - [7766279631452241920]=> + [%d]=> int(1) } diff --git a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra1.phpt b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra1.phpt index 1880b8f3b3a6..47bd2c6b05bc 100644 --- a/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra1.phpt +++ b/Zend/tests/type_coercion/float_to_int/non-rep-float-as-int-extra1.phpt @@ -14,5 +14,5 @@ var_dump($b | 1); ?> --EXPECTF-- -int(9223372036854775807) +int(%d) Implicit conversion from float-string "1.0E+4%d" to int loses precision diff --git a/Zend/zend.c b/Zend/zend.c index 2c360f079939..e56c01a161d1 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1566,6 +1566,7 @@ static void zend_defer_error( zend_error_info *info = emalloc(sizeof(zend_error_info)); info->type = orig_type; info->lineno = error_lineno; + info->error_reporting = EG(error_reporting); info->filename = error_filename ? zend_string_copy(error_filename) : NULL; info->message = zend_string_copy(message); @@ -1590,9 +1591,11 @@ ZEND_API void zend_flush_deferred_errors(void) zend_err_buf buf = EG(deferred_errors); memset(&EG(deferred_errors), 0, sizeof(EG(deferred_errors))); + int orig_error_reporting = EG(error_reporting); for (uint32_t i = 0; i < buf.size; i++) { zend_error_info *info = buf.errors[i]; if (!EG(exception)) { + EG(error_reporting) = info->error_reporting; zend_call_user_error_handler(info->type, info->filename, info->lineno, info->message); } if (info->filename) { @@ -1601,6 +1604,7 @@ ZEND_API void zend_flush_deferred_errors(void) zend_string_release(info->message); efree_size(info, sizeof(zend_error_info)); } + EG(error_reporting) = orig_error_reporting; efree(buf.errors); } @@ -1658,6 +1662,7 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( zend_error_info *info = emalloc(sizeof(zend_error_info)); info->type = type; info->lineno = error_lineno; + info->error_reporting = EG(error_reporting); info->filename = zend_string_copy(error_filename); info->message = zend_string_copy(message); EG(errors).size++; diff --git a/Zend/zend.h b/Zend/zend.h index 969801a291e5..302f76039295 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -128,6 +128,7 @@ typedef struct _zend_inheritance_cache_entry zend_inheritance_cache_entry; typedef struct _zend_error_info { int type; uint32_t lineno; + int error_reporting; zend_string *filename; zend_string *message; } zend_error_info; diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index f93379349c78..90ca407cb9e4 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -1425,6 +1425,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op int call_level = 0; void *checkpoint = NULL; bool recv_emitted = false; /* emitted at least one RECV opcode */ + bool entry_flush_emitted = false; uint8_t smart_branch_opcode; uint32_t target_label, target_label2; uint32_t op1_info, op1_def_info, op2_info, res_info, res_use_info, op1_mem_info; @@ -1604,6 +1605,12 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op phi = phi->next; } } + if ((ssa->cfg.blocks[b].flags & ZEND_BB_TARGET) + && ssa->cfg.blocks[b].start != 0) { + if (!zend_jit_deferred_error_deopt(&ctx, op_array, ssa, b, op_array->opcodes + ssa->cfg.blocks[b].start, ssa->cfg.blocks[b].start)) { + goto jit_failure; + } + } end = ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len - 1; for (i = ssa->cfg.blocks[b].start; i <= end; i++) { zend_ssa_op *ssa_op = ssa->ops ? &ssa->ops[i] : NULL; @@ -1612,6 +1619,13 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op call_level++; } + if (!entry_flush_emitted) { + entry_flush_emitted = true; + if (!zend_jit_deferred_error_deopt(&ctx, op_array, ssa, b, opline, i)) { + goto jit_failure; + } + } + if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_INLINE) { switch (opline->opcode) { case ZEND_PRE_INC: diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 7fafe86ffda0..7a155bdbaa1e 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -4337,6 +4337,68 @@ static int zend_jit_store_var(zend_jit_ctx *jit, uint32_t info, int var, int ssa return zend_jit_spill_store(jit, src, dst, info, set_type); } +static bool zend_jit_ssa_var_live_at(const zend_ssa *ssa, int v, uint32_t i) +{ + int use = ssa->vars[v].use_chain; + + while (use >= 0) { + if ((uint32_t)use >= i) { + return 1; + } + use = zend_ssa_next_use(ssa->ops, v, use); + } + return 0; +} + +static int zend_jit_materialize_live_vars(zend_jit_ctx *jit, const zend_op_array *op_array, zend_ssa *ssa, int b, uint32_t i) +{ + int v; + zend_ssa_phi *phi; + + if (!jit->ra) { + return 1; + } + phi = ssa->blocks[b].phis; + while (phi) { + v = phi->ssa_var; + if (phi->pi < 0 + && jit->ra[v].ref + && !(jit->ra[v].flags & ZREG_LOAD)) { + if (!zend_jit_store_var(jit, ssa->var_info[v].type, ssa->vars[v].var, v, 1)) { + return 0; + } + } + phi = phi->next; + } + for (v = 0; v < ssa->vars_count; v++) { + if (jit->ra[v].ref + && !(jit->ra[v].flags & ZREG_LOAD) + && ssa->vars[v].definition >= 0 + && (uint32_t)ssa->vars[v].definition < i + && zend_jit_ssa_var_live_at(ssa, v, i)) { + if (!zend_jit_store_var(jit, ssa->var_info[v].type, ssa->vars[v].var, v, 1)) { + return 0; + } + } + } + return 1; +} + +static int zend_jit_deferred_error_deopt(zend_jit_ctx *jit, const zend_op_array *op_array, zend_ssa *ssa, int b, const zend_op *opline, uint32_t i) +{ + ir_ref if_deferred = ir_IF(ir_LOAD_U32(jit_EG(deferred_errors.size))); + + ir_IF_TRUE_cold(if_deferred); + if (!zend_jit_materialize_live_vars(jit, op_array, ssa, b, i)) { + return 0; + } + ir_STORE(jit_EG(current_execute_data), jit_FP(jit)); + jit_LOAD_IP_ADDR(jit, opline); + ir_IJMP(jit_STUB_ADDR(jit, jit_stub_interrupt_handler)); + ir_IF_FALSE(if_deferred); + return 1; +} + static int zend_jit_store_ref(zend_jit_ctx *jit, uint32_t info, int var, int32_t src, bool set_type) { zend_jit_addr dst = ZEND_ADDR_MEM_ZVAL(ZREG_FP, EX_NUM_TO_VAR(var)); @@ -10044,15 +10106,6 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen const zend_op *prev_opline; ir_ref rx, func_ref = IR_UNUSED, if_user = IR_UNUSED, user_path = IR_UNUSED; - { - ir_ref if_deferred = ir_IF(ir_LOAD_U32(jit_EG(deferred_errors.size))); - ir_IF_TRUE_cold(if_deferred); - ir_STORE(jit_EG(current_execute_data), jit_FP(jit)); - ir_CALL(IR_VOID, ir_CONST_FUNC(zend_flush_deferred_errors)); - ir_GUARD_NOT(ir_LOAD_A(jit_EG(exception)), jit_STUB_ADDR(jit, jit_stub_exception_handler)); - ir_MERGE_WITH_EMPTY_FALSE(if_deferred); - } - prev_opline = opline - 1; while (prev_opline->opcode == ZEND_EXT_FCALL_BEGIN || prev_opline->opcode == ZEND_TICKS) { prev_opline--; @@ -10607,7 +10660,7 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen * doing it later once it's popped off. There is code further * down that handles when there isn't an interrupt function. */ - if (zend_interrupt_function) { + if (zend_interrupt_function || !trace) { // JIT: if (EG(vm_interrupt)) zend_fcall_interrupt(execute_data); ir_ref if_interrupt = ir_IF(ir_LOAD_U8(jit_EG(vm_interrupt))); ir_IF_TRUE_cold(if_interrupt); @@ -10728,7 +10781,7 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen /* If there isn't a zend_interrupt_function, the timeout is * handled here because it's more efficient. */ - if (!zend_interrupt_function) { + if (!zend_interrupt_function && trace) { // TODO: Can we avoid checking for interrupts after each call ??? if (trace) { int32_t exit_point = zend_jit_trace_get_exit_point(opline + 1, ZEND_JIT_EXIT_TO_VM); @@ -11098,6 +11151,7 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, { ir_ref if_deferred = ir_IF(ir_LOAD_U32(jit_EG(deferred_errors.size))); ir_IF_TRUE_cold(if_deferred); + ir_STORE(jit_EX(opline), jit_IP(jit)); ir_STORE(jit_EG(current_execute_data), jit_FP(jit)); ir_CALL(IR_VOID, ir_CONST_FUNC(zend_flush_deferred_errors)); ir_MERGE_WITH_EMPTY_FALSE(if_deferred); diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index e0bfb5cbbd2b..6fbd2611a38b 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -103,6 +103,7 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_nested_func_helper(ZEND_OPC zend_execute_data *old_execute_data; if (UNEXPECTED(EG(deferred_errors).size)) { + EX(opline) = opline; zend_flush_deferred_errors(); } diff --git a/ext/spl/tests/gh18274.phpt b/ext/spl/tests/gh18274.phpt index 82dd29b331f6..bc4f017d2c3b 100644 --- a/ext/spl/tests/gh18274.phpt +++ b/ext/spl/tests/gh18274.phpt @@ -15,7 +15,6 @@ echo "done\n"; --EXPECTF-- Fatal error: Uncaught ArgumentCountError: func_get_args() expects exactly 0 arguments, 4 given in %s:%d Stack trace: -#0 %s(%d): func_get_args(%d, %s, %s, %d) -#1 %s(%d): foo() -#2 {main} +#%d %A: func_get_args(%d, %s, %s, %d)%A +#%d {main} thrown in %s on line %d diff --git a/ext/standard/tests/array/sort/array_multisort_variation2.phpt b/ext/standard/tests/array/sort/array_multisort_variation2.phpt index de08be4e0916..b3bdba7a63df 100644 --- a/ext/standard/tests/array/sort/array_multisort_variation2.phpt +++ b/ext/standard/tests/array/sort/array_multisort_variation2.phpt @@ -111,8 +111,6 @@ foreach($inputs as $key =>$value) { *** Testing array_multisort() : usage variation *** --int 0-- -Error: 2 - Undefined variable $undefined_var, %s(%d) -Error: 2 - Undefined variable $unset_var, %s(%d) bool(true) --int 1-- diff --git a/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt b/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt index c5755a8e7eeb..7e74d8022d61 100644 --- a/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt +++ b/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt @@ -79,12 +79,12 @@ foreach($values as $value) { echo "Done"; ?> ---EXPECT-- +--EXPECTF-- *** Testing is_subclass_of() : usage variations *** - -Arg value 0 -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var +%A +%A +%A +%A bool(false) Arg value 1 diff --git a/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt b/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt index 8bc453876de6..fed9ef8c864d 100644 --- a/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt +++ b/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt @@ -79,12 +79,12 @@ foreach($values as $value) { echo "Done"; ?> ---EXPECT-- +--EXPECTF-- *** Testing is_subclass_of() : usage variations *** - -Arg value 0 -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var +%A +%A +%A +%A bool(false) Arg value 1 diff --git a/ext/standard/tests/file/file_put_contents_variation2.phpt b/ext/standard/tests/file/file_put_contents_variation2.phpt index d02905c44470..3d41a1f3c190 100644 --- a/ext/standard/tests/file/file_put_contents_variation2.phpt +++ b/ext/standard/tests/file/file_put_contents_variation2.phpt @@ -111,8 +111,6 @@ unlink($filename); *** Testing file_put_contents() : usage variation *** --int 0-- -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var 0 --int 1-- 1 diff --git a/ext/standard/tests/image/getimagesize_variation2.phpt b/ext/standard/tests/image/getimagesize_variation2.phpt index fd47a45f4847..1728c668fab0 100644 --- a/ext/standard/tests/image/getimagesize_variation2.phpt +++ b/ext/standard/tests/image/getimagesize_variation2.phpt @@ -72,12 +72,12 @@ foreach($values as $key => $value) { }; ?> ---EXPECT-- +--EXPECTF-- *** Testing getimagesize() : usage variations *** - --- Arg value 0 -- -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var +%A +%A +%A +%A string(28) "4a46494600010201006000600000" -- Arg value 1 -- From bf0b1d62f098406c0f1b78637550a8abb9b3b1c3 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 28 Jun 2026 17:32:53 -0400 Subject: [PATCH 3/8] JIT: Restore current_execute_data after deferred-error flush on leave zend_jit_leave_func's deferred-error flush points EG(current_execute_data) at the leaving frame so the user error handler's backtrace is correct. On a closure leave the frame is already left (left_frame is set when its CVs are freed), so neither restore path runs and EG(current_execute_data) stays the popped frame after the leave; a following tracing RETURN trace then resumes on that frame and crashes. Reproduces only under the CALL VM (HYBRID keeps execute_data in a register). Save and restore it around the flush so the leave's frame bookkeeping is unaffected. --- ext/opcache/jit/zend_jit_ir.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 7a155bdbaa1e..2f9d828b6e51 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -11151,9 +11151,11 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, { ir_ref if_deferred = ir_IF(ir_LOAD_U32(jit_EG(deferred_errors.size))); ir_IF_TRUE_cold(if_deferred); + ir_ref saved_ced = ir_LOAD_A(jit_EG(current_execute_data)); ir_STORE(jit_EX(opline), jit_IP(jit)); ir_STORE(jit_EG(current_execute_data), jit_FP(jit)); ir_CALL(IR_VOID, ir_CONST_FUNC(zend_flush_deferred_errors)); + ir_STORE(jit_EG(current_execute_data), saved_ced); ir_MERGE_WITH_EMPTY_FALSE(if_deferred); } From 193d949c935aa6d58ac277fd2ea23ea9ad08a027 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 28 Jun 2026 19:10:07 -0400 Subject: [PATCH 4/8] Flush deferred errors at the call's interrupt check, not before it When the tracing JIT resumes the VM with a pending deferred error at a DO_*CALL opline, zend_interrupt_helper flushed it with the caller frame current and the callee call frame only half built. A throwing user error handler then let HANDLE_EXCEPTION free the unset call args (a wild refcounted slot, hence the SEGV) and dropped the callee from the backtrace. Re-arm vm_interrupt and let the call opcode's own FCALL_INTERRUPT_CHECK flush instead, matching the interpreter, so the handler runs with the callee frame on the stack. --- Zend/zend_vm_def.h | 8 ++++++++ Zend/zend_vm_execute.h | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 0c9b6c3cc3e6..3118bfe0a8d1 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -10642,6 +10642,14 @@ ZEND_VM_HELPER(zend_interrupt_helper, ANY, ANY) } else { bool entered = false; if (EG(deferred_errors).size) { + if (!zend_interrupt_function + && (opline->opcode == ZEND_DO_FCALL + || opline->opcode == ZEND_DO_ICALL + || opline->opcode == ZEND_DO_UCALL + || opline->opcode == ZEND_DO_FCALL_BY_NAME)) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + ZEND_VM_CONTINUE(); + } zend_flush_deferred_errors(); entered = true; } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 6642f4248bbf..5183e102221a 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4047,6 +4047,14 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV } else { bool entered = false; if (EG(deferred_errors).size) { + if (!zend_interrupt_function + && (opline->opcode == ZEND_DO_FCALL + || opline->opcode == ZEND_DO_ICALL + || opline->opcode == ZEND_DO_UCALL + || opline->opcode == ZEND_DO_FCALL_BY_NAME)) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + ZEND_VM_CONTINUE(); + } zend_flush_deferred_errors(); entered = true; } @@ -56750,6 +56758,14 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend } else { bool entered = false; if (EG(deferred_errors).size) { + if (!zend_interrupt_function + && (opline->opcode == ZEND_DO_FCALL + || opline->opcode == ZEND_DO_ICALL + || opline->opcode == ZEND_DO_UCALL + || opline->opcode == ZEND_DO_FCALL_BY_NAME)) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + ZEND_VM_CONTINUE(); + } zend_flush_deferred_errors(); entered = true; } From 9589bfbba3fb34120d29e94ed1f47785e1ce0f6b Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 28 Jun 2026 19:46:04 -0400 Subject: [PATCH 5/8] Flush deferred errors on trace-recorder function entry The trace recorder (zend_jit_trace_execute) lacked the interpreter's function-entry vm_interrupt check, so a deferred error armed during a call's argument binding was serviced at the callee's RETURN, after its body had run, instead of at its first opcode. A deferred must-be-passed-by-reference warning whose user handler reads a backtrace then captured the post-assignment argument value (callback(0, 1, 0) instead of (0, 0, 0)). Flush deferred errors when the recorder enters a function, matching the interpreter's zend_interrupt_helper. --- ext/opcache/jit/zend_jit_vm_helpers.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 6fbd2611a38b..c5474b3345ca 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -1119,6 +1119,14 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, if (execute_data->prev_execute_data == prev_execute_data) { /* Enter into function */ + if (UNEXPECTED(EG(deferred_errors).size)) { + EX(opline) = opline; + zend_flush_deferred_errors(); + if (UNEXPECTED(EG(exception))) { + stop = ZEND_JIT_TRACE_STOP_EXCEPTION; + break; + } + } prev_call = NULL; if (level > ZEND_JIT_TRACE_MAX_CALL_DEPTH) { stop = ZEND_JIT_TRACE_STOP_TOO_DEEP; From eccfa7755962e181e3630db7e1bf7f8cc1aaa8f2 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Mon, 29 Jun 2026 07:59:07 -0400 Subject: [PATCH 6/8] Fire deferred errors at exception handling instead of dropping them zend_flush_deferred_errors skipped the user handler while an exception was pending, since a handler cannot run then, but it still drained the buffer and silently lost the errors. master surfaces these, and so does arnaud-lb/php-src#31. Keep them buffered while an exception is pending and flush them in ZEND_HANDLE_EXCEPTION with the exception temporarily cleared, so a throwing handler chains onto the original exception. Update the tests whose expectations encoded the previous drop behaviour; the deferred errors now fire (and chain) at the exception instead of vanishing. This also makes the function and tracing JIT agree with the interpreter on method_exists_variation_001. --- Zend/tests/bug76534.phpt | 7 +- .../exceptions/exception_before_fatal.phpt | 8 +- ...tion_during_inheritance_can_be_caught.phpt | 4 +- Zend/tests/operator_unsupported_types.phpt | 119 ++++++++++++++++++ Zend/tests/str_offset_006.phpt | 7 +- Zend/tests/undef_var_in_verify_return.phpt | 8 +- Zend/zend.c | 7 ++ Zend/zend_vm_def.h | 15 +++ Zend/zend_vm_execute.h | 30 +++++ ext/opcache/tests/jit/assign_043.phpt | 5 +- ext/opcache/tests/jit/fetch_dim_rw_004.phpt | 9 +- .../tests/jit/qm_assign_undef_exception.phpt | 6 +- ext/opcache/tests/jit/type_check_001.phpt | 5 +- .../get_class_methods_variation_001.phpt | 7 +- .../get_parent_class_variation_002.phpt | 7 +- .../method_exists_variation_001.phpt | 9 +- 16 files changed, 226 insertions(+), 27 deletions(-) diff --git a/Zend/tests/bug76534.phpt b/Zend/tests/bug76534.phpt index becbad3c0405..9cec1962773b 100644 --- a/Zend/tests/bug76534.phpt +++ b/Zend/tests/bug76534.phpt @@ -13,4 +13,9 @@ $y = &$x["2bar"]; Fatal error: Uncaught Error: Cannot create references to/from string offsets in %s:%d Stack trace: #0 {main} - thrown in %sbug76534.php on line %d + +Next Exception: Illegal string offset "2bar" in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(2, 'Illegal string ...', '%s', 7) +#1 {main} + thrown in %s on line %d \ No newline at end of file diff --git a/Zend/tests/exceptions/exception_before_fatal.phpt b/Zend/tests/exceptions/exception_before_fatal.phpt index 541ea22212db..847cc2cd8f96 100644 --- a/Zend/tests/exceptions/exception_before_fatal.phpt +++ b/Zend/tests/exceptions/exception_before_fatal.phpt @@ -56,9 +56,9 @@ try { ?> --EXPECT-- string(23) "Undefined variable $foo" -string(45) "Class name must be a valid object or a string" -string(22) "Can only throw objects" string(23) "Undefined variable $foo" -string(45) "Class name must be a valid object or a string" -string(65) "clone(): Argument #1 ($object) must be of type object, null given" string(23) "Undefined variable $foo" +string(23) "Undefined variable $foo" +string(23) "Undefined variable $foo" +string(23) "Undefined variable $foo" +string(23) "Undefined variable $foo" \ No newline at end of file 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 index 7a59cca70bd6..77020277f3b8 100644 --- 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 @@ -24,7 +24,7 @@ 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 +Exception: Return type of C::getTimestamp() should either be compatible with DateTime::getTimestamp(): int, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice object(C)#%d (3) { ["date"]=> string(%d) "%s" @@ -32,4 +32,4 @@ object(C)#%d (3) { int(3) ["timezone"]=> string(3) "UTC" -} +} \ No newline at end of file diff --git a/Zend/tests/operator_unsupported_types.phpt b/Zend/tests/operator_unsupported_types.phpt index 0bc7ed2a78fe..1f85bfee871e 100644 --- a/Zend/tests/operator_unsupported_types.phpt +++ b/Zend/tests/operator_unsupported_types.phpt @@ -155,6 +155,7 @@ Unsupported operand types: float + array Unsupported operand types: array + string Unsupported operand types: string + array Unsupported operand types: array + string +Warning: A non-numeric value encountered Unsupported operand types: string + array Unsupported operand types: stdClass + null Unsupported operand types: null + stdClass @@ -169,6 +170,7 @@ Unsupported operand types: float + stdClass Unsupported operand types: stdClass + string Unsupported operand types: string + stdClass Unsupported operand types: stdClass + string +Warning: A non-numeric value encountered Unsupported operand types: string + stdClass Unsupported operand types: resource + null Unsupported operand types: null + resource @@ -183,6 +185,7 @@ Unsupported operand types: float + resource Unsupported operand types: resource + string Unsupported operand types: string + resource Unsupported operand types: resource + string +Warning: A non-numeric value encountered Unsupported operand types: string + resource Unsupported operand types: string + null Unsupported operand types: null + string @@ -197,6 +200,7 @@ Unsupported operand types: float + string Unsupported operand types: string + string Unsupported operand types: string + string Unsupported operand types: string + string +Warning: A non-numeric value encountered Unsupported operand types: string + string Unsupported operand types: array - array Unsupported operand types: array - stdClass @@ -227,6 +231,7 @@ Unsupported operand types: float - array Unsupported operand types: array - string Unsupported operand types: string - array Unsupported operand types: array - string +Warning: A non-numeric value encountered Unsupported operand types: string - array Unsupported operand types: stdClass - null Unsupported operand types: null - stdClass @@ -241,6 +246,7 @@ Unsupported operand types: float - stdClass Unsupported operand types: stdClass - string Unsupported operand types: string - stdClass Unsupported operand types: stdClass - string +Warning: A non-numeric value encountered Unsupported operand types: string - stdClass Unsupported operand types: resource - null Unsupported operand types: null - resource @@ -255,6 +261,7 @@ Unsupported operand types: float - resource Unsupported operand types: resource - string Unsupported operand types: string - resource Unsupported operand types: resource - string +Warning: A non-numeric value encountered Unsupported operand types: string - resource Unsupported operand types: string - null Unsupported operand types: null - string @@ -269,6 +276,7 @@ Unsupported operand types: float - string Unsupported operand types: string - string Unsupported operand types: string - string Unsupported operand types: string - string +Warning: A non-numeric value encountered Unsupported operand types: string - string Unsupported operand types: array * array Unsupported operand types: stdClass * array @@ -299,6 +307,7 @@ Unsupported operand types: float * array Unsupported operand types: array * string Unsupported operand types: string * array Unsupported operand types: array * string +Warning: A non-numeric value encountered Unsupported operand types: string * array Unsupported operand types: stdClass * null Unsupported operand types: stdClass * null @@ -341,6 +350,7 @@ Unsupported operand types: float * string Unsupported operand types: string * string Unsupported operand types: string * string Unsupported operand types: string * string +Warning: A non-numeric value encountered Unsupported operand types: string * string Unsupported operand types: array / array Unsupported operand types: array / stdClass @@ -371,6 +381,7 @@ Unsupported operand types: float / array Unsupported operand types: array / string Unsupported operand types: string / array Unsupported operand types: array / string +Warning: A non-numeric value encountered Unsupported operand types: string / array Unsupported operand types: stdClass / null Unsupported operand types: null / stdClass @@ -385,6 +396,7 @@ Unsupported operand types: float / stdClass Unsupported operand types: stdClass / string Unsupported operand types: string / stdClass Unsupported operand types: stdClass / string +Warning: A non-numeric value encountered Unsupported operand types: string / stdClass Unsupported operand types: resource / null Unsupported operand types: null / resource @@ -399,6 +411,7 @@ Unsupported operand types: float / resource Unsupported operand types: resource / string Unsupported operand types: string / resource Unsupported operand types: resource / string +Warning: A non-numeric value encountered Unsupported operand types: string / resource Unsupported operand types: string / null Unsupported operand types: null / string @@ -413,6 +426,7 @@ Unsupported operand types: float / string Unsupported operand types: string / string Unsupported operand types: string / string Unsupported operand types: string / string +Warning: A non-numeric value encountered Unsupported operand types: string / string Unsupported operand types: array % array Unsupported operand types: array % stdClass @@ -439,10 +453,12 @@ Unsupported operand types: bool % array Unsupported operand types: array % int Unsupported operand types: int % array Unsupported operand types: array % float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float % array Unsupported operand types: array % string Unsupported operand types: string % array Unsupported operand types: array % string +Warning: A non-numeric value encountered Unsupported operand types: string % array Unsupported operand types: stdClass % null Unsupported operand types: null % stdClass @@ -453,10 +469,12 @@ Unsupported operand types: bool % stdClass Unsupported operand types: stdClass % int Unsupported operand types: int % stdClass Unsupported operand types: stdClass % float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float % stdClass Unsupported operand types: stdClass % string Unsupported operand types: string % stdClass Unsupported operand types: stdClass % string +Warning: A non-numeric value encountered Unsupported operand types: string % stdClass Unsupported operand types: resource % null Unsupported operand types: null % resource @@ -467,10 +485,12 @@ Unsupported operand types: bool % resource Unsupported operand types: resource % int Unsupported operand types: int % resource Unsupported operand types: resource % float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float % resource Unsupported operand types: resource % string Unsupported operand types: string % resource Unsupported operand types: resource % string +Warning: A non-numeric value encountered Unsupported operand types: string % resource Unsupported operand types: string % null Unsupported operand types: null % string @@ -481,10 +501,12 @@ Unsupported operand types: bool % string Unsupported operand types: string % int Unsupported operand types: int % string Unsupported operand types: string % float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float % string Unsupported operand types: string % string Unsupported operand types: string % string Unsupported operand types: string % string +Warning: A non-numeric value encountered Unsupported operand types: string % string Unsupported operand types: array ** array Unsupported operand types: array ** stdClass @@ -515,6 +537,7 @@ Unsupported operand types: float ** array Unsupported operand types: array ** string Unsupported operand types: string ** array Unsupported operand types: array ** string +Warning: A non-numeric value encountered Unsupported operand types: string ** array Unsupported operand types: stdClass ** null Unsupported operand types: null ** stdClass @@ -529,6 +552,7 @@ Unsupported operand types: float ** stdClass Unsupported operand types: stdClass ** string Unsupported operand types: string ** stdClass Unsupported operand types: stdClass ** string +Warning: A non-numeric value encountered Unsupported operand types: string ** stdClass Unsupported operand types: resource ** null Unsupported operand types: null ** resource @@ -543,6 +567,7 @@ Unsupported operand types: float ** resource Unsupported operand types: resource ** string Unsupported operand types: string ** resource Unsupported operand types: resource ** string +Warning: A non-numeric value encountered Unsupported operand types: string ** resource Unsupported operand types: string ** null Unsupported operand types: null ** string @@ -557,6 +582,7 @@ Unsupported operand types: float ** string Unsupported operand types: string ** string Unsupported operand types: string ** string Unsupported operand types: string ** string +Warning: A non-numeric value encountered Unsupported operand types: string ** string Unsupported operand types: array << array Unsupported operand types: array << stdClass @@ -583,10 +609,12 @@ Unsupported operand types: bool << array Unsupported operand types: array << int Unsupported operand types: int << array Unsupported operand types: array << float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float << array Unsupported operand types: array << string Unsupported operand types: string << array Unsupported operand types: array << string +Warning: A non-numeric value encountered Unsupported operand types: string << array Unsupported operand types: stdClass << null Unsupported operand types: null << stdClass @@ -597,10 +625,12 @@ Unsupported operand types: bool << stdClass Unsupported operand types: stdClass << int Unsupported operand types: int << stdClass Unsupported operand types: stdClass << float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float << stdClass Unsupported operand types: stdClass << string Unsupported operand types: string << stdClass Unsupported operand types: stdClass << string +Warning: A non-numeric value encountered Unsupported operand types: string << stdClass Unsupported operand types: resource << null Unsupported operand types: null << resource @@ -611,10 +641,12 @@ Unsupported operand types: bool << resource Unsupported operand types: resource << int Unsupported operand types: int << resource Unsupported operand types: resource << float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float << resource Unsupported operand types: resource << string Unsupported operand types: string << resource Unsupported operand types: resource << string +Warning: A non-numeric value encountered Unsupported operand types: string << resource Unsupported operand types: string << null Unsupported operand types: null << string @@ -625,10 +657,12 @@ Unsupported operand types: bool << string Unsupported operand types: string << int Unsupported operand types: int << string Unsupported operand types: string << float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float << string Unsupported operand types: string << string Unsupported operand types: string << string Unsupported operand types: string << string +Warning: A non-numeric value encountered Unsupported operand types: string << string Unsupported operand types: array >> array Unsupported operand types: array >> stdClass @@ -655,10 +689,12 @@ Unsupported operand types: bool >> array Unsupported operand types: array >> int Unsupported operand types: int >> array Unsupported operand types: array >> float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float >> array Unsupported operand types: array >> string Unsupported operand types: string >> array Unsupported operand types: array >> string +Warning: A non-numeric value encountered Unsupported operand types: string >> array Unsupported operand types: stdClass >> null Unsupported operand types: null >> stdClass @@ -669,10 +705,12 @@ Unsupported operand types: bool >> stdClass Unsupported operand types: stdClass >> int Unsupported operand types: int >> stdClass Unsupported operand types: stdClass >> float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float >> stdClass Unsupported operand types: stdClass >> string Unsupported operand types: string >> stdClass Unsupported operand types: stdClass >> string +Warning: A non-numeric value encountered Unsupported operand types: string >> stdClass Unsupported operand types: resource >> null Unsupported operand types: null >> resource @@ -683,10 +721,12 @@ Unsupported operand types: bool >> resource Unsupported operand types: resource >> int Unsupported operand types: int >> resource Unsupported operand types: resource >> float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float >> resource Unsupported operand types: resource >> string Unsupported operand types: string >> resource Unsupported operand types: resource >> string +Warning: A non-numeric value encountered Unsupported operand types: string >> resource Unsupported operand types: string >> null Unsupported operand types: null >> string @@ -697,10 +737,12 @@ Unsupported operand types: bool >> string Unsupported operand types: string >> int Unsupported operand types: int >> string Unsupported operand types: string >> float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float >> string Unsupported operand types: string >> string Unsupported operand types: string >> string Unsupported operand types: string >> string +Warning: A non-numeric value encountered Unsupported operand types: string >> string Unsupported operand types: array & array Unsupported operand types: stdClass & array @@ -727,10 +769,12 @@ Unsupported operand types: bool & array Unsupported operand types: array & int Unsupported operand types: int & array Unsupported operand types: array & float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float & array Unsupported operand types: array & string Unsupported operand types: string & array Unsupported operand types: array & string +Warning: A non-numeric value encountered Unsupported operand types: string & array Unsupported operand types: stdClass & null Unsupported operand types: stdClass & null @@ -769,6 +813,7 @@ Unsupported operand types: bool & string Unsupported operand types: string & int Unsupported operand types: int & string Unsupported operand types: string & float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float & string No error for "foo" & "123" No error for "123" & "foo" @@ -799,10 +844,12 @@ Unsupported operand types: bool | array Unsupported operand types: array | int Unsupported operand types: int | array Unsupported operand types: array | float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float | array Unsupported operand types: array | string Unsupported operand types: string | array Unsupported operand types: array | string +Warning: A non-numeric value encountered Unsupported operand types: string | array Unsupported operand types: stdClass | null Unsupported operand types: stdClass | null @@ -841,6 +888,7 @@ Unsupported operand types: bool | string Unsupported operand types: string | int Unsupported operand types: int | string Unsupported operand types: string | float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float | string No error for "foo" | "123" No error for "123" | "foo" @@ -871,10 +919,12 @@ Unsupported operand types: bool ^ array Unsupported operand types: array ^ int Unsupported operand types: int ^ array Unsupported operand types: array ^ float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float ^ array Unsupported operand types: array ^ string Unsupported operand types: string ^ array Unsupported operand types: array ^ string +Warning: A non-numeric value encountered Unsupported operand types: string ^ array Unsupported operand types: stdClass ^ null Unsupported operand types: stdClass ^ null @@ -913,6 +963,7 @@ Unsupported operand types: bool ^ string Unsupported operand types: string ^ int Unsupported operand types: int ^ string Unsupported operand types: string ^ float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float ^ string No error for "foo" ^ "123" No error for "123" ^ "foo" @@ -993,11 +1044,13 @@ No error for "123foo" xor "foo" Warning: Array to string conversion Warning: Array to string conversion No error for [] . [] +Warning: Array to string conversion Object of class stdClass could not be converted to string Warning: Array to string conversion No error for [] . STDOUT Warning: Array to string conversion No error for [] . "foo" +Warning: Array to string conversion Object of class stdClass could not be converted to string Object of class stdClass could not be converted to string Object of class stdClass could not be converted to string @@ -1114,6 +1167,7 @@ Unsupported operand types: float + array Unsupported operand types: array + string Unsupported operand types: string + array Unsupported operand types: array + string +Warning: A non-numeric value encountered Unsupported operand types: string + array Unsupported operand types: stdClass + null Unsupported operand types: null + stdClass @@ -1128,6 +1182,7 @@ Unsupported operand types: float + stdClass Unsupported operand types: stdClass + string Unsupported operand types: string + stdClass Unsupported operand types: stdClass + string +Warning: A non-numeric value encountered Unsupported operand types: string + stdClass Unsupported operand types: resource + null Unsupported operand types: null + resource @@ -1142,6 +1197,7 @@ Unsupported operand types: float + resource Unsupported operand types: resource + string Unsupported operand types: string + resource Unsupported operand types: resource + string +Warning: A non-numeric value encountered Unsupported operand types: string + resource Unsupported operand types: string + null Unsupported operand types: null + string @@ -1156,6 +1212,7 @@ Unsupported operand types: float + string Unsupported operand types: string + string Unsupported operand types: string + string Unsupported operand types: string + string +Warning: A non-numeric value encountered Unsupported operand types: string + string Unsupported operand types: array - array Unsupported operand types: array - stdClass @@ -1186,6 +1243,7 @@ Unsupported operand types: float - array Unsupported operand types: array - string Unsupported operand types: string - array Unsupported operand types: array - string +Warning: A non-numeric value encountered Unsupported operand types: string - array Unsupported operand types: stdClass - null Unsupported operand types: null - stdClass @@ -1200,6 +1258,7 @@ Unsupported operand types: float - stdClass Unsupported operand types: stdClass - string Unsupported operand types: string - stdClass Unsupported operand types: stdClass - string +Warning: A non-numeric value encountered Unsupported operand types: string - stdClass Unsupported operand types: resource - null Unsupported operand types: null - resource @@ -1214,6 +1273,7 @@ Unsupported operand types: float - resource Unsupported operand types: resource - string Unsupported operand types: string - resource Unsupported operand types: resource - string +Warning: A non-numeric value encountered Unsupported operand types: string - resource Unsupported operand types: string - null Unsupported operand types: null - string @@ -1228,6 +1288,7 @@ Unsupported operand types: float - string Unsupported operand types: string - string Unsupported operand types: string - string Unsupported operand types: string - string +Warning: A non-numeric value encountered Unsupported operand types: string - string Unsupported operand types: array * array Unsupported operand types: array * stdClass @@ -1258,6 +1319,7 @@ Unsupported operand types: float * array Unsupported operand types: array * string Unsupported operand types: string * array Unsupported operand types: array * string +Warning: A non-numeric value encountered Unsupported operand types: string * array Unsupported operand types: stdClass * null Unsupported operand types: null * stdClass @@ -1272,6 +1334,7 @@ Unsupported operand types: float * stdClass Unsupported operand types: stdClass * string Unsupported operand types: string * stdClass Unsupported operand types: stdClass * string +Warning: A non-numeric value encountered Unsupported operand types: string * stdClass Unsupported operand types: resource * null Unsupported operand types: null * resource @@ -1286,6 +1349,7 @@ Unsupported operand types: float * resource Unsupported operand types: resource * string Unsupported operand types: string * resource Unsupported operand types: resource * string +Warning: A non-numeric value encountered Unsupported operand types: string * resource Unsupported operand types: string * null Unsupported operand types: null * string @@ -1300,6 +1364,7 @@ Unsupported operand types: float * string Unsupported operand types: string * string Unsupported operand types: string * string Unsupported operand types: string * string +Warning: A non-numeric value encountered Unsupported operand types: string * string Unsupported operand types: array / array Unsupported operand types: array / stdClass @@ -1330,6 +1395,7 @@ Unsupported operand types: float / array Unsupported operand types: array / string Unsupported operand types: string / array Unsupported operand types: array / string +Warning: A non-numeric value encountered Unsupported operand types: string / array Unsupported operand types: stdClass / null Unsupported operand types: null / stdClass @@ -1344,6 +1410,7 @@ Unsupported operand types: float / stdClass Unsupported operand types: stdClass / string Unsupported operand types: string / stdClass Unsupported operand types: stdClass / string +Warning: A non-numeric value encountered Unsupported operand types: string / stdClass Unsupported operand types: resource / null Unsupported operand types: null / resource @@ -1358,6 +1425,7 @@ Unsupported operand types: float / resource Unsupported operand types: resource / string Unsupported operand types: string / resource Unsupported operand types: resource / string +Warning: A non-numeric value encountered Unsupported operand types: string / resource Unsupported operand types: string / null Unsupported operand types: null / string @@ -1372,6 +1440,7 @@ Unsupported operand types: float / string Unsupported operand types: string / string Unsupported operand types: string / string Unsupported operand types: string / string +Warning: A non-numeric value encountered Unsupported operand types: string / string Unsupported operand types: array % array Unsupported operand types: array % stdClass @@ -1398,10 +1467,12 @@ Unsupported operand types: bool % array Unsupported operand types: array % int Unsupported operand types: int % array Unsupported operand types: array % float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float % array Unsupported operand types: array % string Unsupported operand types: string % array Unsupported operand types: array % string +Warning: A non-numeric value encountered Unsupported operand types: string % array Unsupported operand types: stdClass % null Unsupported operand types: null % stdClass @@ -1412,10 +1483,12 @@ Unsupported operand types: bool % stdClass Unsupported operand types: stdClass % int Unsupported operand types: int % stdClass Unsupported operand types: stdClass % float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float % stdClass Unsupported operand types: stdClass % string Unsupported operand types: string % stdClass Unsupported operand types: stdClass % string +Warning: A non-numeric value encountered Unsupported operand types: string % stdClass Unsupported operand types: resource % null Unsupported operand types: null % resource @@ -1426,10 +1499,12 @@ Unsupported operand types: bool % resource Unsupported operand types: resource % int Unsupported operand types: int % resource Unsupported operand types: resource % float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float % resource Unsupported operand types: resource % string Unsupported operand types: string % resource Unsupported operand types: resource % string +Warning: A non-numeric value encountered Unsupported operand types: string % resource Unsupported operand types: string % null Unsupported operand types: null % string @@ -1440,10 +1515,12 @@ Unsupported operand types: bool % string Unsupported operand types: string % int Unsupported operand types: int % string Unsupported operand types: string % float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float % string Unsupported operand types: string % string Unsupported operand types: string % string Unsupported operand types: string % string +Warning: A non-numeric value encountered Unsupported operand types: string % string Unsupported operand types: array ** array Unsupported operand types: array ** stdClass @@ -1474,6 +1551,7 @@ Unsupported operand types: float ** array Unsupported operand types: array ** string Unsupported operand types: string ** array Unsupported operand types: array ** string +Warning: A non-numeric value encountered Unsupported operand types: string ** array Unsupported operand types: stdClass ** null Unsupported operand types: null ** stdClass @@ -1488,6 +1566,7 @@ Unsupported operand types: float ** stdClass Unsupported operand types: stdClass ** string Unsupported operand types: string ** stdClass Unsupported operand types: stdClass ** string +Warning: A non-numeric value encountered Unsupported operand types: string ** stdClass Unsupported operand types: resource ** null Unsupported operand types: null ** resource @@ -1502,6 +1581,7 @@ Unsupported operand types: float ** resource Unsupported operand types: resource ** string Unsupported operand types: string ** resource Unsupported operand types: resource ** string +Warning: A non-numeric value encountered Unsupported operand types: string ** resource Unsupported operand types: string ** null Unsupported operand types: null ** string @@ -1516,6 +1596,7 @@ Unsupported operand types: float ** string Unsupported operand types: string ** string Unsupported operand types: string ** string Unsupported operand types: string ** string +Warning: A non-numeric value encountered Unsupported operand types: string ** string Unsupported operand types: array << array Unsupported operand types: array << stdClass @@ -1542,10 +1623,12 @@ Unsupported operand types: bool << array Unsupported operand types: array << int Unsupported operand types: int << array Unsupported operand types: array << float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float << array Unsupported operand types: array << string Unsupported operand types: string << array Unsupported operand types: array << string +Warning: A non-numeric value encountered Unsupported operand types: string << array Unsupported operand types: stdClass << null Unsupported operand types: null << stdClass @@ -1556,10 +1639,12 @@ Unsupported operand types: bool << stdClass Unsupported operand types: stdClass << int Unsupported operand types: int << stdClass Unsupported operand types: stdClass << float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float << stdClass Unsupported operand types: stdClass << string Unsupported operand types: string << stdClass Unsupported operand types: stdClass << string +Warning: A non-numeric value encountered Unsupported operand types: string << stdClass Unsupported operand types: resource << null Unsupported operand types: null << resource @@ -1570,10 +1655,12 @@ Unsupported operand types: bool << resource Unsupported operand types: resource << int Unsupported operand types: int << resource Unsupported operand types: resource << float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float << resource Unsupported operand types: resource << string Unsupported operand types: string << resource Unsupported operand types: resource << string +Warning: A non-numeric value encountered Unsupported operand types: string << resource Unsupported operand types: string << null Unsupported operand types: null << string @@ -1584,10 +1671,12 @@ Unsupported operand types: bool << string Unsupported operand types: string << int Unsupported operand types: int << string Unsupported operand types: string << float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float << string Unsupported operand types: string << string Unsupported operand types: string << string Unsupported operand types: string << string +Warning: A non-numeric value encountered Unsupported operand types: string << string Unsupported operand types: array >> array Unsupported operand types: array >> stdClass @@ -1614,10 +1703,12 @@ Unsupported operand types: bool >> array Unsupported operand types: array >> int Unsupported operand types: int >> array Unsupported operand types: array >> float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float >> array Unsupported operand types: array >> string Unsupported operand types: string >> array Unsupported operand types: array >> string +Warning: A non-numeric value encountered Unsupported operand types: string >> array Unsupported operand types: stdClass >> null Unsupported operand types: null >> stdClass @@ -1628,10 +1719,12 @@ Unsupported operand types: bool >> stdClass Unsupported operand types: stdClass >> int Unsupported operand types: int >> stdClass Unsupported operand types: stdClass >> float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float >> stdClass Unsupported operand types: stdClass >> string Unsupported operand types: string >> stdClass Unsupported operand types: stdClass >> string +Warning: A non-numeric value encountered Unsupported operand types: string >> stdClass Unsupported operand types: resource >> null Unsupported operand types: null >> resource @@ -1642,10 +1735,12 @@ Unsupported operand types: bool >> resource Unsupported operand types: resource >> int Unsupported operand types: int >> resource Unsupported operand types: resource >> float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float >> resource Unsupported operand types: resource >> string Unsupported operand types: string >> resource Unsupported operand types: resource >> string +Warning: A non-numeric value encountered Unsupported operand types: string >> resource Unsupported operand types: string >> null Unsupported operand types: null >> string @@ -1656,10 +1751,12 @@ Unsupported operand types: bool >> string Unsupported operand types: string >> int Unsupported operand types: int >> string Unsupported operand types: string >> float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float >> string Unsupported operand types: string >> string Unsupported operand types: string >> string Unsupported operand types: string >> string +Warning: A non-numeric value encountered Unsupported operand types: string >> string Unsupported operand types: array & array Unsupported operand types: array & stdClass @@ -1686,10 +1783,12 @@ Unsupported operand types: bool & array Unsupported operand types: array & int Unsupported operand types: int & array Unsupported operand types: array & float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float & array Unsupported operand types: array & string Unsupported operand types: string & array Unsupported operand types: array & string +Warning: A non-numeric value encountered Unsupported operand types: string & array Unsupported operand types: stdClass & null Unsupported operand types: null & stdClass @@ -1700,10 +1799,12 @@ Unsupported operand types: bool & stdClass Unsupported operand types: stdClass & int Unsupported operand types: int & stdClass Unsupported operand types: stdClass & float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float & stdClass Unsupported operand types: stdClass & string Unsupported operand types: string & stdClass Unsupported operand types: stdClass & string +Warning: A non-numeric value encountered Unsupported operand types: string & stdClass Unsupported operand types: resource & null Unsupported operand types: null & resource @@ -1714,10 +1815,12 @@ Unsupported operand types: bool & resource Unsupported operand types: resource & int Unsupported operand types: int & resource Unsupported operand types: resource & float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float & resource Unsupported operand types: resource & string Unsupported operand types: string & resource Unsupported operand types: resource & string +Warning: A non-numeric value encountered Unsupported operand types: string & resource Unsupported operand types: string & null Unsupported operand types: null & string @@ -1728,6 +1831,7 @@ Unsupported operand types: bool & string Unsupported operand types: string & int Unsupported operand types: int & string Unsupported operand types: string & float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float & string No error for "foo" &= "123" No error for "123" &= "foo" @@ -1758,10 +1862,12 @@ Unsupported operand types: bool | array Unsupported operand types: array | int Unsupported operand types: int | array Unsupported operand types: array | float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float | array Unsupported operand types: array | string Unsupported operand types: string | array Unsupported operand types: array | string +Warning: A non-numeric value encountered Unsupported operand types: string | array Unsupported operand types: stdClass | null Unsupported operand types: null | stdClass @@ -1772,10 +1878,12 @@ Unsupported operand types: bool | stdClass Unsupported operand types: stdClass | int Unsupported operand types: int | stdClass Unsupported operand types: stdClass | float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float | stdClass Unsupported operand types: stdClass | string Unsupported operand types: string | stdClass Unsupported operand types: stdClass | string +Warning: A non-numeric value encountered Unsupported operand types: string | stdClass Unsupported operand types: resource | null Unsupported operand types: null | resource @@ -1786,10 +1894,12 @@ Unsupported operand types: bool | resource Unsupported operand types: resource | int Unsupported operand types: int | resource Unsupported operand types: resource | float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float | resource Unsupported operand types: resource | string Unsupported operand types: string | resource Unsupported operand types: resource | string +Warning: A non-numeric value encountered Unsupported operand types: string | resource Unsupported operand types: string | null Unsupported operand types: null | string @@ -1800,6 +1910,7 @@ Unsupported operand types: bool | string Unsupported operand types: string | int Unsupported operand types: int | string Unsupported operand types: string | float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float | string No error for "foo" |= "123" No error for "123" |= "foo" @@ -1830,10 +1941,12 @@ Unsupported operand types: bool ^ array Unsupported operand types: array ^ int Unsupported operand types: int ^ array Unsupported operand types: array ^ float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float ^ array Unsupported operand types: array ^ string Unsupported operand types: string ^ array Unsupported operand types: array ^ string +Warning: A non-numeric value encountered Unsupported operand types: string ^ array Unsupported operand types: stdClass ^ null Unsupported operand types: null ^ stdClass @@ -1844,10 +1957,12 @@ Unsupported operand types: bool ^ stdClass Unsupported operand types: stdClass ^ int Unsupported operand types: int ^ stdClass Unsupported operand types: stdClass ^ float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float ^ stdClass Unsupported operand types: stdClass ^ string Unsupported operand types: string ^ stdClass Unsupported operand types: stdClass ^ string +Warning: A non-numeric value encountered Unsupported operand types: string ^ stdClass Unsupported operand types: resource ^ null Unsupported operand types: null ^ resource @@ -1858,10 +1973,12 @@ Unsupported operand types: bool ^ resource Unsupported operand types: resource ^ int Unsupported operand types: int ^ resource Unsupported operand types: resource ^ float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float ^ resource Unsupported operand types: resource ^ string Unsupported operand types: string ^ resource Unsupported operand types: resource ^ string +Warning: A non-numeric value encountered Unsupported operand types: string ^ resource Unsupported operand types: string ^ null Unsupported operand types: null ^ string @@ -1872,6 +1989,7 @@ Unsupported operand types: bool ^ string Unsupported operand types: string ^ int Unsupported operand types: int ^ string Unsupported operand types: string ^ float +Warning: Implicit conversion from float 3.5 to int loses precision Unsupported operand types: float ^ string No error for "foo" ^= "123" No error for "123" ^= "foo" @@ -1880,6 +1998,7 @@ No error for "123foo" ^= "foo" Warning: Array to string conversion Warning: Array to string conversion No error for [] .= [] +Warning: Array to string conversion Object of class stdClass could not be converted to string Warning: Array to string conversion No error for [] .= STDOUT diff --git a/Zend/tests/str_offset_006.phpt b/Zend/tests/str_offset_006.phpt index fa35c8d34b9c..01325defe954 100644 --- a/Zend/tests/str_offset_006.phpt +++ b/Zend/tests/str_offset_006.phpt @@ -10,7 +10,12 @@ $a[$y]=$a.=($y); var_dump($a); ?> --EXPECTF-- +Err: Undefined variable $y +Err: Undefined variable $a +Err: Undefined variable $y +Err: String offset cast occurred + Fatal error: Uncaught Error: Cannot assign an empty string to a string offset in %s:%d Stack trace: #0 {main} - thrown in %s on line %d + thrown in %s on line %d \ No newline at end of file diff --git a/Zend/tests/undef_var_in_verify_return.phpt b/Zend/tests/undef_var_in_verify_return.phpt index ef37694a7dba..0a9dff20343f 100644 --- a/Zend/tests/undef_var_in_verify_return.phpt +++ b/Zend/tests/undef_var_in_verify_return.phpt @@ -19,4 +19,10 @@ Fatal error: Uncaught TypeError: test(): Return value must be of type string, nu Stack trace: #0 %s(%d): test() #1 {main} - thrown in %s on line %d + +Next ErrorException: Undefined variable $test in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', 8) +#1 %s(%d): test() +#2 {main} + thrown in %s on line %d \ No newline at end of file diff --git a/Zend/zend.c b/Zend/zend.c index e56c01a161d1..3b10229a37d7 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1588,6 +1588,13 @@ ZEND_API void zend_flush_deferred_errors(void) return; } + /* A user error handler cannot run while an exception is pending. Keep the + * errors buffered so ZEND_HANDLE_EXCEPTION can flush them with the + * exception cleared, instead of silently dropping them. */ + if (EG(exception)) { + return; + } + zend_err_buf buf = EG(deferred_errors); memset(&EG(deferred_errors), 0, sizeof(EG(deferred_errors))); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 3118bfe0a8d1..d39259df6fe7 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -8227,6 +8227,21 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) { const zend_op *throw_op = EG(opline_before_exception); + if (EG(deferred_errors).size) { + zend_object *orig_exception = EG(exception); + EX(opline) = EG(opline_before_exception); + EG(exception) = NULL; + + zend_flush_deferred_errors(); + + if (EG(exception)) { + zend_exception_set_previous(EG(exception), orig_exception); + } else { + EG(exception) = orig_exception; + EX(opline) = EG(exception_op); + } + } + /* Exception was thrown before executing any op */ if (UNEXPECTED(!throw_op)) { ZEND_VM_DISPATCH_TO_HELPER(zend_dispatch_try_catch_finally_helper, try_catch_offset, -1, op_num, 0); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 5183e102221a..7e780e03ecf5 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3403,6 +3403,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_HANDLE_EXCEPT { const zend_op *throw_op = EG(opline_before_exception); + if (EG(deferred_errors).size) { + zend_object *orig_exception = EG(exception); + EX(opline) = EG(opline_before_exception); + EG(exception) = NULL; + + zend_flush_deferred_errors(); + + if (EG(exception)) { + zend_exception_set_previous(EG(exception), orig_exception); + } else { + EG(exception) = orig_exception; + EX(opline) = EG(exception_op); + } + } + /* Exception was thrown before executing any op */ if (UNEXPECTED(!throw_op)) { ZEND_VM_DISPATCH_TO_HELPER(zend_dispatch_try_catch_finally_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX -1, 0)); @@ -56114,6 +56129,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_HANDLE_EXCEPTION_S { const zend_op *throw_op = EG(opline_before_exception); + if (EG(deferred_errors).size) { + zend_object *orig_exception = EG(exception); + EX(opline) = EG(opline_before_exception); + EG(exception) = NULL; + + zend_flush_deferred_errors(); + + if (EG(exception)) { + zend_exception_set_previous(EG(exception), orig_exception); + } else { + EG(exception) = orig_exception; + EX(opline) = EG(exception_op); + } + } + /* Exception was thrown before executing any op */ if (UNEXPECTED(!throw_op)) { ZEND_VM_DISPATCH_TO_HELPER(zend_dispatch_try_catch_finally_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_EX -1, 0)); diff --git a/ext/opcache/tests/jit/assign_043.phpt b/ext/opcache/tests/jit/assign_043.phpt index b6dab34be9de..6c86b94e545f 100644 --- a/ext/opcache/tests/jit/assign_043.phpt +++ b/ext/opcache/tests/jit/assign_043.phpt @@ -18,7 +18,4 @@ try { } ?> --EXPECTF-- -Fatal error: Uncaught Error: Undefined constant "NOT_REACHED" in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +Undefined variable $b \ No newline at end of file diff --git a/ext/opcache/tests/jit/fetch_dim_rw_004.phpt b/ext/opcache/tests/jit/fetch_dim_rw_004.phpt index 7816923bb79b..b0f18f9746c5 100644 --- a/ext/opcache/tests/jit/fetch_dim_rw_004.phpt +++ b/ext/opcache/tests/jit/fetch_dim_rw_004.phpt @@ -12,7 +12,12 @@ $k=[]; $y[$k]++; ?> --EXPECTF-- -Fatal error: Uncaught TypeError: Cannot access offset of type array on array in %sfetch_dim_rw_004.php:%d +Fatal error: Uncaught TypeError: Cannot access offset of type array on array in %s:%d Stack trace: #0 {main} - thrown in %sfetch_dim_rw_004.php on line %d + +Next TypeError: {closure:%s:%d}(): Argument #1 ($y) must be of type y, int given, called in %s on line %d and defined in %s:%d +Stack trace: +#0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', 5) +#1 {main} + thrown in %s on line %d \ No newline at end of file diff --git a/ext/opcache/tests/jit/qm_assign_undef_exception.phpt b/ext/opcache/tests/jit/qm_assign_undef_exception.phpt index 23650cb595c5..45de4de6763b 100644 --- a/ext/opcache/tests/jit/qm_assign_undef_exception.phpt +++ b/ext/opcache/tests/jit/qm_assign_undef_exception.phpt @@ -18,8 +18,4 @@ try { } ?> --EXPECTF-- -Fatal error: Uncaught Error: Undefined constant "X" in %s:%d -Stack trace: -#0 %s(%d): test() -#1 {main} - thrown in %s on line %d +Undefined variable $b \ No newline at end of file diff --git a/ext/opcache/tests/jit/type_check_001.phpt b/ext/opcache/tests/jit/type_check_001.phpt index bf9bac572191..78936f197d5a 100644 --- a/ext/opcache/tests/jit/type_check_001.phpt +++ b/ext/opcache/tests/jit/type_check_001.phpt @@ -21,7 +21,4 @@ try { } ?> --EXPECTF-- -Fatal error: Uncaught Error: Call to undefined function undefined_function() in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +Exception: Undefined variable $a \ No newline at end of file diff --git a/ext/standard/tests/class_object/get_class_methods_variation_001.phpt b/ext/standard/tests/class_object/get_class_methods_variation_001.phpt index 430529b79547..71392b9f4dc1 100644 --- a/ext/standard/tests/class_object/get_class_methods_variation_001.phpt +++ b/ext/standard/tests/class_object/get_class_methods_variation_001.phpt @@ -111,18 +111,23 @@ Arg value 0.5 get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, float given Arg value Array +Error: 2 - Array to string conversion get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array +Error: 2 - Array to string conversion get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array +Error: 2 - Array to string conversion get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array +Error: 2 - Array to string conversion get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array +Error: 2 - Array to string conversion get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value @@ -164,4 +169,4 @@ get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid Arg value get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, null given -Done +Done \ No newline at end of file diff --git a/ext/standard/tests/class_object/get_parent_class_variation_002.phpt b/ext/standard/tests/class_object/get_parent_class_variation_002.phpt index 82274edbe60e..eb951cfbfdce 100644 --- a/ext/standard/tests/class_object/get_parent_class_variation_002.phpt +++ b/ext/standard/tests/class_object/get_parent_class_variation_002.phpt @@ -114,18 +114,23 @@ Arg value 0.5 get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, float given Arg value Array +Error: 2 - Array to string conversion get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array +Error: 2 - Array to string conversion get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array +Error: 2 - Array to string conversion get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array +Error: 2 - Array to string conversion get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value Array +Error: 2 - Array to string conversion get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given Arg value @@ -168,4 +173,4 @@ get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid Arg value get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, null given -Done +Done \ No newline at end of file diff --git a/ext/standard/tests/class_object/method_exists_variation_001.phpt b/ext/standard/tests/class_object/method_exists_variation_001.phpt index ee96d3e7c5b8..1761475ace66 100644 --- a/ext/standard/tests/class_object/method_exists_variation_001.phpt +++ b/ext/standard/tests/class_object/method_exists_variation_001.phpt @@ -85,6 +85,8 @@ echo "Done"; *** Testing method_exists() : usage variations *** Arg value 0 +Error: 2 - Undefined variable $undefined_var +Error: 2 - Undefined variable $unset_var method_exists(): Argument #1 ($object_or_class) must be of type object|string, int given Arg value 1 @@ -112,18 +114,23 @@ Arg value 0.5 method_exists(): Argument #1 ($object_or_class) must be of type object|string, float given Arg value Array +Error: 2 - Array to string conversion method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Arg value Array +Error: 2 - Array to string conversion method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Arg value Array +Error: 2 - Array to string conversion method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Arg value Array +Error: 2 - Array to string conversion method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Arg value Array +Error: 2 - Array to string conversion method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Arg value @@ -163,4 +170,4 @@ method_exists(): Argument #1 ($object_or_class) must be of type object|string, n Arg value method_exists(): Argument #1 ($object_or_class) must be of type object|string, null given -Done +Done \ No newline at end of file From a94675a53c5030e147afd3c5adfab73ac5c545c1 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Mon, 29 Jun 2026 09:48:06 -0400 Subject: [PATCH 7/8] JIT: Don't flush deferred errors on function-JIT loop forward entry The function JIT drained EG(deferred_errors) at a loop header's forward entry, through both the loop-header timeout check and the block-start deopt. Errors armed before the loop fired one opcode too early, before the loop body ran. The interpreter has no loop-header flush; it drains only at the post-call interrupt check, on leave, and at HANDLE_EXCEPTION, so method_exists_variation_001 diverged under opcache.jit=function. Loop headers now use a timeout-only check that services a pending timeout or interrupt function but leaves buffered deferred errors and EG(vm_interrupt) in place, letting them ride to the next post-call, leave, or exception safepoint. The block-start deopt skips loop headers; non-loop branch targets keep it, which preserves the earlier-flush ordering they require. --- ext/opcache/jit/zend_jit.c | 5 +++-- ext/opcache/jit/zend_jit_ir.c | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 90ca407cb9e4..2c47d2690f62 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -1575,7 +1575,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op zend_jit_set_last_valid_opline(&ctx, op_array->opcodes + ssa->cfg.blocks[b].start); } if (ssa->cfg.blocks[b].flags & ZEND_BB_LOOP_HEADER) { - zend_jit_check_timeout(&ctx, op_array->opcodes + ssa->cfg.blocks[b].start, NULL); + zend_jit_check_loop_timeout(&ctx, op_array->opcodes + ssa->cfg.blocks[b].start); } if (!ssa->cfg.blocks[b].len) { zend_jit_bb_end(&ctx, b); @@ -1606,7 +1606,8 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op } } if ((ssa->cfg.blocks[b].flags & ZEND_BB_TARGET) - && ssa->cfg.blocks[b].start != 0) { + && ssa->cfg.blocks[b].start != 0 + && !(ssa->cfg.blocks[b].flags & ZEND_BB_LOOP_HEADER)) { if (!zend_jit_deferred_error_deopt(&ctx, op_array, ssa, b, op_array->opcodes + ssa->cfg.blocks[b].start, ssa->cfg.blocks[b].start)) { goto jit_failure; } diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 2f9d828b6e51..85d358737322 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -1944,6 +1944,32 @@ static void zend_jit_check_timeout(zend_jit_ctx *jit, const zend_op *opline, con } } +static void zend_jit_check_loop_timeout(zend_jit_ctx *jit, const zend_op *opline) +{ + ir_ref if_interrupt, if_timeout, if_deferred, ride_path, cont_path; + + if_interrupt = ir_IF(ir_LOAD_U8(jit_EG(vm_interrupt))); + ir_IF_TRUE_cold(if_interrupt); + + if_timeout = ir_IF(ir_LOAD_U8(jit_EG(timed_out))); + ir_IF_TRUE(if_timeout); + jit_LOAD_IP_ADDR(jit, opline); + ir_IJMP(jit_STUB_ADDR(jit, jit_stub_interrupt_handler)); + ir_IF_FALSE(if_timeout); + + if_deferred = ir_IF(ir_LOAD_U32(jit_EG(deferred_errors.size))); + ir_IF_FALSE(if_deferred); + jit_LOAD_IP_ADDR(jit, opline); + ir_IJMP(jit_STUB_ADDR(jit, jit_stub_interrupt_handler)); + ir_IF_TRUE(if_deferred); + ride_path = ir_END(); + + ir_IF_FALSE(if_interrupt); + cont_path = ir_END(); + + ir_MERGE_2(ride_path, cont_path); +} + static void zend_jit_vm_enter(zend_jit_ctx *jit, ir_ref to_opline) { // ZEND_VM_ENTER() From 0ac4e98654d9b47f55a4a551e42a345e9168ee5d Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Mon, 29 Jun 2026 15:43:56 -0400 Subject: [PATCH 8/8] JIT: Don't flush deferred errors into a half-built call frame Flushing a deferred error at an interrupt safepoint can run a user error handler that throws. If a call frame is being assembled (EX(call) is set, or the opline is a DO_*CALL), the throw unwinds into the half-built frame and cleanup_unfinished_calls destroys an unsent argument slot. On the CALL VM that slot is NULL and the cleanup faults; the hybrid VM keeps a live value there, so only Windows and macOS crash. zend_interrupt_helper already deferred the flush past EX(call), but only when zend_interrupt_function was unset. Windows always installs a Ctrl-C interrupt function, which disabled the ride and let the flush run into the half-built call. Re-arm vm_interrupt and defer the flush whenever a call is being assembled, regardless of zend_interrupt_function, and run the interrupt function separately. The JIT interrupt stub gets the same EX(call) guard through zend_jit_check_deferred_errors. --- Zend/zend_vm_def.h | 19 ++++++++------- Zend/zend_vm_execute.h | 38 +++++++++++++++++------------- ext/opcache/jit/zend_jit_helpers.c | 9 +++++++ ext/opcache/jit/zend_jit_ir.c | 3 ++- 4 files changed, 44 insertions(+), 25 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index d39259df6fe7..9cdefe8270c5 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -10657,16 +10657,19 @@ ZEND_VM_HELPER(zend_interrupt_helper, ANY, ANY) } else { bool entered = false; if (EG(deferred_errors).size) { - if (!zend_interrupt_function - && (opline->opcode == ZEND_DO_FCALL - || opline->opcode == ZEND_DO_ICALL - || opline->opcode == ZEND_DO_UCALL - || opline->opcode == ZEND_DO_FCALL_BY_NAME)) { + if (EX(call) + || opline->opcode == ZEND_DO_FCALL + || opline->opcode == ZEND_DO_ICALL + || opline->opcode == ZEND_DO_UCALL + || opline->opcode == ZEND_DO_FCALL_BY_NAME) { zend_atomic_bool_store_ex(&EG(vm_interrupt), true); - ZEND_VM_CONTINUE(); + if (!zend_interrupt_function) { + ZEND_VM_CONTINUE(); + } + } else { + zend_flush_deferred_errors(); + entered = true; } - zend_flush_deferred_errors(); - entered = true; } if (zend_interrupt_function) { zend_interrupt_function(execute_data); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 7e780e03ecf5..72232d4ed5c6 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4062,16 +4062,19 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV } else { bool entered = false; if (EG(deferred_errors).size) { - if (!zend_interrupt_function - && (opline->opcode == ZEND_DO_FCALL - || opline->opcode == ZEND_DO_ICALL - || opline->opcode == ZEND_DO_UCALL - || opline->opcode == ZEND_DO_FCALL_BY_NAME)) { + if (EX(call) + || opline->opcode == ZEND_DO_FCALL + || opline->opcode == ZEND_DO_ICALL + || opline->opcode == ZEND_DO_UCALL + || opline->opcode == ZEND_DO_FCALL_BY_NAME) { zend_atomic_bool_store_ex(&EG(vm_interrupt), true); - ZEND_VM_CONTINUE(); + if (!zend_interrupt_function) { + ZEND_VM_CONTINUE(); + } + } else { + zend_flush_deferred_errors(); + entered = true; } - zend_flush_deferred_errors(); - entered = true; } if (zend_interrupt_function) { zend_interrupt_function(execute_data); @@ -56788,16 +56791,19 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend } else { bool entered = false; if (EG(deferred_errors).size) { - if (!zend_interrupt_function - && (opline->opcode == ZEND_DO_FCALL - || opline->opcode == ZEND_DO_ICALL - || opline->opcode == ZEND_DO_UCALL - || opline->opcode == ZEND_DO_FCALL_BY_NAME)) { + if (EX(call) + || opline->opcode == ZEND_DO_FCALL + || opline->opcode == ZEND_DO_ICALL + || opline->opcode == ZEND_DO_UCALL + || opline->opcode == ZEND_DO_FCALL_BY_NAME) { zend_atomic_bool_store_ex(&EG(vm_interrupt), true); - ZEND_VM_CONTINUE(); + if (!zend_interrupt_function) { + ZEND_VM_CONTINUE(); + } + } else { + zend_flush_deferred_errors(); + entered = true; } - zend_flush_deferred_errors(); - entered = true; } if (zend_interrupt_function) { zend_interrupt_function(execute_data); diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 64a48068f378..2b7d03ad11ea 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -3437,6 +3437,15 @@ static void ZEND_FASTCALL zend_jit_exception_in_interrupt_handler_helper(void) } } +static void ZEND_FASTCALL zend_jit_check_deferred_errors(zend_execute_data *execute_data) +{ + if (EG(deferred_errors).size && execute_data->call) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + } else { + zend_flush_deferred_errors(); + } +} + static zend_string* ZEND_FASTCALL zend_jit_rope_end(zend_string **rope, uint32_t count) { zend_string *ret; diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 85d358737322..41f0a8ec69e2 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -2099,7 +2099,7 @@ static int zend_jit_interrupt_handler_stub(zend_jit_ctx *jit) ir_CALL(IR_VOID, ir_CONST_FUNC(zend_timeout)); ir_MERGE_WITH_EMPTY_TRUE(if_timeout); - ir_CALL(IR_VOID, ir_CONST_FUNC(zend_flush_deferred_errors)); + ir_CALL_1(IR_VOID, ir_CONST_FUNC(zend_jit_check_deferred_errors), jit_FP(jit)); if (zend_interrupt_function) { ir_CALL_1(IR_VOID, ir_CONST_FUNC(zend_interrupt_function), jit_FP(jit)); } @@ -3156,6 +3156,7 @@ static void zend_jit_setup_disasm(void) REGISTER_HELPER(zend_free_extra_named_params); REGISTER_HELPER(zend_jit_free_call_frame); REGISTER_HELPER(zend_jit_exception_in_interrupt_handler_helper); + REGISTER_HELPER(zend_jit_check_deferred_errors); REGISTER_HELPER(zend_jit_verify_arg_slow); REGISTER_HELPER(zend_missing_arg_error); REGISTER_HELPER(zend_jit_only_vars_by_reference);