Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8f285af
Delay notice emission until end of opcode
nikic Apr 23, 2021
b0fa748
Add support for INDIRECT delaying
iluuu1994 Aug 29, 2023
55e9c1e
Delay notice until interrupt handler
arnaud-lb May 6, 2026
dee6e1a
Add interrupt check in ZEND_RETURN
arnaud-lb May 6, 2026
3cf4c71
Delay all errors
arnaud-lb May 6, 2026
e8b3d4f
set_error_handler(): $promote_to_exception
arnaud-lb May 6, 2026
9c2af6f
Delay only if we are going to call a user error handler
arnaud-lb May 6, 2026
89bb912
Handle silence operator
arnaud-lb May 7, 2026
a4e8b15
First promoted error takes precedence over other subsequent errors or…
arnaud-lb May 19, 2026
e9bbb42
Fix delayed error handling in ZEND_RETURN
arnaud-lb May 19, 2026
8a15d25
Chain exceptions thrown by handlers at the beginning of ZEND_HANDLE_E…
arnaud-lb May 19, 2026
6ecb521
Free trampolines
arnaud-lb May 19, 2026
de0e991
FIXMEs
arnaud-lb May 19, 2026
1746fc4
Fix tests
arnaud-lb May 7, 2026
6ef688b
Generated file
arnaud-lb May 19, 2026
e7b1f75
Delay destructors
arnaud-lb May 20, 2026
f2e845e
Only check delayed effects once in leave helper
arnaud-lb May 25, 2026
866c500
Reuse delayed effect buffer
arnaud-lb May 26, 2026
139a094
Rename promote_to_exception -> delay
arnaud-lb May 27, 2026
95c1936
Simplify
arnaud-lb May 27, 2026
fd64e35
Rename flag
arnaud-lb May 27, 2026
307640e
Move the ZEND_VM_ENTER_EX() interrupt check to the zend_vm_stack_push…
arnaud-lb May 27, 2026
7221b7f
These tests should pass again
arnaud-lb May 27, 2026
255b5ed
Fix more tests
arnaud-lb May 27, 2026
bc11dd7
Destroy hash
arnaud-lb May 27, 2026
36f2e89
Save opline before calling interrupt helper in non-tailcalling handle…
arnaud-lb May 29, 2026
2b999b2
Fix JIT
arnaud-lb May 29, 2026
ec9e195
Update may_throw
arnaud-lb May 29, 2026
d6ae346
Handle delay:false + silence operator
arnaud-lb May 29, 2026
de280ff
Add tests
arnaud-lb May 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions Zend/Optimizer/zend_inference.c
Original file line number Diff line number Diff line change
Expand Up @@ -4951,7 +4951,7 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
}
} else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
if ((t1 & MAY_BE_RC1)
&& (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) {
&& (t1 & (MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) {
switch (opline->opcode) {
case ZEND_CASE:
case ZEND_CASE_STRICT:
Expand All @@ -4972,7 +4972,7 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
case ZEND_MAKE_REF:
break;
default:
/* destructor may be called */
/* resource destructor may be called */
return 1;
}
}
Expand All @@ -4992,14 +4992,14 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
}
} else if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
if ((t2 & MAY_BE_RC1)
&& (t2 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) {
&& (t2 & (MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) {
switch (opline->opcode) {
case ZEND_ASSIGN:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
break;
default:
/* destructor may be called */
/* resource destructor may be called */
return 1;
}
}
Expand Down Expand Up @@ -5178,11 +5178,11 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
}
ZEND_FALLTHROUGH;
case ZEND_UNSET_VAR:
return (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY));
return (t1 & (MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY));
case ZEND_BIND_STATIC:
case ZEND_BIND_INIT_STATIC_OR_JMP:
if (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY)) {
/* Destructor may throw. */
if (t1 & (MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY)) {
/* resource destructor may throw. */
return 1;
}
return 0;
Expand All @@ -5192,7 +5192,7 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
return 1;
}
}
if (t1 & (MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_REF)) {
if (t1 & (MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_REF)) {
return 1;
}
return (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_TRUE|MAY_BE_FALSE|MAY_BE_STRING|MAY_BE_LONG|MAY_BE_DOUBLE)) || opline->op2_type == IS_UNUSED ||
Expand Down Expand Up @@ -5317,7 +5317,7 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
}
if (opline->op2_type == IS_CV
&& (t2 & MAY_BE_RC1)
&& (t2 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) {
&& (t2 & (MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) {
return 1;
}
return 0;
Expand Down
4 changes: 4 additions & 0 deletions Zend/Optimizer/zend_inference.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
(MAY_BE_OBJECT|MAY_BE_RESOURCE \
|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE)

#define MAY_HAVE_UNDELAYED_DTOR \
(MAY_BE_RESOURCE \
|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_RESOURCE)

#define DEFINE_SSA_OP_HAS_RANGE(opN) \
static zend_always_inline bool _ssa_##opN##_has_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
{ \
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/ArrayAccess/bug41209.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class env
{
public function __construct()
{
set_error_handler(array(__CLASS__, 'errorHandler'));
set_error_handler(array(__CLASS__, 'errorHandler'), delay: false);
}

public static function errorHandler($errno, $errstr, $errfile, $errline)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

set_error_handler(function (int $errno, string $errstr, ?string $errfile = null, ?int $errline = null) {
throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
});
}, delay: false);

class Clazz {
#[\Deprecated(self::TEST)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

set_error_handler(function (int $errno, string $errstr, ?string $errfile = null, ?int $errline = null) {
throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
});
}, delay: false);

#[\Deprecated(TEST)]
const TEST = "from itself";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

set_error_handler(function (int $errno, string $errstr, ?string $errfile = null, ?int $errline = null) {
throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
});
}, delay: false);

