]> granicus.if.org Git - php/commitdiff
Fix bug 666222
authorLevi Morrison <levim@php.net>
Tue, 13 May 2014 03:53:08 +0000 (21:53 -0600)
committerStanislav Malyshev <stas@php.net>
Mon, 9 Jun 2014 01:59:44 +0000 (18:59 -0700)
This also adds some smaller, isolated tests related to bug 66622.

12 files changed:
Zend/tests/closure_049.phpt [new file with mode: 0644]
Zend/tests/closure_050.phpt [new file with mode: 0644]
Zend/tests/closure_051.phpt [new file with mode: 0644]
Zend/tests/closure_052.phpt [new file with mode: 0644]
Zend/tests/closure_053.phpt [new file with mode: 0644]
Zend/tests/closure_054.phpt [new file with mode: 0644]
Zend/tests/closure_055.phpt [new file with mode: 0644]
Zend/tests/closure_056.phpt [new file with mode: 0644]
Zend/tests/closure_bug66622.phpt [new file with mode: 0644]
Zend/zend_closures.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

diff --git a/Zend/tests/closure_049.phpt b/Zend/tests/closure_049.phpt
new file mode 100644 (file)
index 0000000..684b33d
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+Closure 049: static::class in static closure in non-static method.
+
+--FILE--
+<?php
+
+class A {
+    function foo() {
+        $f = static function() {
+            return static::class;
+        };
+        return $f();
+    }
+}
+
+class B extends A {}
+
+$b = new B;
+
+var_dump($b->foo());
+--EXPECT--
+string(1) "B"
diff --git a/Zend/tests/closure_050.phpt b/Zend/tests/closure_050.phpt
new file mode 100644 (file)
index 0000000..d43f325
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+Closure 050: static::class in non-static closure in non-static method.
+
+--FILE--
+<?php
+
+class A {
+    function foo() {
+        $f = function() {
+            return static::class;
+        };
+        return $f();
+    }
+}
+
+class B extends A {}
+
+$b = new B;
+var_dump($b->foo());
+
+--EXPECT--
+string(1) "B"
diff --git a/Zend/tests/closure_051.phpt b/Zend/tests/closure_051.phpt
new file mode 100644 (file)
index 0000000..78b28d7
--- /dev/null
@@ -0,0 +1,21 @@
+--TEST--
+Closure 051: static::class in static closure in static method.
+
+--FILE--
+<?php
+
+class A {
+    static function foo() {
+        $f = static function() {
+            return static::class;
+        };
+        return $f();
+    }
+}
+
+class B extends A {}
+
+var_dump(B::foo());
+
+--EXPECT--
+string(1) "B"
diff --git a/Zend/tests/closure_052.phpt b/Zend/tests/closure_052.phpt
new file mode 100644 (file)
index 0000000..f878515
--- /dev/null
@@ -0,0 +1,21 @@
+--TEST--
+Closure 052: static::class in non-static closure in static method.
+
+--FILE--
+<?php
+
+class A {
+    static function foo() {
+        $f = function() {
+            return static::class;
+        };
+        return $f();
+    }
+}
+
+class B extends A {}
+
+var_dump(B::foo());
+
+--EXPECT--
+string(1) "B"
diff --git a/Zend/tests/closure_053.phpt b/Zend/tests/closure_053.phpt
new file mode 100644 (file)
index 0000000..b1d76a2
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+Closure 053: self::class in static closure in non-static method.
+
+--FILE--
+<?php
+
+class A {
+    function foo() {
+        $f = static function() {
+            return self::class;
+        };
+        return $f();
+    }
+}
+
+class B extends A {}
+
+$b = new B;
+var_dump($b->foo());
+
+--EXPECT--
+string(1) "A"
diff --git a/Zend/tests/closure_054.phpt b/Zend/tests/closure_054.phpt
new file mode 100644 (file)
index 0000000..b2f87d1
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+Closure 054: self::class in non-static closure in non-static method.
+
+--FILE--
+<?php
+
+class A {
+    function foo() {
+        $f = function() {
+            return self::class;
+        };
+        return $f();
+    }
+}
+
+class B extends A {}
+
+$b = new B;
+var_dump($b->foo());
+
+--EXPECT--
+string(1) "A"
diff --git a/Zend/tests/closure_055.phpt b/Zend/tests/closure_055.phpt
new file mode 100644 (file)
index 0000000..047d72a
--- /dev/null
@@ -0,0 +1,21 @@
+--TEST--
+Closure 055: self::class in static closure in static method.
+
+--FILE--
+<?php
+
+class A {
+    static function foo() {
+        $f = static function() {
+            return self::class;
+        };
+        return $f();
+    }
+}
+
+class B extends A {}
+
+var_dump(B::foo());
+
+--EXPECT--
+string(1) "A"
diff --git a/Zend/tests/closure_056.phpt b/Zend/tests/closure_056.phpt
new file mode 100644 (file)
index 0000000..566de10
--- /dev/null
@@ -0,0 +1,21 @@
+--TEST--
+Closure 056: self::class in non-static closure in static method.
+
+--FILE--
+<?php
+
+class A {
+    static function foo() {
+        $f = function() {
+            return self::class;
+        };
+        return $f();
+    }
+}
+
+class B extends A {}
+
+var_dump(B::foo());
+
+--EXPECT--
+string(1) "A"
diff --git a/Zend/tests/closure_bug66622.phpt b/Zend/tests/closure_bug66622.phpt
new file mode 100644 (file)
index 0000000..1c9577d
--- /dev/null
@@ -0,0 +1,37 @@
+--TEST--
+Bug 66622: Closures do not correctly capture the late bound class (static::) in some cases
+
+--FILE--
+<?php
+class A {
+    static function name() { return 'A'; }
+    function foo() {
+        $fn = function() { return static::name(); };
+        echo static::name() . ' vs ' . $fn() . "\n";
+    }
+    function bar() {
+        $fn = static function() { return static::name(); };
+        echo static::name() . ' vs ' . $fn() . "\n";
+    }
+    static function baz() {
+        $fn = function() { return static::name(); };
+        echo static::name() . ' vs ' . $fn() . "\n";
+    }
+}
+class B extends A {
+    static function name() { return 'B'; }
+}
+
+function test() {
+    (new B)->foo();
+    (new B)->bar();
+    (new B)->baz();
+    B::baz();
+}
+test();
+
+--EXPECT--
+B vs B
+B vs B
+B vs B
+B vs B
index fcea56d811afb20bd55a3cd5b36a83a002bb0329..08127248268d9a8895aad78981b9e3ee918bfdfd 100644 (file)
@@ -486,6 +486,7 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent
                }
        }
 
