Skip to content

Commit

Permalink
Fixed bug #75079
Browse files Browse the repository at this point in the history
  • Loading branch information
nikic committed Jan 15, 2018
1 parent 089a321 commit 2023346
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 13 deletions.
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ PHP NEWS
. Fixed bug #75799 (arg of get_defined_functions is optional). (carusogabriel)
. Fixed bug #75396 (Exit inside generator finally results in fatal error).
(Nikita)
. Fixed bug #75079 (self keyword leads to incorrectly generated TypeError when
in closure in trait). (Nikita)

- IMAP:
. Fixed bug #75774 (imap_append HeapCorruction). (Anatol)
Expand Down
39 changes: 39 additions & 0 deletions Zend/tests/bug75079.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
--TEST--
Bug #75079: self keyword leads to incorrectly generated TypeError when in closure in trait
--FILE--
<?php

trait Foo
{
public function selfDo(self ...$Selfs)
{
array_map(
function (self $Self) : self
{
return $Self;
},
$Selfs
);
}
}

class Bar
{
use Foo;
}

class Baz
{
use Foo;
}

$Bar = new Bar;
$Baz = new Baz;

$Bar->selfDo($Bar, $Bar);
$Baz->selfDo($Baz, $Baz);

?>
===DONE===
--EXPECT--
===DONE===
36 changes: 36 additions & 0 deletions Zend/tests/bug75079_2.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
Bug #75079 variation without traits
--FILE--
<?php

class Foo
{
private static $bar = 123;

static function test(){
return function(){
return function(){
return Foo::$bar;
};
};
}
}


$f = Foo::test();

var_dump($f()());

class A{}
$a = new A;
var_dump($f->bindTo($a, A::CLASS)()());

?>
--EXPECTF--
int(123)

Fatal error: Uncaught Error: Cannot access private property Foo::$bar in %s:%d
Stack trace:
#0 %s(%d): A->{closure}()
#1 {main}
thrown in %s on line %d
32 changes: 19 additions & 13 deletions Zend/zend_closures.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ ZEND_METHOD(Closure, call)
ZEND_METHOD(Closure, bind)
{
zval *newthis, *zclosure, *scope_arg = NULL;
zend_closure *closure, *new_closure;
zend_closure *closure;
zend_class_entry *ce, *called_scope;

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

zend_create_closure(return_value, &closure->func, ce, called_scope, newthis);
new_closure = (zend_closure *) Z_OBJ_P(return_value);

/* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */
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))) {
new_closure->func.op_array.run_time_cache = emalloc(new_closure->func.op_array.cache_size);
memset(new_closure->func.op_array.run_time_cache, 0, new_closure->func.op_array.cache_size);

new_closure->func.op_array.fn_flags |= ZEND_ACC_NO_RT_ARENA;
}
}
/* }}} */

Expand Down Expand Up @@ -669,9 +660,24 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent
closure->func.op_array.static_variables =
zend_array_dup(closure->func.op_array.static_variables);
}
if (UNEXPECTED(!closure->func.op_array.run_time_cache)) {
closure->func.op_array.run_time_cache = func->op_array.run_time_cache = zend_arena_alloc(&CG(arena), func->op_array.cache_size);
memset(func->op_array.run_time_cache, 0, func->op_array.cache_size);

/* Runtime cache is scope-dependent, so we cannot reuse it if the scope changed */
if (!closure->func.op_array.run_time_cache
|| func->common.scope != scope
|| (func->common.fn_flags & ZEND_ACC_NO_RT_ARENA)
) {
if (!func->op_array.run_time_cache && (func->common.fn_flags & ZEND_ACC_CLOSURE)) {
/* If a real closure is used for the first time, we create a shared runtime cache
* and remember which scope it is for. */
func->common.scope = scope;
func->op_array.run_time_cache = zend_arena_alloc(&CG(arena), func->op_array.cache_size);
closure->func.op_array.run_time_cache = func->op_array.run_time_cache;
} else {
/* Otherwise, we use a non-shared runtime cache */
closure->func.op_array.run_time_cache = emalloc(func->op_array.cache_size);
closure->func.op_array.fn_flags |= ZEND_ACC_NO_RT_ARENA;
}
memset(closure->func.op_array.run_time_cache, 0, func->op_array.cache_size);
}
if (closure->func.op_array.refcount) {
(*closure->func.op_array.refcount)++;
Expand Down

0 comments on commit 2023346

Please sign in to comment.