Skip to content

Commit 93f11d8

Browse files
Fix GH-8932: Provide a way to get the called-scope of closures (#9299)
Co-authored-by: Christoph M. Becker <cmbecker69@gmx.de>
1 parent 3d6ed8c commit 93f11d8

File tree

4 files changed

+100
-1
lines changed

4 files changed

+100
-1
lines changed

ext/reflection/php_reflection.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,6 +1633,28 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureScopeClass)
16331633
}
16341634
/* }}} */
16351635

1636+
/* {{{ Returns the called scope associated to the closure */
1637+
ZEND_METHOD(ReflectionFunctionAbstract, getClosureCalledClass)
1638+
{
1639+
reflection_object *intern;
1640+
1641+
if (zend_parse_parameters_none() == FAILURE) {
1642+
RETURN_THROWS();
1643+
}
1644+
GET_REFLECTION_OBJECT();
1645+
if (!Z_ISUNDEF(intern->obj)) {
1646+
zend_class_entry *called_scope;
1647+
zend_function *closure_func;
1648+
zend_object *object;
1649+
if (Z_OBJ_HANDLER(intern->obj, get_closure)
1650+
&& Z_OBJ_HANDLER(intern->obj, get_closure)(Z_OBJ(intern->obj), &called_scope, &closure_func, &object, 1) == SUCCESS
1651+
&& closure_func && (called_scope || closure_func->common.scope)) {
1652+
zend_reflection_class_factory(called_scope ? (zend_class_entry *) called_scope : closure_func->common.scope, return_value);
1653+
}
1654+
}
1655+
}
1656+
/* }}} */
1657+
16361658
/* {{{ Returns a dynamically created closure for the function */
16371659
ZEND_METHOD(ReflectionFunction, getClosure)
16381660
{

ext/reflection/php_reflection.stub.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ public function getClosureThis() {}
4848
/** @return ReflectionClass|null */
4949
public function getClosureScopeClass() {}
5050

51+
/** @return ReflectionClass|null */
52+
public function getClosureCalledClass() {}
53+
5154
/** @return string|false */
5255
public function getDocComment() {}
5356

ext/reflection/php_reflection_arginfo.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: 6e98777552147f4a413db16ecd87c9a6931f9c00 */
2+
* Stub hash: 9309c0d567aae3041255b5f9b9782add9b6ac783 */
33

44
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 0, 1)
55
ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0)
@@ -27,6 +27,8 @@ ZEND_END_ARG_INFO()
2727

2828
#define arginfo_class_ReflectionFunctionAbstract_getClosureScopeClass arginfo_class_ReflectionFunctionAbstract_inNamespace
2929

30+
#define arginfo_class_ReflectionFunctionAbstract_getClosureCalledClass arginfo_class_ReflectionFunctionAbstract_inNamespace
31+
3032
#define arginfo_class_ReflectionFunctionAbstract_getDocComment arginfo_class_ReflectionFunctionAbstract_inNamespace
3133

3234
#define arginfo_class_ReflectionFunctionAbstract_getEndLine arginfo_class_ReflectionFunctionAbstract_inNamespace
@@ -501,6 +503,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, isGenerator);
501503
ZEND_METHOD(ReflectionFunctionAbstract, isVariadic);
502504
ZEND_METHOD(ReflectionFunctionAbstract, getClosureThis);
503505
ZEND_METHOD(ReflectionFunctionAbstract, getClosureScopeClass);
506+
ZEND_METHOD(ReflectionFunctionAbstract, getClosureCalledClass);
504507
ZEND_METHOD(ReflectionFunctionAbstract, getDocComment);
505508
ZEND_METHOD(ReflectionFunctionAbstract, getEndLine);
506509
ZEND_METHOD(ReflectionFunctionAbstract, getExtension);
@@ -719,6 +722,7 @@ static const zend_function_entry class_ReflectionFunctionAbstract_methods[] = {
719722
ZEND_ME(ReflectionFunctionAbstract, isVariadic, arginfo_class_ReflectionFunctionAbstract_isVariadic, ZEND_ACC_PUBLIC)
720723
ZEND_ME(ReflectionFunctionAbstract, getClosureThis, arginfo_class_ReflectionFunctionAbstract_getClosureThis, ZEND_ACC_PUBLIC)
721724
ZEND_ME(ReflectionFunctionAbstract, getClosureScopeClass, arginfo_class_ReflectionFunctionAbstract_getClosureScopeClass, ZEND_ACC_PUBLIC)
725+
ZEND_ME(ReflectionFunctionAbstract, getClosureCalledClass, arginfo_class_ReflectionFunctionAbstract_getClosureCalledClass, ZEND_ACC_PUBLIC)
722726
ZEND_ME(ReflectionFunctionAbstract, getDocComment, arginfo_class_ReflectionFunctionAbstract_getDocComment, ZEND_ACC_PUBLIC)
723727
ZEND_ME(ReflectionFunctionAbstract, getEndLine, arginfo_class_ReflectionFunctionAbstract_getEndLine, ZEND_ACC_PUBLIC)
724728
ZEND_ME(ReflectionFunctionAbstract, getExtension, arginfo_class_ReflectionFunctionAbstract_getExtension, ZEND_ACC_PUBLIC)
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
--TEST--
2+
GH-8932 (Provide a way to get the called-scope of closures)
3+
--FILE--
4+
<?php
5+
class A {
6+
public static function __callStatic($name, $args) {
7+
echo static::class.'::'.$name, "\n";
8+
}
9+
10+
public function __call($name, $args) {
11+
echo static::class.'->'.$name, "\n";
12+
}
13+
14+
public static function b() {
15+
echo static::class.'::b', "\n";
16+
}
17+
18+
19+
public function c() {
20+
echo static::class.'->c', "\n";
21+
}
22+
}
23+
24+
class B extends A {}
25+
26+
$c = ['B', 'b'];
27+
$d = \Closure::fromCallable($c);
28+
$r = new \ReflectionFunction($d);
29+
var_dump($r->getClosureCalledClass());
30+
$d();
31+
32+
$c = [new B(), 'c'];
33+
$d = \Closure::fromCallable($c);
34+
$r = new \ReflectionFunction($d);
35+
var_dump($r->getClosureCalledClass());
36+
$d();
37+
38+
$c = ['B', 'd'];
39+
$d = \Closure::fromCallable($c);
40+
$r = new \ReflectionFunction($d);
41+
var_dump($r->getClosureCalledClass());
42+
$d();
43+
44+
$c = [new B(), 'e'];
45+
$d = \Closure::fromCallable($c);
46+
$r = new \ReflectionFunction($d);
47+
var_dump($r->getClosureCalledClass());
48+
$d();
49+
?>
50+
--EXPECTF--
51+
object(ReflectionClass)#%d (1) {
52+
["name"]=>
53+
string(1) "B"
54+
}
55+
B::b
56+
object(ReflectionClass)#%d (1) {
57+
["name"]=>
58+
string(1) "B"
59+
}
60+
B->c
61+
object(ReflectionClass)#%d (1) {
62+
["name"]=>
63+
string(1) "B"
64+
}
65+
B::d
66+
object(ReflectionClass)#%d (1) {
67+
["name"]=>
68+
string(1) "B"
69+
}
70+
B->e

0 commit comments

Comments
 (0)