Skip to content

Commit d952419

Browse files
committed
Merge branch 'PHP-8.2'
* PHP-8.2: Fix GH-10011 (Trampoline autoloader will get reregistered and cannot be unregistered)
2 parents c4a0fc6 + 334d108 commit d952419

File tree

2 files changed

+76
-0
lines changed

2 files changed

+76
-0
lines changed

ext/spl/php_spl.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,16 @@ static autoload_func_info *autoload_func_info_from_fci(
401401

402402
static bool autoload_func_info_equals(
403403
const autoload_func_info *alfi1, const autoload_func_info *alfi2) {
404+
if (UNEXPECTED(
405+
(alfi1->func_ptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) &&
406+
(alfi2->func_ptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)
407+
)) {
408+
return alfi1->obj == alfi2->obj
409+
&& alfi1->ce == alfi2->ce
410+
&& alfi1->closure == alfi2->closure
411+
&& zend_string_equals(alfi1->func_ptr->common.function_name, alfi2->func_ptr->common.function_name)
412+
;
413+
}
404414
return alfi1->func_ptr == alfi2->func_ptr
405415
&& alfi1->obj == alfi2->obj
406416
&& alfi1->ce == alfi2->ce
@@ -580,6 +590,13 @@ PHP_FUNCTION(spl_autoload_unregister)
580590
RETURN_TRUE;
581591
}
582592

593+
if (!fcc.function_handler) {
594+
/* Call trampoline has been cleared by zpp. Refetch it, because we want to deal
595+
* with it outselves. It is important that it is not refetched on every call,
596+
* because calls may occur from different scopes. */
597+
zend_is_callable_ex(&fci.function_name, NULL, 0, NULL, &fcc, NULL);
598+
}
599+
583600
autoload_func_info *alfi = autoload_func_info_from_fci(&fci, &fcc);
584601
Bucket *p = spl_find_registered_function(alfi);
585602
autoload_func_info_destroy(alfi);

ext/spl/tests/gh10011.phpt

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
--TEST--
2+
Bug GH-10011 (Trampoline autoloader will get reregistered and cannot be unregistered)
3+
--FILE--
4+
<?php
5+
6+
class TrampolineTest {
7+
public function __call(string $name, array $arguments) {
8+
echo 'Trampoline for ', $name, PHP_EOL;
9+
}
10+
}
11+
$o = new TrampolineTest();
12+
$callback1 = [$o, 'trampoline1'];
13+
$callback2 = [$o, 'trampoline2'];
14+
15+
spl_autoload_register($callback1);
16+
spl_autoload_register($callback2);
17+
spl_autoload_register($callback1); // 2nd call ignored
18+
19+
var_dump(spl_autoload_functions());
20+
21+
var_dump(class_exists("TestClass", true));
22+
23+
echo "Unregister trampoline:\n";
24+
var_dump(spl_autoload_unregister($callback1));
25+
var_dump(spl_autoload_unregister($callback1));
26+
var_dump(spl_autoload_unregister($callback2));
27+
28+
var_dump(spl_autoload_functions());
29+
var_dump(class_exists("TestClass", true));
30+
?>
31+
--EXPECT--
32+
array(2) {
33+
[0]=>
34+
array(2) {
35+
[0]=>
36+
object(TrampolineTest)#1 (0) {
37+
}
38+
[1]=>
39+
string(11) "trampoline1"
40+
}
41+
[1]=>
42+
array(2) {
43+
[0]=>
44+
object(TrampolineTest)#1 (0) {
45+
}
46+
[1]=>
47+
string(11) "trampoline2"
48+
}
49+
}
50+
Trampoline for trampoline1
51+
Trampoline for trampoline2
52+
bool(false)
53+
Unregister trampoline:
54+
bool(true)
55+
bool(false)
56+
bool(true)
57+
array(0) {
58+
}
59+
bool(false)

0 commit comments

Comments
 (0)