Fix LSB handling for closures

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.
This commit is contained in:
Nikita Popov 2015-05-05 21:06:34 +02:00
parent dc546bdc4d
commit d9c2959c27
6 changed files with 75 additions and 19 deletions

25
Zend/tests/bug69568.phpt Normal file
View File

@ -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

View File

@ -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

View File

@ -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 */
@ -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();
@ -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);
}
/* }}} */
@ -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);
}
/* }}} */
@ -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;
}
@ -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;
@ -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) {

View File

@ -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);

View File

@ -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();

View File

@ -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();