Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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)

- Opcache:
. Fixed bug GH-19486 (Incorrect opline after deoptimization). (Arnaud)
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/first_class_callable/constexpr/autoload.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ var_dump(Closure);
Autoloading AutoloadedClass
object(Closure)#%d (1) {
["function"]=>
string(16) "withStaticMethod"
string(33) "AutoloadedClass::withStaticMethod"
}
Called AutoloadedClass::withStaticMethod
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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"]=>
Expand Down
Original file line number Diff line number Diff line change
@@ -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

Expand Down
108 changes: 108 additions & 0 deletions Zend/tests/first_class_callable/constexpr/gh19637_closure_scope.phpt
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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"]=>
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/first_class_callable/constexpr/static_call.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var_dump(Closure);
--EXPECTF--
object(Closure)#%d (2) {
["function"]=>
string(8) "myMethod"
string(13) "Foo::myMethod"
["parameter"]=>
array(1) {
["$foo"]=>
Expand Down
13 changes: 8 additions & 5 deletions Zend/zend_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down