+       closure->this_ptr = NULL;
        /* Invariants:
         * If the closure is unscoped, it has no bound object.
         * The the closure is scoped, it's either static or it's bound */
@@ -497,10 +498,7 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent
                        Z_ADDREF_P(this_ptr);
                } else {
                        closure->func.common.fn_flags |= ZEND_ACC_STATIC;
-                       closure->this_ptr = NULL;
                }
-       } else {
-               closure->this_ptr = NULL;
        }
 }
 /* }}} */
index 03cc9c45a0aaaba8ec1f0a45e327f33a6726cc62..f76e52961ae0d0143c2b781cf97e44a0ea4c52f4 100644 (file)
@@ -5179,6 +5179,7 @@ ZEND_VM_HANDLER(153, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, UNUSED)
 {
        USE_OPLINE
        zend_function *op_array;
+       int closure_is_static, closure_is_being_defined_inside_static_context;
 
        SAVE_OPLINE();
 
@@ -5187,7 +5188,13 @@ ZEND_VM_HANDLER(153, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, UNUSED)
                zend_error_noreturn(E_ERROR, "Base lambda function for closure not found");
        }
 
-       zend_create_closure(&EX_T(opline->result.var).tmp_var, (zend_function *) op_array, EG(scope), EG(This) TSRMLS_CC);
+       closure_is_static = op_array->common.fn_flags & ZEND_ACC_STATIC;
+       closure_is_being_defined_inside_static_context = EX(prev_execute_data) && EX(prev_execute_data)->function_state.function->common.fn_flags & ZEND_ACC_STATIC;
+       if (closure_is_static || closure_is_being_defined_inside_static_context) {
+               zend_create_closure(&EX_T(opline->result.var).tmp_var, (zend_function *) op_array,  EG(called_scope), NULL TSRMLS_CC);
+       } else {
+               zend_create_closure(&EX_T(opline->result.var).tmp_var, (zend_function *) op_array,  EG(scope), EG(This) TSRMLS_CC);
+       }
 
        CHECK_EXCEPTION();
        ZEND_VM_NEXT_OPCODE();
index 511a40b37e4065ae170a9d12867b94397b77beb4..94026a08efccf4a111727fb98acee6ae71cbe6a2 100644 (file)
@@ -6487,6 +6487,7 @@ static int ZEND_FASTCALL  ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_HANDLER
 {
        USE_OPLINE
        zend_function *op_array;
+       int closure_is_static, closure_is_being_defined_inside_static_context;
 
        SAVE_OPLINE();
 
@@ -6495,7 +6496,13 @@ static int ZEND_FASTCALL  ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_HANDLER
                zend_error_noreturn(E_ERROR, "Base lambda function for closure not found");
        }
 
-       zend_create_closure(&EX_T(opline->result.var).tmp_var, (zend_function *) op_array, EG(scope), EG(This) TSRMLS_CC);
+       closure_is_static = op_array->common.fn_flags & ZEND_ACC_STATIC;
+       closure_is_being_defined_inside_static_context = EX(prev_execute_data) && EX(prev_execute_data)->function_state.function->common.fn_flags & ZEND_ACC_STATIC;
+       if (closure_is_static || closure_is_being_defined_inside_static_context) {
+               zend_create_closure(&EX_T(opline->result.var).tmp_var, (zend_function *) op_array,  EG(called_scope), NULL TSRMLS_CC);
+       } else {
+               zend_create_closure(&EX_T(opline->result.var).tmp_var, (zend_function *) op_array,  EG(scope), EG(This) TSRMLS_CC);
+       }
 
        CHECK_EXCEPTION();
        ZEND_VM_NEXT_OPCODE();