Skip to content

Commit dcde9b8

Browse files
committed
Merge branch 'PHP-8.1' into PHP-8.2
* PHP-8.1: [ci skip] NEWS [ci skip] NEWS Add tests Fix phpGH-8932: Provide a way to get the called-scope of closures (php#9299)
2 parents 64b962b + 6deddd3 commit dcde9b8

File tree

4 files changed

+138
-1
lines changed

4 files changed

+138
-1
lines changed

ext/reflection/php_reflection.c

+22
Original file line numberDiff line numberDiff line change
@@ -1701,6 +1701,28 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureScopeClass)
17011701
}
17021702
/* }}} */
17031703

1704+
/* {{{ Returns the called scope associated to the closure */
1705+
ZEND_METHOD(ReflectionFunctionAbstract, getClosureCalledClass)
1706+
{
1707+
reflection_object *intern;
1708+
1709+
if (zend_parse_parameters_none() == FAILURE) {
1710+
RETURN_THROWS();
1711+
}
1712+
GET_REFLECTION_OBJECT();
1713+
if (!Z_ISUNDEF(intern->obj)) {
1714+
zend_class_entry *called_scope;
1715+
zend_function *closure_func;
1716+
zend_object *object;
1717+
if (Z_OBJ_HANDLER(intern->obj, get_closure)
1718+
&& Z_OBJ_HANDLER(intern->obj, get_closure)(Z_OBJ(intern->obj), &called_scope, &closure_func, &object, 1) == SUCCESS
1719+
&& closure_func && (called_scope || closure_func->common.scope)) {
1720+
zend_reflection_class_factory(called_scope ? (zend_class_entry *) called_scope : closure_func->common.scope, return_value);
1721+
}
1722+
}
1723+
}
1724+
/* }}} */
1725+
17041726
/* {{{ Returns an associative array containing the closures lexical scope variables */
17051727
ZEND_METHOD(ReflectionFunctionAbstract, getClosureUsedVariables)
17061728
{

ext/reflection/php_reflection.stub.php

+3
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ public function getClosureThis(): ?object {}
5454
/** @tentative-return-type */
5555
public function getClosureScopeClass(): ?ReflectionClass {}
5656

57+
/** @tentative-return-type */
58+
public function getClosureCalledClass(): ?ReflectionClass {}
59+
5760
public function getClosureUsedVariables(): array {}
5861

5962
/** @tentative-return-type */

ext/reflection/php_reflection_arginfo.h

+5-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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+
public function makeClosure() {
24+
return function () {
25+
echo static::class.'::{closure}'."\n";
26+
};
27+
}
28+
}
29+
30+
class B extends A {}
31+
32+
$c = ['B', 'b'];
33+
$d = \Closure::fromCallable($c);
34+
$r = new \ReflectionFunction($d);
35+
var_dump($r->getClosureCalledClass());
36+
$d();
37+
38+
$c = [new B(), 'c'];
39+
$d = \Closure::fromCallable($c);
40+
$r = new \ReflectionFunction($d);
41+
var_dump($r->getClosureCalledClass());
42+
$d();
43+
44+
$c = ['B', 'd'];
45+
$d = \Closure::fromCallable($c);
46+
$r = new \ReflectionFunction($d);
47+
var_dump($r->getClosureCalledClass());
48+
$d();
49+
50+
$c = [new B(), 'e'];
51+
$d = \Closure::fromCallable($c);
52+
$r = new \ReflectionFunction($d);
53+
var_dump($r->getClosureCalledClass());
54+
$d();
55+
56+
$c = ['A', 'b'];
57+
$d = \Closure::fromCallable($c);
58+
$r = new \ReflectionFunction($d);
59+
var_dump($r->getClosureCalledClass());
60+
$d();
61+
62+
$b = new B();
63+
$d = $b->makeClosure();
64+
$r = new \ReflectionFunction($d);
65+
var_dump($r->getClosureCalledClass());
66+
$d();
67+
68+
$d = function () {
69+
echo "{closure}\n";
70+
};
71+
$r = new \ReflectionFunction($d);
72+
var_dump($r->getClosureCalledClass());
73+
$d();
74+
75+
?>
76+
--EXPECTF--
77+
object(ReflectionClass)#%d (1) {
78+
["name"]=>
79+
string(1) "B"
80+
}
81+
B::b
82+
object(ReflectionClass)#%d (1) {
83+
["name"]=>
84+
string(1) "B"
85+
}
86+
B->c
87+
object(ReflectionClass)#%d (1) {
88+
["name"]=>
89+
string(1) "B"
90+
}
91+
B::d
92+
object(ReflectionClass)#%d (1) {
93+
["name"]=>
94+
string(1) "B"
95+
}
96+
B->e
97+
object(ReflectionClass)#%d (1) {
98+
["name"]=>
99+
string(1) "A"
100+
}
101+
A::b
102+
object(ReflectionClass)#%d (1) {
103+
["name"]=>
104+
string(1) "B"
105+
}
106+
B::{closure}
107+
NULL
108+
{closure}

0 commit comments

Comments
 (0)