Skip to content

Commit

Permalink
Fix LSB handling for closures
Browse files Browse the repository at this point in the history
Closures will now use the called_scope from their instantiation
site. If they are rebound either the class of $this is used or if
no $this is provided the bound scope is used.

With this change the scope for static closures can be changed back
to use EG(scope) rather than EX(called_scope), thus fixing
bug #69568.
  • Loading branch information
nikic committed May 5, 2015
1 parent dc546bd commit d9c2959
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 19 deletions.
25 changes: 25 additions & 0 deletions Zend/tests/bug69568.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
Bug #69568: call a private function in closure failed
--FILE--
<?php
class A {

private static function testprivate() {
return 1;
}
public static function test() {
return function() {
return self::testprivate();
};
}
}

class B extends A {
}

$fn = B::test();
echo $fn();

?>
--EXPECT--
1
21 changes: 21 additions & 0 deletions Zend/tests/parent_class_name_without_parent.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
Getting parent class name when there is no parent generates an error
--FILE--
<?php

trait T {
public function f() {
var_dump(parent::class);
}
}

class C {
use T;
}

(new C)->f();

?>
--EXPECTF--
Fatal error: Cannot use "parent" when current class scope has no parent in %s on line 5

34 changes: 20 additions & 14 deletions Zend/zend_closures.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@
zend_error(E_EXCEPTION | E_ERROR, "Closure object cannot have properties")

typedef struct _zend_closure {
zend_object std;
zend_function func;
zval this_ptr;
zend_object std;
zend_function func;
zval this_ptr;
zend_class_entry *called_scope;
} zend_closure;

/* non-static since it needs to be referenced */
Expand Down Expand Up @@ -129,7 +130,7 @@ ZEND_METHOD(Closure, bind)
{
zval *newthis, *zclosure, *scope_arg = NULL;
zend_closure *closure;
zend_class_entry *ce;
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) {
RETURN_NULL();
Expand Down Expand Up @@ -161,7 +162,13 @@ ZEND_METHOD(Closure, bind)
ce = closure->func.common.scope;
}

zend_create_closure(return_value, &closure->func, ce, newthis);
if (newthis) {
called_scope = Z_OBJCE_P(newthis);
} else {
called_scope = ce;
}

zend_create_closure(return_value, &closure->func, ce, called_scope, newthis);
}
/* }}} */

Expand Down Expand Up @@ -296,7 +303,8 @@ static zend_object *zend_closure_clone(zval *zobject) /* {{{ */
zend_closure *closure = (zend_closure *)Z_OBJ_P(zobject);
zval result;

zend_create_closure(&result, &closure->func, closure->func.common.scope, &closure->this_ptr);
zend_create_closure(&result, &closure->func,
closure->func.common.scope, closure->called_scope, &closure->this_ptr);
return Z_OBJ(result);
}
/* }}} */
Expand All @@ -311,17 +319,14 @@ int zend_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function

closure = (zend_closure *)Z_OBJ_P(obj);
*fptr_ptr = &closure->func;
*ce_ptr = closure->called_scope;

if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
if (obj_ptr) {
if (obj_ptr) {
if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
*obj_ptr = Z_OBJ(closure->this_ptr);
}
*ce_ptr = Z_OBJCE(closure->this_ptr);
} else {
if (obj_ptr) {
} else {
*obj_ptr = NULL;
}
*ce_ptr = closure->func.common.scope;
}
return SUCCESS;
}
Expand Down Expand Up @@ -457,7 +462,7 @@ void zend_register_closure_ce(void) /* {{{ */
}
/* }}} */

ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zval *this_ptr) /* {{{ */
ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */
{
zend_closure *closure;

Expand Down Expand Up @@ -512,6 +517,7 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent
* If the closure is unscoped, it has no bound object.
* The the closure is scoped, it's either static or it's bound */
closure->func.common.scope = scope;
closure->called_scope = called_scope;
if (scope) {
closure->func.common.fn_flags |= ZEND_ACC_PUBLIC;
if (this_ptr && Z_TYPE_P(this_ptr) == IS_OBJECT && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0) {
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_closures.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ void zend_register_closure_ce(void);

extern ZEND_API zend_class_entry *zend_ce_closure;

ZEND_API void zend_create_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zval *this_ptr);
ZEND_API void zend_create_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr);
ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *obj);
ZEND_API const zend_function *zend_get_closure_method_def(zval *obj);
ZEND_API zval* zend_get_closure_this_ptr(zval *obj);
Expand Down
6 changes: 4 additions & 2 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -7301,9 +7301,11 @@ ZEND_VM_HANDLER(153, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, UNUSED)

if (UNEXPECTED((Z_FUNC_P(zfunc)->common.fn_flags & ZEND_ACC_STATIC) ||
(EX(func)->common.fn_flags & ZEND_ACC_STATIC))) {
zend_create_closure(EX_VAR(opline->result.var), Z_FUNC_P(zfunc), EX(called_scope), NULL);
zend_create_closure(EX_VAR(opline->result.var), Z_FUNC_P(zfunc),
EG(scope), EX(called_scope), NULL);
} else {
zend_create_closure(EX_VAR(opline->result.var), Z_FUNC_P(zfunc), EG(scope), Z_OBJ(EX(This)) ? &EX(This) : NULL);
zend_create_closure(EX_VAR(opline->result.var), Z_FUNC_P(zfunc),
EG(scope), EX(called_scope), Z_OBJ(EX(This)) ? &EX(This) : NULL);
}

CHECK_EXCEPTION();
Expand Down
6 changes: 4 additions & 2 deletions Zend/zend_vm_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -7990,9 +7990,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_C

if (UNEXPECTED((Z_FUNC_P(zfunc)->common.fn_flags & ZEND_ACC_STATIC) ||
(EX(func)->common.fn_flags & ZEND_ACC_STATIC))) {
zend_create_closure(EX_VAR(opline->result.var), Z_FUNC_P(zfunc), EX(called_scope), NULL);
zend_create_closure(EX_VAR(opline->result.var), Z_FUNC_P(zfunc),
EG(scope), EX(called_scope), NULL);
} else {
zend_create_closure(EX_VAR(opline->result.var), Z_FUNC_P(zfunc), EG(scope), Z_OBJ(EX(This)) ? &EX(This) : NULL);
zend_create_closure(EX_VAR(opline->result.var), Z_FUNC_P(zfunc),
EG(scope), EX(called_scope), Z_OBJ(EX(This)) ? &EX(This) : NULL);
}

CHECK_EXCEPTION();
Expand Down

0 comments on commit d9c2959

Please sign in to comment.