mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
zend_ast: Fix Closure scope for FCC in constant expression (#19639)
Fixes php/php-src#19637
This commit is contained in:
2
NEWS
2
NEWS
@@ -5,6 +5,8 @@ PHP NEWS
|
||||
- Core:
|
||||
. Destructing non-array values (other than NULL) using [] or list() now
|
||||
emits a warning. (Girgias)
|
||||
. Fixed bug GH-19637 (Incorrect Closure scope for FCC in constant
|
||||
expression). (timwolla)
|
||||
|
||||
- EXIF:
|
||||
. Added support to retrieve Exif from HEIF file. (Benstone Zhang)
|
||||
|
||||
@@ -26,6 +26,6 @@ var_dump(Closure);
|
||||
Autoloading AutoloadedClass
|
||||
object(Closure)#%d (1) {
|
||||
["function"]=>
|
||||
string(16) "withStaticMethod"
|
||||
string(33) "AutoloadedClass::withStaticMethod"
|
||||
}
|
||||
Called AutoloadedClass::withStaticMethod
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
--TEST--
|
||||
FCC in initializer errors for static reference to instance method.
|
||||
FCC in initializer warns for static reference to trait method.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
@@ -20,7 +20,7 @@ var_dump(Closure);
|
||||
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
|
||||
object(Closure)#%d (2) {
|
||||
["function"]=>
|
||||
string(8) "myMethod"
|
||||
string(13) "Foo::myMethod"
|
||||
["parameter"]=>
|
||||
array(1) {
|
||||
["$foo"]=>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
--TEST--
|
||||
FCC in initializer errors for static reference to instance method (Exception).
|
||||
FCC in initializer emits deprecation for static reference to trait method (Exception).
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
--TEST--
|
||||
FCC in attribute sets Closure scope.
|
||||
--EXTENSIONS--
|
||||
reflection
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
#[Attribute(Attribute::TARGET_CLASS)]
|
||||
class Attr {
|
||||
public function __construct(public array $value) {}
|
||||
}
|
||||
|
||||
class F {
|
||||
public static function foreign() {}
|
||||
}
|
||||
|
||||
class G extends F { }
|
||||
|
||||
#[Attr([
|
||||
F::foreign(...),
|
||||
G::foreign(...),
|
||||
self::myMethod(...),
|
||||
strrev(...),
|
||||
])]
|
||||
class C {
|
||||
private static function myMethod(string $foo) {
|
||||
return "XXX";
|
||||
}
|
||||
|
||||
public static function foo() {
|
||||
foreach ([
|
||||
F::foreign(...),
|
||||
G::foreign(...),
|
||||
self::myMethod(...),
|
||||
strrev(...),
|
||||
] as $fn) {
|
||||
$r = new \ReflectionFunction($fn);
|
||||
var_dump($r->getClosureCalledClass());
|
||||
var_dump($r->getClosureScopeClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ((new ReflectionClass(C::class))->getAttributes() as $reflectionAttribute) {
|
||||
foreach ($reflectionAttribute->newInstance()->value as $fn) {
|
||||
$r = new \ReflectionFunction($fn);
|
||||
var_dump($r->getClosureCalledClass());
|
||||
var_dump($r->getClosureScopeClass());
|
||||
}
|
||||
}
|
||||
echo "=======\n";
|
||||
C::foo();
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
object(ReflectionClass)#%d (1) {
|
||||
["name"]=>
|
||||
string(1) "F"
|
||||
}
|
||||
object(ReflectionClass)#%d (1) {
|
||||
["name"]=>
|
||||
string(1) "F"
|
||||
}
|
||||
object(ReflectionClass)#%d (1) {
|
||||
["name"]=>
|
||||
string(1) "G"
|
||||
}
|
||||
object(ReflectionClass)#%d (1) {
|
||||
["name"]=>
|
||||
string(1) "F"
|
||||
}
|
||||
object(ReflectionClass)#%d (1) {
|
||||
["name"]=>
|
||||
string(1) "C"
|
||||
}
|
||||
object(ReflectionClass)#%d (1) {
|
||||
["name"]=>
|
||||
string(1) "C"
|
||||
}
|
||||
NULL
|
||||
NULL
|
||||
=======
|
||||
object(ReflectionClass)#%d (1) {
|
||||
["name"]=>
|
||||
string(1) "F"
|
||||
}
|
||||
object(ReflectionClass)#%d (1) {
|
||||
["name"]=>
|
||||
string(1) "F"
|
||||
}
|
||||
object(ReflectionClass)#%d (1) {
|
||||
["name"]=>
|
||||
string(1) "G"
|
||||
}
|
||||
object(ReflectionClass)#%d (1) {
|
||||
["name"]=>
|
||||
string(1) "F"
|
||||
}
|
||||
object(ReflectionClass)#%d (1) {
|
||||
["name"]=>
|
||||
string(1) "C"
|
||||
}
|
||||
object(ReflectionClass)#%d (1) {
|
||||
["name"]=>
|
||||
string(1) "C"
|
||||
}
|
||||
NULL
|
||||
NULL
|
||||
@@ -23,7 +23,7 @@ var_dump(($c->d)("abc"));
|
||||
--EXPECTF--
|
||||
object(Closure)#%d (2) {
|
||||
["function"]=>
|
||||
string(11) "C::myMethod"
|
||||
string(11) "P::myMethod"
|
||||
["parameter"]=>
|
||||
array(1) {
|
||||
["$foo"]=>
|
||||
|
||||
@@ -19,7 +19,7 @@ var_dump(Closure);
|
||||
--EXPECTF--
|
||||
object(Closure)#%d (2) {
|
||||
["function"]=>
|
||||
string(8) "myMethod"
|
||||
string(13) "Foo::myMethod"
|
||||
["parameter"]=>
|
||||
array(1) {
|
||||
["$foo"]=>
|
||||
|
||||
@@ -1055,6 +1055,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
|
||||
case ZEND_AST_STATIC_CALL:
|
||||
{
|
||||
zend_function *fptr;
|
||||
zend_class_entry *called_scope = NULL;
|
||||
switch (ast->kind) {
|
||||
case ZEND_AST_CALL: {
|
||||
ZEND_ASSERT(ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT);
|
||||
@@ -1086,13 +1087,15 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
|
||||
ZEND_ASSERT(ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT);
|
||||
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast->child[2];
|
||||
|
||||
zend_class_entry *ce = zend_ast_fetch_class(ast->child[0], scope);
|
||||
if (!ce) {
|
||||
return FAILURE;
|
||||
}
|
||||
called_scope = ce;
|
||||
|
||||
fptr = ZEND_MAP_PTR_GET(fcc_ast->fptr);
|
||||
|
||||
if (!fptr) {
|
||||
zend_class_entry *ce = zend_ast_fetch_class(ast->child[0], scope);
|
||||
if (!ce) {
|
||||
return FAILURE;
|
||||
}
|
||||
zend_string *method_name = zend_ast_get_str(ast->child[1]);
|
||||
if (ce->get_static_method) {
|
||||
fptr = ce->get_static_method(ce, method_name);
|
||||
@@ -1145,7 +1148,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
|
||||
}
|
||||
}
|
||||
|
||||
zend_create_fake_closure(result, fptr, scope, scope, NULL);
|
||||
zend_create_fake_closure(result, fptr, fptr->common.scope, called_scope, NULL);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user