#[\Deprecated("convert to exception")]
function test() {
Expand Down Expand Up @@ -76,7 +76,7 @@ class Destructor {
}

try {
new Destructor();
(function () { new Destructor(); })();
} catch (ErrorException $e) {
echo "Caught: ", $e->getMessage(), PHP_EOL;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ try {
}
?>
--EXPECTF--
ERR#2: include(class://non.existent.Class): Failed to open stream: "CLWrapper::stream_open" call failed @ include
ERR#2: include(): Failed opening 'class://non.existent.Class' for inclusion (include_path='%s') @ include
ERR#2: include(class://non.existent.Class): Failed to open stream: "CLWrapper::stream_open" call failed @ __construct
ERR#2: include(): Failed opening 'class://non.existent.Class' for inclusion (include_path='.:') @ __construct

Fatal error: Uncaught Exception: Failed loading class://non.existent.Class in %s
Stack trace:
Expand Down
6 changes: 4 additions & 2 deletions Zend/tests/bind_static_exception.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ class Test {
}
}
try {
$new = new Test;
static $new;
(function () {
$new = new Test;
static $new;
})();
} catch (Exception $e) {
echo $e->getMessage(), "\n";
}
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/bitwise_not_precision_exception.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Promoting float precision warning to exception in bitwise_not
<?php
set_error_handler(function($_, $msg) {
throw new Exception($msg);
});
}, delay: false);
try {
var_dump(~INF);
} catch (Exception $e) {
Expand Down
3 changes: 1 addition & 2 deletions Zend/tests/bug29896.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,4 @@ GenerateError2("Test2");
?>
--EXPECTF--
#0 %s(%d): userErrorHandler(2, 'Undefined varia...', '%s', %d)
#1 %s(%d): GenerateError1('Test1')
#2 %s(%d): GenerateError2('Test2')
#1 %s(%d): GenerateError2('Test2')
2 changes: 1 addition & 1 deletion Zend/tests/bug35017.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Bug #35017 (Exception thrown in error handler may cause unexpected behavior)
--FILE--
<?php
set_error_handler('errorHandler');
set_error_handler('errorHandler', delay: false);
try {
if ($a) {
echo "1\n";
Expand Down
1 change: 1 addition & 0 deletions Zend/tests/bug38220.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class A {
$drv = myserv::drv();

$drv->obj = $this;
(function (){})();

echo "before call $method\n";
print_r($this);
Expand Down
5 changes: 4 additions & 1 deletion Zend/tests/bug49893.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ class B {
throw new Exception("1");
}
}
try {
function f() {
$b = new B();
}
try {
f();
} catch(Exception $e) {
echo $e->getMessage() . "\n";
}
Expand Down
2 changes: 2 additions & 0 deletions Zend/tests/bug52041.phpt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
--TEST--
Bug #52041 (Memory leak when writing on uninitialized variable returned from function)
--INI--
opcache.jit=0
--FILE--
<?php
function foo() {
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/bug60909_1.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ register_shutdown_function(function(){echo("\n\n!!!shutdown!!!\n\n");});
set_error_handler(function($errno, $errstr, $errfile, $errline){
echo "error($errstr)";
throw new Exception("Foo");
});
}, delay: false);

require 'notfound.php';
?>
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/bug61767.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Bug #61767 (Shutdown functions not called in certain error situation)
set_error_handler(function($code, $msg, $file = null, $line = null) {
echo "Error handler called ($msg)\n";
throw new \ErrorException($msg, $code, 0, $file, $line);
});
}, delay: false);

register_shutdown_function(function(){
echo "Shutting down\n";
Expand Down
6 changes: 6 additions & 0 deletions Zend/tests/bug63206.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,19 @@ set_error_handler(function() {
echo 'Internal handler' . PHP_EOL;
});

global $tmp;
$triggerInternalNotice++; // warnings while handling the error should go into internal handler
fwrite($tmp, "."); // handle errors

restore_error_handler();
});

$tmp = tmpfile();

$triggerNotice1++;
fwrite($tmp, "."); // handle errors
$triggerNotice2++;
fwrite($tmp, "."); // handle errors
?>
--EXPECT--
Second handler
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/bug70662.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ var_dump($a);
--EXPECT--
array(1) {
["b"]=>
int(2)
int(1)
}
2 changes: 1 addition & 1 deletion Zend/tests/bug70785.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Bug #70785 (Infinite loop due to exception during identical comparison)

set_error_handler(function($no, $msg) {
throw new Exception($msg);
});
}, delay: false);

