]> granicus.if.org Git - php/commitdiff
Fix LSB handling for closures
authorNikita Popov <nikic@php.net>
Tue, 5 May 2015 19:06:34 +0000 (21:06 +0200)
committerNikita Popov <nikic@php.net>
Tue, 5 May 2015 19:14:03 +0000 (21:14 +0200)
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.

Zend/tests/bug69568.phpt [new file with mode: 0644]
Zend/tests/parent_class_name_without_parent.phpt [new file with mode: 0644]
Zend/zend_closures.c
Zend/zend_closures.h
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

diff --git a/Zend/tests/bug69568.phpt b/Zend/tests/bug69568.phpt
new file mode 100644 (file)
index 0000000..18ec941
--- /dev/null
@@ -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
diff --git a/Zend/tests/parent_class_name_without_parent.phpt b/Zend/tests/parent_class_name_without_parent.phpt
new file mode 100644 (file)
index 0000000..0e67173
--- /dev/null
@@ -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
+
index 952e4ed1e0fedf38390f9080fda90efd1d0671db..97f0cbf57a59b5dd55dad1600add598e22dd11bf 100644 (file)
        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) {
index 9862bc07cf67f8e9eb29728dd49e803f8420b9a0..8d0963ec1799632ff802d5a703c9a443867369f7 100644 (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);
index 089987817ac1bcea57483472cdc0df69a4521af4..e00ee07d8454cceea284b54af2dac1c9a7ae97d7 100644 (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();
index 3a61b5702f29acc18f335dd7ce89305f6f1067c3..85075af2a6fd1c3c2f2125c4159d55ac8e77c3db 100644 (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();