]> granicus.if.org Git - php/commitdiff
Fix static closure error in call_user_func opcode
authorNikita Popov <nikic@php.net>
Fri, 8 May 2015 13:05:59 +0000 (15:05 +0200)
committerNikita Popov <nikic@php.net>
Fri, 8 May 2015 13:17:15 +0000 (15:17 +0200)
I'm assuming this is the only error that is_callable() can generate
with retval=1.

This problem manifested after making closures in static methods
not implicitly static, but would also occur when binding any non-static
closure to a scope without a $this.

Zend/tests/call_user_func_closure_from_static_method.phpt [new file with mode: 0644]
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

diff --git a/Zend/tests/call_user_func_closure_from_static_method.phpt b/Zend/tests/call_user_func_closure_from_static_method.phpt
new file mode 100644 (file)
index 0000000..b427b85
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+call_user_func() on non-static closure without $this inside a static method
+--FILE--
+<?php
+
+class A {
+    public static function exec(callable $c) {
+        return call_user_func($c);
+    }
+
+    public static function doSomething() {
+        return self::exec(function(){
+            return "okay";
+        });
+    }
+}
+
+var_dump(A::doSomething());
+
+?>
+--EXPECT--
+string(4) "okay"
index 95022ac2ccb8871ecbaed1cbad08717005d971db..7d324fe08f733cc167060833354591d1426ae0ca 100644 (file)
@@ -3314,9 +3314,6 @@ ZEND_VM_HANDLER(118, ZEND_INIT_USER_CALL, CONST, CONST|TMPVAR|CV)
        SAVE_OPLINE();
        function_name = GET_OP2_ZVAL_PTR(BP_VAR_R);
        if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) {
-               if (error) {
-                       efree(error);
-               }
                func = fcc.function_handler;
                if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
                        /* Delay closure destruction until its invocation */
@@ -3332,20 +3329,13 @@ ZEND_VM_HANDLER(118, ZEND_INIT_USER_CALL, CONST, CONST|TMPVAR|CV)
                if (object) {
                        call_info |= ZEND_CALL_RELEASE_THIS;
                        GC_REFCOUNT(object)++; /* For $this pointer */
-               } else if (func->common.scope &&
-                          !(func->common.fn_flags & ZEND_ACC_STATIC)) {
-                       if (func->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
-                               zend_error(E_DEPRECATED,
+               }
+               if (error) {
+                       efree(error);
+                       /* This is the only soft error is_callable() can generate */
+                       zend_error(E_DEPRECATED,
                                "Non-static method %s::%s() should not be called statically",
                                func->common.scope->name->val, func->common.function_name->val);
-                       } else {
-                               zend_error(
-                                       E_EXCEPTION | E_ERROR,
-                                       "Non-static method %s::%s() cannot be called statically",
-                                       func->common.scope->name->val, func->common.function_name->val);
-                               FREE_OP2();
-                               HANDLE_EXCEPTION();
-                       }
                }
        } else {
                zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error);
index f4bdf0d882f777ac1f54a118ccaa9c463d0a2e55..7abf0f9854ba82521359957e8264d80c6f4ba140 100644 (file)
@@ -5649,9 +5649,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CONS
        SAVE_OPLINE();
        function_name = EX_CONSTANT(opline->op2);
        if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) {
-               if (error) {
-                       efree(error);
-               }
                func = fcc.function_handler;
                if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
                        /* Delay closure destruction until its invocation */
@@ -5667,20 +5664,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CONS
                if (object) {
                        call_info |= ZEND_CALL_RELEASE_THIS;
                        GC_REFCOUNT(object)++; /* For $this pointer */
-               } else if (func->common.scope &&
-                          !(func->common.fn_flags & ZEND_ACC_STATIC)) {
-                       if (func->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
-                               zend_error(E_DEPRECATED,
+               }
+               if (error) {
+                       efree(error);
+                       /* This is the only soft error is_callable() can generate */
+                       zend_error(E_DEPRECATED,
                                "Non-static method %s::%s() should not be called statically",
                                func->common.scope->name->val, func->common.function_name->val);
-                       } else {
-                               zend_error(
-                                       E_EXCEPTION | E_ERROR,
-                                       "Non-static method %s::%s() cannot be called statically",
-                                       func->common.scope->name->val, func->common.function_name->val);
-
-                               HANDLE_EXCEPTION();
-                       }
                }
        } else {
                zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error);
@@ -9296,9 +9286,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CV_H
        SAVE_OPLINE();
        function_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
        if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) {
-               if (error) {
-                       efree(error);
-               }
                func = fcc.function_handler;
                if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
                        /* Delay closure destruction until its invocation */
@@ -9314,20 +9301,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CV_H
                if (object) {
                        call_info |= ZEND_CALL_RELEASE_THIS;
                        GC_REFCOUNT(object)++; /* For $this pointer */
-               } else if (func->common.scope &&
-                          !(func->common.fn_flags & ZEND_ACC_STATIC)) {
-                       if (func->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
-                               zend_error(E_DEPRECATED,
+               }
+               if (error) {
+                       efree(error);
+                       /* This is the only soft error is_callable() can generate */
+                       zend_error(E_DEPRECATED,
                                "Non-static method %s::%s() should not be called statically",
                                func->common.scope->name->val, func->common.function_name->val);
-                       } else {
-                               zend_error(
-                                       E_EXCEPTION | E_ERROR,
-                                       "Non-static method %s::%s() cannot be called statically",
-                                       func->common.scope->name->val, func->common.function_name->val);
-
-                               HANDLE_EXCEPTION();
-                       }
                }
        } else {
                zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error);
@@ -11032,9 +11012,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_TMPV
        SAVE_OPLINE();
        function_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
        if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) {
-               if (error) {
-                       efree(error);
-               }
                func = fcc.function_handler;
                if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
                        /* Delay closure destruction until its invocation */
@@ -11050,20 +11027,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_TMPV
                if (object) {
                        call_info |= ZEND_CALL_RELEASE_THIS;
                        GC_REFCOUNT(object)++; /* For $this pointer */
-               } else if (func->common.scope &&
-                          !(func->common.fn_flags & ZEND_ACC_STATIC)) {
-                       if (func->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
-                               zend_error(E_DEPRECATED,
+               }
+               if (error) {
+                       efree(error);
+                       /* This is the only soft error is_callable() can generate */
+                       zend_error(E_DEPRECATED,
                                "Non-static method %s::%s() should not be called statically",
                                func->common.scope->name->val, func->common.function_name->val);
-                       } else {
-                               zend_error(
-                                       E_EXCEPTION | E_ERROR,
-                                       "Non-static method %s::%s() cannot be called statically",
-                                       func->common.scope->name->val, func->common.function_name->val);
-                               zval_ptr_dtor_nogc(free_op2);
-                               HANDLE_EXCEPTION();
-                       }
                }
        } else {
                zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error);