Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 6 additions & 0 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -8306,6 +8306,12 @@ static zend_op_array *zend_compile_func_decl_ex(

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

if (is_hook) {
Expand Down
126 changes: 126 additions & 0 deletions ext/zend_test/tests/observer_closure_04.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
--TEST--
Observer: Closures keep their runtime cache when not rebinding
--EXTENSIONS--
zend_test
--INI--
zend_test.observer.enabled=1
zend_test.observer.show_output=1
zend_test.observer.observe_all=1
--FILE--
<?php

class Foo {
private static $value = "x";
function static() {
(static function () {
echo Foo::$value, PHP_EOL;
})();
}
function non_static() {
(function () {
echo Foo::$value, PHP_EOL;
})();
}
function rebind() {
((function () {
try {
echo Foo::$value, PHP_EOL;
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
}
})->bindTo(null, null))();
}
}

$obj = new Foo();
$obj->static();
$obj->static();
$obj->non_static();
$obj->non_static();
$obj->rebind();
$obj->rebind();

$closure = function () {
echo Foo::$value, PHP_EOL;
};

($closure->bindTo(null, Foo::class))();
($closure->bindTo(null, Foo::class))();

$boundClosure = $closure->bindTo(null, Foo::class);
$boundClosure();
$boundClosure();

?>
--EXPECTF--
<!-- init '%s' -->
<file '%s'>
<!-- init Foo::static() -->
<Foo::static>
<!-- init Foo::{closure:Foo::static():6}() -->
<Foo::{closure:Foo::static():6}>
x
</Foo::{closure:Foo::static():6}>
</Foo::static>
<Foo::static>
<Foo::{closure:Foo::static():6}>
x
</Foo::{closure:Foo::static():6}>
</Foo::static>
<!-- init Foo::non_static() -->
<Foo::non_static>
<!-- init Foo::{closure:Foo::non_static():11}() -->
<Foo::{closure:Foo::non_static():11}>
x
</Foo::{closure:Foo::non_static():11}>
</Foo::non_static>
<Foo::non_static>
<Foo::{closure:Foo::non_static():11}>
x
</Foo::{closure:Foo::non_static():11}>
</Foo::non_static>
<!-- init Foo::rebind() -->
<Foo::rebind>
<!-- init Closure::bindTo() -->
<Closure::bindTo>
</Closure::bindTo>
<!-- init {closure:Foo::rebind():16}() -->
<{closure:Foo::rebind():16}>
Error: <!-- init Error::getMessage() -->
<Error::getMessage>
</Error::getMessage>
Cannot access private property Foo::$value
</{closure:Foo::rebind():16}>
</Foo::rebind>
<Foo::rebind>
<Closure::bindTo>
</Closure::bindTo>
<!-- init {closure:Foo::rebind():16}() -->
<{closure:Foo::rebind():16}>
Error: <Error::getMessage>
</Error::getMessage>
Cannot access private property Foo::$value
</{closure:Foo::rebind():16}>
</Foo::rebind>
<Closure::bindTo>
</Closure::bindTo>
<!-- init Foo::{closure:%s:%d}() -->
<Foo::{closure:%s:%d}>
x
</Foo::{closure:%s:%d}>
<Closure::bindTo>
</Closure::bindTo>
<!-- init Foo::{closure:%s:%d}() -->
<Foo::{closure:%s:%d}>
x
</Foo::{closure:%s:%d}>
<Closure::bindTo>
</Closure::bindTo>
<!-- init Foo::{closure:%s:%d}() -->
<Foo::{closure:%s:%d}>
x
</Foo::{closure:%s:%d}>
<Foo::{closure:%s:%d}>
x
</Foo::{closure:%s:%d}>
</file '%s'>
Loading