try {
if ($a === null) { // ZEND_VM_SMART_BRANCH
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/bug72101.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class Mock_MethodCallbackByReference_7b180d26 extends MethodCallbackByReference
set_error_handler(function() {
// var_dump(func_get_args());
DoesNotExists::$nope = true;
}, E_ALL);
}, E_ALL, delay: false);

$foo = new Mock_MethodCallbackByReference_7b180d26();
$InvMocker = new PHPUnit_Framework_MockObject_InvocationMocker();
Expand Down
7 changes: 3 additions & 4 deletions Zend/tests/bug74164.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ namespace Foo;

set_error_handler(function ($type, $msg) {
throw new \Exception($msg);
});
}, delay: false);

call_user_func(function (array &$ref) {var_dump("xxx");}, 'not_an_array_variable');
?>
--EXPECTF--
Fatal error: Uncaught Exception: {closure:%s:%d}(): Argument #1 ($ref) must be passed by reference, value given in %s:%d
Stack trace:
#0 [internal function]: {closure:%s:%d}(2, '%s', '%s', 9)
#1 %sbug74164.php(%d): call_user_func(%s)
#2 {main}
#0 %s(%d): {closure:%s:%d}(2, '{closure:%s', '%s', 9)
#1 {main}
thrown in %sbug74164.php on line %d
2 changes: 1 addition & 1 deletion Zend/tests/bug76534.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Bug #76534 (PHP hangs on 'illegal string offset on string references with an err
<?php
set_error_handler(function ($severity, $message, $file, $line) {
throw new \Exception($message);
});
}, delay: false);

$x = "foo";
$y = &$x["2bar"];
Expand Down
34 changes: 30 additions & 4 deletions Zend/tests/bug78598.phpt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
--TEST--
Bug #78598: Changing array during undef index RW error segfaults
--INI--
opcache.jit=0
--FILE--
<?php

Expand All @@ -25,7 +27,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"
}
}
}
2 changes: 1 addition & 1 deletion Zend/tests/bug79599.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Bug #79599 (coredump in set_error_handler)
<?php
set_error_handler(function($code, $message){
throw new \Exception($message);
});
}, delay: false);
function test1(){
$a[] = $b;
}
Expand Down
18 changes: 15 additions & 3 deletions Zend/tests/bug79784.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
4 changes: 2 additions & 2 deletions Zend/tests/bug79793.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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"
2 changes: 1 addition & 1 deletion Zend/tests/closures/closure_031.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ try {
}
?>
--EXPECT--
Warning: Undefined property: Closure::$a
NULL
Warning: Undefined property: Closure::$a
1 change: 1 addition & 0 deletions Zend/tests/coalesce/assign_coalesce_002.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class Dtor {
$ary = new AA;
try {
$ary[new Dtor][id($foo)] ??= $bar;
(function () {})();
} catch (Exception $e) {
echo $e->getMessage(), "\n";
}
Expand Down
Loading
Loading