From c00baddeee189240d5f20a0e2cfeb4c11dab0fd8 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Mon, 29 Jun 2026 16:51:48 -0400 Subject: [PATCH] Defer user error handlers raised from user-code opcodes A user error handler raised mid-opcode can reenter the VM and free or mutate state the opcode still holds a raw pointer into, a long-standing source of use-after-free and memory-corruption bugs. Defer the handler to the next vm_interrupt safepoint, after the opcode completes. The interpreter and both JITs reach that safepoint through the existing vm_interrupt mechanism. A diagnostic silenced by @ keeps its error_reporting across the deferral; diagnostics raised from internal functions still run synchronously, since the function may gate on EG(exception) or throw right after emitting. Function JIT flushes at the interpreter's safepoints: post-call with the callee frame still on top, each jump-target block, and function entry, spilling the live SSA values before it resumes. Exception handling flushes any buffered diagnostic with the pending exception saved and restored around the handler, so nothing is dropped when a later opcode throws. Under the CALL VM the flush is deferred past a call being assembled, so a throwing handler cannot unwind into a half-built frame and fault in cleanup_unfinished_calls. Fixes GH-20018 Fixes GH-16792 Fixes GH-17416 Fixes GH-18274 Fixes GH-20042 Fixes GH-21245 Fixes GH-22419 --- 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 | 8 +- 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 | 2 +- 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 - ...tion_during_inheritance_can_be_caught.phpt | 4 +- Zend/tests/offsets/array_offset_002.phpt | 6 +- .../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 | 4 +- 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 | 9 +- 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 | 9 +- Zend/zend.c | 263 +++++++++++++----- Zend/zend.h | 3 + Zend/zend_execute.c | 8 +- Zend/zend_execute_API.c | 3 + Zend/zend_globals.h | 5 + Zend/zend_vm_def.h | 66 ++++- Zend/zend_vm_execute.h | 136 +++++++-- ext/opcache/jit/zend_jit.c | 19 +- ext/opcache/jit/zend_jit_helpers.c | 9 + ext/opcache/jit/zend_jit_ir.c | 144 ++++++++-- ext/opcache/jit/zend_jit_trace.c | 8 +- ext/opcache/jit/zend_jit_vm_helpers.c | 13 + ext/opcache/tests/jit/assign_043.phpt | 4 +- 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 | 12 +- ext/opcache/tests/jit/gh18262-002.phpt | 6 +- ext/opcache/tests/jit/gh18262-003.phpt | 1 + .../tests/jit/qm_assign_undef_exception.phpt | 4 +- ext/opcache/tests/jit/type_check_001.phpt | 4 +- ext/spl/tests/gh18274.phpt | 20 ++ ext/standard/array.c | 98 ++++--- ext/standard/tests/array/gh20042.phpt | 18 ++ .../sort/array_multisort_variation2.phpt | 2 +- .../get_class_methods_variation_001.phpt | 12 +- .../get_parent_class_variation_002.phpt | 12 +- .../is_subclass_of_variation_001.phpt | 20 +- .../is_subclass_of_variation_004.phpt | 20 +- .../method_exists_variation_001.phpt | 16 +- .../tests/image/getimagesize_variation2.phpt | 10 +- ext/zend_test/tests/observer_error_05.phpt | 14 +- tests/lang/bug22592.phpt | 4 +- tests/lang/bug25547.phpt | 2 +- tests/lang/bug25922.phpt | 2 +- 94 files changed, 1085 insertions(+), 399 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..9cec1962773b 100644 --- a/Zend/tests/bug76534.phpt +++ b/Zend/tests/bug76534.phpt @@ -10,8 +10,12 @@ $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 {main} + +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 %sbug76534.php on line %d + thrown in %s on line %d \ No newline at end of file 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..847cc2cd8f96 100644 --- a/Zend/tests/exceptions/exception_before_fatal.phpt +++ b/Zend/tests/exceptions/exception_before_fatal.phpt @@ -61,4 +61,4 @@ 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(23) "Undefined variable $foo" \ No newline at end of file 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/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/offsets/array_offset_002.phpt b/Zend/tests/offsets/array_offset_002.phpt index 4d56258ef8b8..ece8a53c7215 100644 --- a/Zend/tests/offsets/array_offset_002.phpt +++ b/Zend/tests/offsets/array_offset_002.phpt @@ -12,7 +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(0) { +array(1) { + [%d]=> + 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..1f85bfee871e 100644 --- a/Zend/tests/operator_unsupported_types.phpt +++ b/Zend/tests/operator_unsupported_types.phpt @@ -2104,7 +2104,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..01325defe954 100644 --- a/Zend/tests/str_offset_006.phpt +++ b/Zend/tests/str_offset_006.phpt @@ -9,8 +9,13 @@ set_error_handler(function($code, $msg) { $a[$y]=$a.=($y); var_dump($a); ?> ---EXPECT-- +--EXPECTF-- Err: Undefined variable $y +Err: Undefined variable $a Err: Undefined variable $y Err: String offset cast occurred -NULL + +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 \ No newline at end of file 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..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-- -Implicit conversion from float-string "1.0E+4%d" to int loses precision int(%d) +Implicit conversion from float-string "1.0E+4%d" to int loses precision 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..0a9dff20343f 100644 --- a/Zend/tests/undef_var_in_verify_return.phpt +++ b/Zend/tests/undef_var_in_verify_return.phpt @@ -15,9 +15,14 @@ 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): test() +#1 {main} + +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 + thrown in %s on line %d \ No newline at end of file diff --git a/Zend/zend.c b/Zend/zend.c index 9411b92a2018..eebb35b9cf60 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1436,9 +1436,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; @@ -1446,11 +1447,199 @@ 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->error_reporting = EG(error_reporting); + 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; + } + + /* 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))); + + 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) { + zend_string_release(info->filename); + } + zend_string_release(info->message); + efree_size(info, sizeof(zend_error_info)); + } + EG(error_reporting) = orig_error_reporting; + 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)) { @@ -1483,6 +1672,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++; @@ -1544,72 +1734,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; } @@ -1973,6 +2101,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..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; @@ -453,6 +454,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 61499c0cc23d..20b07d12eb07 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..9cdefe8270c5 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); @@ -8223,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); @@ -10635,23 +10654,44 @@ 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) { + 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); + if (!zend_interrupt_function) { + ZEND_VM_CONTINUE(); + } + } else { + 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..72232d4ed5c6 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); @@ -3399,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)); @@ -4040,23 +4059,44 @@ 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) { + 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); + if (!zend_interrupt_function) { + ZEND_VM_CONTINUE(); + } + } else { + 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 +54002,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); @@ -56088,6 +56132,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)); @@ -56729,23 +56788,44 @@ 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) { + 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); + if (!zend_interrupt_function) { + ZEND_VM_CONTINUE(); + } + } else { + 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 +110384,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..2c47d2690f62 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; @@ -1574,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); @@ -1604,6 +1605,13 @@ 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 + && !(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; + } + } 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 +1620,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: @@ -2934,7 +2949,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_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 2fe0b1896a92..ab573586aba7 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() @@ -2073,16 +2099,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_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)); - 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 +2555,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); @@ -3132,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); @@ -4339,6 +4364,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)); @@ -10597,7 +10684,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); @@ -10718,9 +10805,9 @@ 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 && 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); @@ -11061,8 +11148,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 || @@ -11086,6 +11172,17 @@ 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_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); + } + if (may_need_call_helper) { if (!left_frame) { left_frame = true; @@ -11167,8 +11264,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 @@ -11211,13 +11306,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); @@ -11249,11 +11339,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..c5474b3345ca 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -102,6 +102,11 @@ 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(); + } + if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) { zend_clean_and_cache_symbol_table(EX(symbol_table)); } @@ -1114,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; diff --git a/ext/opcache/tests/jit/assign_043.phpt b/ext/opcache/tests/jit/assign_043.phpt index edb0cb212ab0..6c86b94e545f 100644 --- a/ext/opcache/tests/jit/assign_043.phpt +++ b/ext/opcache/tests/jit/assign_043.phpt @@ -17,5 +17,5 @@ try { echo $e->getMessage(), "\n"; } ?> ---EXPECT-- -Undefined variable $b +--EXPECTF-- +Undefined variable $b \ No newline at end of file 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..b0f18f9746c5 100644 --- a/ext/opcache/tests/jit/fetch_dim_rw_004.phpt +++ b/ext/opcache/tests/jit/fetch_dim_rw_004.phpt @@ -12,12 +12,12 @@ $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 +Fatal error: Uncaught TypeError: Cannot access offset of type array on array in %s:%d Stack trace: -#0 %s(%d): {closure:%s:%d}(2, 'Undefined varia...', '%s', 5) -#1 {main} +#0 {main} -Next TypeError: Cannot access offset of type array on array in %sfetch_dim_rw_004.php:5 +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 {main} - thrown in %sfetch_dim_rw_004.php on line 5 +#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/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..45de4de6763b 100644 --- a/ext/opcache/tests/jit/qm_assign_undef_exception.phpt +++ b/ext/opcache/tests/jit/qm_assign_undef_exception.phpt @@ -17,5 +17,5 @@ try { echo $e->getMessage(), "\n"; } ?> ---EXPECT-- -Undefined variable $b +--EXPECTF-- +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 028d2e8637e2..78936f197d5a 100644 --- a/ext/opcache/tests/jit/type_check_001.phpt +++ b/ext/opcache/tests/jit/type_check_001.phpt @@ -20,5 +20,5 @@ try { echo "Exception: " . $e->getMessage() . "\n"; } ?> ---EXPECT-- -Exception: Undefined variable $a +--EXPECTF-- +Exception: Undefined variable $a \ No newline at end of file diff --git a/ext/spl/tests/gh18274.phpt b/ext/spl/tests/gh18274.phpt new file mode 100644 index 000000000000..bc4f017d2c3b --- /dev/null +++ b/ext/spl/tests/gh18274.phpt @@ -0,0 +1,20 @@ +--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: +#%d %A: func_get_args(%d, %s, %s, %d)%A +#%d {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..b3bdba7a63df 100644 --- a/ext/standard/tests/array/sort/array_multisort_variation2.phpt +++ b/ext/standard/tests/array/sort/array_multisort_variation2.phpt @@ -107,7 +107,7 @@ foreach($inputs as $key =>$value) { }; ?> ---EXPECT-- +--EXPECTF-- *** Testing array_multisort() : usage variation *** --int 0-- 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..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 @@ -109,25 +109,25 @@ 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 +get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given 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 +get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given 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 +get_class_methods(): Argument #1 ($object_or_class) must be an object or a valid class name, array given 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 +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 @@ -169,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 8faeff9dd8dc..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 @@ -112,25 +112,25 @@ 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 +get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given 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 +get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given 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 +get_parent_class(): Argument #1 ($object_or_class) must be an object or a valid class name, array given 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 +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 @@ -173,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/is_subclass_of_variation_001.phpt b/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt index f03841dbac7d..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 *** -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var - -Arg value 0 +%A +%A +%A +%A 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..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 *** -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var - -Arg value 0 +%A +%A +%A +%A 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..1761475ace66 100644 --- a/ext/standard/tests/class_object/method_exists_variation_001.phpt +++ b/ext/standard/tests/class_object/method_exists_variation_001.phpt @@ -83,10 +83,10 @@ echo "Done"; ?> --EXPECT-- *** Testing method_exists() : 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 method_exists(): Argument #1 ($object_or_class) must be of type object|string, int given Arg value 1 @@ -112,25 +112,25 @@ 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 +method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Arg value Array -method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Error: 2 - Array to string conversion +method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Arg value Array -method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Error: 2 - Array to string conversion +method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given Arg value Array -method_exists(): Argument #1 ($object_or_class) must be of type object|string, array given 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 @@ -170,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 diff --git a/ext/standard/tests/image/getimagesize_variation2.phpt b/ext/standard/tests/image/getimagesize_variation2.phpt index 045d9141a6bd..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 *** -Error: 2 - Undefined variable $undefined_var -Error: 2 - Undefined variable $unset_var - --- Arg value 0 -- +%A +%A +%A +%A 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: ''