Skip to content

Commit d7c5b32

Browse files
committed
Fix GH-21699: don't treat callables as valid if exception pending after deprecations
When resolving string callables using self::, parent::, or static::, zend_is_callable_check_class() emits E_DEPRECATED. If the user error handler throws, EG(exception) is set but the function could still return true, leading to trampoline allocation and a failed assertion in shutdown_executor(). Return false from zend_is_callable_check_class() when EG(exception) is set after handling. Add regression tests for self::, parent::, and static:: forms.
1 parent e60f880 commit d7c5b32

5 files changed

Lines changed: 100 additions & 0 deletions

File tree

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ PHP NEWS
33
?? ??? ????, PHP 8.6.0alpha1
44

55
- Core:
6+
. Fixed bug GH-21699 (Assertion failure in shutdown_executor when resolving
7+
self::/parent::/static:: callables if the error handler throws).
68
. Added first-class callable cache to share instances for the duration of the
79
request. (ilutov)
810
. It is now possible to use reference assign on WeakMap without the key

Zend/tests/gh_21699.phpt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
GH-21699: Assertion failure in shutdown_executor when error handler throws during self:: callable resolution
3+
--FILE--
4+
<?php
5+
set_error_handler(function () {
6+
throw new Exception;
7+
});
8+
class bar {
9+
public static function __callstatic($fusion, $b)
10+
{
11+
}
12+
public function test()
13+
{
14+
call_user_func('self::y');
15+
}
16+
}
17+
$x = new bar;
18+
$x->test();
19+
?>
20+
--EXPECTF--
21+
Fatal error: Uncaught Exception in %s:%d
22+
Stack trace:
23+
#0 %s(%d): {closure:%s}(%d, 'Use of "self" i%s', '%s', %d)
24+
#1 %s(%d): bar->test()
25+
#2 {main}
26+
27+
Next TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, (null) in %s:%d
28+
Stack trace:
29+
#0 %s(%d): bar->test()
30+
#1 {main}
31+
thrown in %s on line %d

Zend/tests/gh_21699_parent.phpt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
GH-21699 (parent::): no shutdown_executor trampoline assertion when error handler throws during parent:: callable resolution
3+
--FILE--
4+
<?php
5+
set_error_handler(function () {
6+
throw new Exception;
7+
});
8+
class Base {
9+
public static function __callStatic($name, $args)
10+
{
11+
}
12+
}
13+
class Child extends Base {
14+
public function test()
15+
{
16+
call_user_func('parent::missing');
17+
}
18+
}
19+
(new Child)->test();
20+
?>
21+
--EXPECTF--
22+
Fatal error: Uncaught Exception in %s:%d
23+
Stack trace:
24+
#0 %s(%d): {closure:%s}(%d, 'Use of "parent"%s', '%s', %d)
25+
#1 %s(%d): Child->test()
26+
#2 {main}
27+
28+
Next TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, (null) in %s:%d
29+
Stack trace:
30+
#0 %s(%d): Child->test()
31+
#1 {main}
32+
thrown in %s on line %d

Zend/tests/gh_21699_static.phpt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
GH-21699 (static::): no shutdown_executor trampoline assertion when error handler throws during static:: callable resolution
3+
--FILE--
4+
<?php
5+
set_error_handler(function () {
6+
throw new Exception;
7+
});
8+
class bar {
9+
public static function __callstatic($fusion, $b)
10+
{
11+
}
12+
public function test()
13+
{
14+
call_user_func('static::y');
15+
}
16+
}
17+
$x = new bar;
18+
$x->test();
19+
?>
20+
--EXPECTF--
21+
Fatal error: Uncaught Exception in %s:%d
22+
Stack trace:
23+
#0 %s(%d): {closure:%s}(%d, 'Use of "static"%s', '%s', %d)
24+
#1 %s(%d): bar->test()
25+
#2 {main}
26+
27+
Next TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, (null) in %s:%d
28+
Stack trace:
29+
#0 %s(%d): bar->test()
30+
#1 {main}
31+
thrown in %s on line %d

Zend/zend_API.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3770,6 +3770,10 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc
37703770
if (error) zend_spprintf(error, 0, "class \"%.*s\" not found", (int)name_len, ZSTR_VAL(name));
37713771
}
37723772
ZSTR_ALLOCA_FREE(lcname, use_heap);
3773+
/* User error handlers may throw from deprecations above; do not report callable as valid. */
3774+
if (UNEXPECTED(EG(exception))) {
3775+
return false;
3776+
}
37733777
return ret;
37743778
}
37753779
/* }}} */

0 commit comments

Comments
 (0)