Skip to content

Commit c93b467

Browse files
authored
zend_ast: Fix Closure scope for FCC in constant expression (#19639)
Fixes #19637
1 parent 7d5c8da commit c93b467

File tree

8 files changed

+124
-11
lines changed

8 files changed

+124
-11
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ PHP NEWS
55
- Core:
66
. Destructing non-array values (other than NULL) using [] or list() now
77
emits a warning. (Girgias)
8+
. Fixed bug GH-19637 (Incorrect Closure scope for FCC in constant
9+
expression). (timwolla)
810

911
- EXIF:
1012
. Added support to retrieve Exif from HEIF file. (Benstone Zhang)

Zend/tests/first_class_callable/constexpr/autoload.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ var_dump(Closure);
2626
Autoloading AutoloadedClass
2727
object(Closure)#%d (1) {
2828
["function"]=>
29-
string(16) "withStaticMethod"
29+
string(33) "AutoloadedClass::withStaticMethod"
3030
}
3131
Called AutoloadedClass::withStaticMethod

Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_001.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
FCC in initializer errors for static reference to instance method.
2+
FCC in initializer warns for static reference to trait method.
33
--FILE--
44
<?php
55

@@ -20,7 +20,7 @@ var_dump(Closure);
2020
Deprecated: Calling static trait method Foo::myMethod is deprecated, it should only be called on a class using the trait in %s on line %d
2121
object(Closure)#%d (2) {
2222
["function"]=>
23-
string(8) "myMethod"
23+
string(13) "Foo::myMethod"
2424
["parameter"]=>
2525
array(1) {
2626
["$foo"]=>

Zend/tests/first_class_callable/constexpr/error_static_call_trait_method_002.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
FCC in initializer errors for static reference to instance method (Exception).
2+
FCC in initializer emits deprecation for static reference to trait method (Exception).
33
--FILE--
44
<?php
55

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
--TEST--
2+
FCC in attribute sets Closure scope.
3+
--EXTENSIONS--
4+
reflection
5+
--FILE--
6+
<?php
7+
8+
#[Attribute(Attribute::TARGET_CLASS)]
9+
class Attr {
10+
public function __construct(public array $value) {}
11+
}
12+
13+
class F {
14+
public static function foreign() {}
15+
}
16+
17+
class G extends F { }
18+
19+
#[Attr([
20+
F::foreign(...),
21+
G::foreign(...),
22+
self::myMethod(...),
23+
strrev(...),
24+
])]
25+
class C {
26+
private static function myMethod(string $foo) {
27+
return "XXX";
28+
}
29+
30+
public static function foo() {
31+
foreach ([
32+
F::foreign(...),
33+
G::foreign(...),
34+
self::myMethod(...),
35+
strrev(...),
36+
] as $fn) {
37+
$r = new \ReflectionFunction($fn);
38+
var_dump($r->getClosureCalledClass());
39+
var_dump($r->getClosureScopeClass());
40+
}
41+
}
42+
}
43+
44+
foreach ((new ReflectionClass(C::class))->getAttributes() as $reflectionAttribute) {
45+
foreach ($reflectionAttribute->newInstance()->value as $fn) {
46+
$r = new \ReflectionFunction($fn);
47+
var_dump($r->getClosureCalledClass());
48+
var_dump($r->getClosureScopeClass());
49+
}
50+
}
51+
echo "=======\n";
52+
C::foo();
53+
54+
?>
55+
--EXPECTF--
56+
object(ReflectionClass)#%d (1) {
57+
["name"]=>
58+
string(1) "F"
59+
}
60+
object(ReflectionClass)#%d (1) {
61+
["name"]=>
62+
string(1) "F"
63+
}
64+
object(ReflectionClass)#%d (1) {
65+
["name"]=>
66+
string(1) "G"
67+
}
68+
object(ReflectionClass)#%d (1) {
69+
["name"]=>
70+
string(1) "F"
71+
}
72+
object(ReflectionClass)#%d (1) {
73+
["name"]=>
74+
string(1) "C"
75+
}
76+
object(ReflectionClass)#%d (1) {
77+
["name"]=>
78+
string(1) "C"
79+
}
80+
NULL
81+
NULL
82+
=======
83+
object(ReflectionClass)#%d (1) {
84+
["name"]=>
85+
string(1) "F"
86+
}
87+
object(ReflectionClass)#%d (1) {
88+
["name"]=>
89+
string(1) "F"
90+
}
91+
object(ReflectionClass)#%d (1) {
92+
["name"]=>
93+
string(1) "G"
94+
}
95+
object(ReflectionClass)#%d (1) {
96+
["name"]=>
97+
string(1) "F"
98+
}
99+
object(ReflectionClass)#%d (1) {
100+
["name"]=>
101+
string(1) "C"
102+
}
103+
object(ReflectionClass)#%d (1) {
104+
["name"]=>
105+
string(1) "C"
106+
}
107+
NULL
108+
NULL

Zend/tests/first_class_callable/constexpr/property_initializer_scope_003.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ var_dump(($c->d)("abc"));
2323
--EXPECTF--
2424
object(Closure)#%d (2) {
2525
["function"]=>
26-
string(11) "C::myMethod"
26+
string(11) "P::myMethod"
2727
["parameter"]=>
2828
array(1) {
2929
["$foo"]=>

Zend/tests/first_class_callable/constexpr/static_call.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ var_dump(Closure);
1919
--EXPECTF--
2020
object(Closure)#%d (2) {
2121
["function"]=>
22-
string(8) "myMethod"
22+
string(13) "Foo::myMethod"
2323
["parameter"]=>
2424
array(1) {
2525
["$foo"]=>

Zend/zend_ast.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
10551055
case ZEND_AST_STATIC_CALL:
10561056
{
10571057
zend_function *fptr;
1058+
zend_class_entry *called_scope = NULL;
10581059
switch (ast->kind) {
10591060
case ZEND_AST_CALL: {
10601061
ZEND_ASSERT(ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT);
@@ -1086,13 +1087,15 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
10861087
ZEND_ASSERT(ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT);
10871088
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast->child[2];
10881089

1090+
zend_class_entry *ce = zend_ast_fetch_class(ast->child[0], scope);
1091+
if (!ce) {
1092+
return FAILURE;
1093+
}
1094+
called_scope = ce;
1095+
10891096
fptr = ZEND_MAP_PTR_GET(fcc_ast->fptr);
10901097

10911098
if (!fptr) {
1092-
zend_class_entry *ce = zend_ast_fetch_class(ast->child[0], scope);
1093-
if (!ce) {
1094-
return FAILURE;
1095-
}
10961099
zend_string *method_name = zend_ast_get_str(ast->child[1]);
10971100
if (ce->get_static_method) {
10981101
fptr = ce->get_static_method(ce, method_name);
@@ -1145,7 +1148,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
11451148
}
11461149
}
11471150

1148-
zend_create_fake_closure(result, fptr, scope, scope, NULL);
1151+
zend_create_fake_closure(result, fptr, fptr->common.scope, called_scope, NULL);
11491152

11501153
return SUCCESS;
11511154
}

0 commit comments

Comments
 (0)