Skip to content

Commit 2023346

Browse files
committedJan 15, 2018
Fixed bug #75079
1 parent 089a321 commit 2023346

File tree

4 files changed

+96
-13
lines changed

4 files changed

+96
-13
lines changed
 

‎NEWS

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ PHP NEWS
99
. Fixed bug #75799 (arg of get_defined_functions is optional). (carusogabriel)
1010
. Fixed bug #75396 (Exit inside generator finally results in fatal error).
1111
(Nikita)
12+
. Fixed bug #75079 (self keyword leads to incorrectly generated TypeError when
13+
in closure in trait). (Nikita)
1214

1315
- IMAP:
1416
. Fixed bug #75774 (imap_append HeapCorruction). (Anatol)

‎Zend/tests/bug75079.phpt

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
Bug #75079: self keyword leads to incorrectly generated TypeError when in closure in trait
3+
--FILE--
4+
<?php
5+
6+
trait Foo
7+
{
8+
public function selfDo(self ...$Selfs)
9+
{
10+
array_map(
11+
function (self $Self) : self
12+
{
13+
return $Self;
14+
},
15+
$Selfs
16+
);
17+
}
18+
}
19+
20+
class Bar
21+
{
22+
use Foo;
23+
}
24+
25+
class Baz
26+
{
27+
use Foo;
28+
}
29+
30+
$Bar = new Bar;
31+
$Baz = new Baz;
32+
33+
$Bar->selfDo($Bar, $Bar);
34+
$Baz->selfDo($Baz, $Baz);
35+
36+
?>
37+
===DONE===
38+
--EXPECT--
39+
===DONE===

‎Zend/tests/bug75079_2.phpt

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
Bug #75079 variation without traits
3+
--FILE--
4+
<?php
5+
6+
class Foo
7+
{
8+
private static $bar = 123;
9+
10+
static function test(){
11+
return function(){
12+
return function(){
13+
return Foo::$bar;
14+
};
15+
};
16+
}
17+
}
18+
19+
20+
$f = Foo::test();
21+
22+
var_dump($f()());
23+
24+
class A{}
25+
$a = new A;
26+
var_dump($f->bindTo($a, A::CLASS)()());
27+
28+
?>
29+
--EXPECTF--
30+
int(123)
31+
32+
Fatal error: Uncaught Error: Cannot access private property Foo::$bar in %s:%d
33+
Stack trace:
34+
#0 %s(%d): A->{closure}()
35+
#1 {main}
36+
thrown in %s on line %d

‎Zend/zend_closures.c

+19-13
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ ZEND_METHOD(Closure, call)
186186
ZEND_METHOD(Closure, bind)
187187
{
188188
zval *newthis, *zclosure, *scope_arg = NULL;
189-
zend_closure *closure, *new_closure;
189+
zend_closure *closure;
190190
zend_class_entry *ce, *called_scope;
191191

192192
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oo!|z", &zclosure, zend_ce_closure, &newthis, &scope_arg) == FAILURE) {
@@ -226,15 +226,6 @@ ZEND_METHOD(Closure, bind)
226226
}
227227

228228
zend_create_closure(return_value, &closure->func, ce, called_scope, newthis);
229-
new_closure = (zend_closure *) Z_OBJ_P(return_value);
230-
231-
/* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */
232-
if (ZEND_USER_CODE(closure->func.type) && (closure->func.common.scope != new_closure->func.common.scope || (closure->func.op_array.fn_flags & ZEND_ACC_NO_RT_ARENA))) {
233-
new_closure->func.op_array.run_time_cache = emalloc(new_closure->func.op_array.cache_size);
234-
memset(new_closure->func.op_array.run_time_cache, 0, new_closure->func.op_array.cache_size);
235-
236-
new_closure->func.op_array.fn_flags |= ZEND_ACC_NO_RT_ARENA;
237-
}
238229
}
239230
/* }}} */
240231

@@ -669,9 +660,24 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent
669660
closure->func.op_array.static_variables =
670661
zend_array_dup(closure->func.op_array.static_variables);
671662
}
672-
if (UNEXPECTED(!closure->func.op_array.run_time_cache)) {
673-
closure->func.op_array.run_time_cache = func->op_array.run_time_cache = zend_arena_alloc(&CG(arena), func->op_array.cache_size);
674-
memset(func->op_array.run_time_cache, 0, func->op_array.cache_size);
663+
664+
/* Runtime cache is scope-dependent, so we cannot reuse it if the scope changed */
665+
if (!closure->func.op_array.run_time_cache
666+
|| func->common.scope != scope
667+
|| (func->common.fn_flags & ZEND_ACC_NO_RT_ARENA)
668+
) {
669+
if (!func->op_array.run_time_cache && (func->common.fn_flags & ZEND_ACC_CLOSURE)) {
670+
/* If a real closure is used for the first time, we create a shared runtime cache
671+
* and remember which scope it is for. */
672+
func->common.scope = scope;
673+
func->op_array.run_time_cache = zend_arena_alloc(&CG(arena), func->op_array.cache_size);
674+
closure->func.op_array.run_time_cache = func->op_array.run_time_cache;
675+
} else {
676+
/* Otherwise, we use a non-shared runtime cache */
677+
closure->func.op_array.run_time_cache = emalloc(func->op_array.cache_size);
678+
closure->func.op_array.fn_flags |= ZEND_ACC_NO_RT_ARENA;
679+
}
680+
memset(closure->func.op_array.run_time_cache, 0, func->op_array.cache_size);
675681
}
676682
if (closure->func.op_array.refcount) {
677683
(*closure->func.op_array.refcount)++;

0 commit comments

Comments
 (0)