Skip to content

Commit 955da0a

Browse files
committed
zend_compile: Set op_array->scope for Closures as compile time
see #18546
1 parent 4122daa commit 955da0a

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

Zend/zend_compile.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8306,6 +8306,12 @@ static zend_op_array *zend_compile_func_decl_ex(
83068306

83078307
if (decl->kind == ZEND_AST_CLOSURE || decl->kind == ZEND_AST_ARROW_FUNC) {
83088308
op_array->fn_flags |= ZEND_ACC_CLOSURE;
8309+
/* Set the closure scope at compile time as an optimization to
8310+
* prevent creating a separate runtime cache for every initialization
8311+
* of this closure. Most closures are expected not to change their
8312+
* scope in practice.
8313+
*/
8314+
op_array->scope = CG(active_class_entry);
83098315
}
83108316

83118317
if (is_hook) {
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
--TEST--
2+
Observer: Closures keep their runtime cache when not rebinding
3+
--EXTENSIONS--
4+
zend_test
5+
--INI--
6+
zend_test.observer.enabled=1
7+
zend_test.observer.show_output=1
8+
zend_test.observer.observe_all=1
9+
--FILE--
10+
<?php
11+
12+
class Foo {
13+
private static $value = "x";
14+
function static() {
15+
(static function () {
16+
echo Foo::$value, PHP_EOL;
17+
})();
18+
}
19+
function non_static() {
20+
(function () {
21+
echo Foo::$value, PHP_EOL;
22+
})();
23+
}
24+
function rebind() {
25+
((function () {
26+
try {
27+
echo Foo::$value, PHP_EOL;
28+
} catch (Error $e) {
29+
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
30+
}
31+
})->bindTo(null, null))();
32+
}
33+
}
34+
35+
$obj = new Foo();
36+
$obj->static();
37+
$obj->static();
38+
$obj->non_static();
39+
$obj->non_static();
40+
$obj->rebind();
41+
$obj->rebind();
42+
43+
$closure = function () {
44+
echo Foo::$value, PHP_EOL;
45+
};
46+
47+
($closure->bindTo(null, Foo::class))();
48+
($closure->bindTo(null, Foo::class))();
49+
50+
$boundClosure = $closure->bindTo(null, Foo::class);
51+
$boundClosure();
52+
$boundClosure();
53+
54+
?>
55+
--EXPECTF--
56+
<!-- init '%s' -->
57+
<file '%s'>
58+
<!-- init Foo::static() -->
59+
<Foo::static>
60+
<!-- init Foo::{closure:Foo::static():6}() -->
61+
<Foo::{closure:Foo::static():6}>
62+
x
63+
</Foo::{closure:Foo::static():6}>
64+
</Foo::static>
65+
<Foo::static>
66+
<Foo::{closure:Foo::static():6}>
67+
x
68+
</Foo::{closure:Foo::static():6}>
69+
</Foo::static>
70+
<!-- init Foo::non_static() -->
71+
<Foo::non_static>
72+
<!-- init Foo::{closure:Foo::non_static():11}() -->
73+
<Foo::{closure:Foo::non_static():11}>
74+
x
75+
</Foo::{closure:Foo::non_static():11}>
76+
</Foo::non_static>
77+
<Foo::non_static>
78+
<Foo::{closure:Foo::non_static():11}>
79+
x
80+
</Foo::{closure:Foo::non_static():11}>
81+
</Foo::non_static>
82+
<!-- init Foo::rebind() -->
83+
<Foo::rebind>
84+
<!-- init Closure::bindTo() -->
85+
<Closure::bindTo>
86+
</Closure::bindTo>
87+
<!-- init {closure:Foo::rebind():16}() -->
88+
<{closure:Foo::rebind():16}>
89+
Error: <!-- init Error::getMessage() -->
90+
<Error::getMessage>
91+
</Error::getMessage>
92+
Cannot access private property Foo::$value
93+
</{closure:Foo::rebind():16}>
94+
</Foo::rebind>
95+
<Foo::rebind>
96+
<Closure::bindTo>
97+
</Closure::bindTo>
98+
<!-- init {closure:Foo::rebind():16}() -->
99+
<{closure:Foo::rebind():16}>
100+
Error: <Error::getMessage>
101+
</Error::getMessage>
102+
Cannot access private property Foo::$value
103+
</{closure:Foo::rebind():16}>
104+
</Foo::rebind>
105+
<Closure::bindTo>
106+
</Closure::bindTo>
107+
<!-- init Foo::{closure:%s:%d}() -->
108+
<Foo::{closure:%s:%d}>
109+
x
110+
</Foo::{closure:%s:%d}>
111+
<Closure::bindTo>
112+
</Closure::bindTo>
113+
<!-- init Foo::{closure:%s:%d}() -->
114+
<Foo::{closure:%s:%d}>
115+
x
116+
</Foo::{closure:%s:%d}>
117+
<Closure::bindTo>
118+
</Closure::bindTo>
119+
<!-- init Foo::{closure:%s:%d}() -->
120+
<Foo::{closure:%s:%d}>
121+
x
122+
</Foo::{closure:%s:%d}>
123+
<Foo::{closure:%s:%d}>
124+
x
125+
</Foo::{closure:%s:%d}>
126+
</file '%s'>

0 commit comments

Comments
 (0)