]> granicus.if.org Git - php/commitdiff
call_user_func(_array): Don't abort on reference warning
authorNikita Popov <nikic@php.net>
Sat, 6 Aug 2016 13:24:23 +0000 (15:24 +0200)
committerDmitry Stogov <dmitry@zend.com>
Tue, 23 Aug 2016 07:29:15 +0000 (10:29 +0300)
Change zend_call_function() to not abort the call if a non-reference
is passed to a reference argument. The usual warning will still be
thrown, but the call will proceed as usual.

12 files changed:
Zend/tests/call_user_func_008.phpt [new file with mode: 0644]
Zend/zend_execute_API.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h
ext/phar/tests/cache_list/files/frontcontroller17.phar
ext/phar/tests/cache_list/files/frontcontroller17.phar.inc
ext/phar/tests/cache_list/frontcontroller32.phpt
ext/phar/tests/files/frontcontroller17.phar
ext/phar/tests/files/frontcontroller17.phar.inc
ext/phar/tests/frontcontroller32.phpt
ext/reflection/tests/bug42976.phpt
ext/standard/tests/general_functions/bug41970.phpt

diff --git a/Zend/tests/call_user_func_008.phpt b/Zend/tests/call_user_func_008.phpt
new file mode 100644 (file)
index 0000000..3e727e7
--- /dev/null
@@ -0,0 +1,54 @@
+--TEST--
+call_user_func() behavior with references
+--FILE--
+<?php
+
+function test(&$ref1, &$ref2) {
+    $ref1 += 42;
+    $ref2 -= 42;
+    return true;
+}
+
+$i = $j = 0;
+var_dump(call_user_func('test', $i, $j));
+var_dump($i, $j);
+
+var_dump(call_user_func_array('test', [$i, $j]));
+var_dump($i, $j);
+
+$x =& $i; $y =& $j;
+var_dump(call_user_func('test', $i, $j));
+var_dump($i, $j);
+
+var_dump(call_user_func_array('test', [$i, $j]));
+var_dump($i, $j);
+
+?>
+--EXPECTF--
+Warning: Parameter 1 to test() expected to be a reference, value given in %s on line %d
+
+Warning: Parameter 2 to test() expected to be a reference, value given in %s on line %d
+bool(true)
+int(0)
+int(0)
+
+Warning: Parameter 1 to test() expected to be a reference, value given in %s on line %d
+
+Warning: Parameter 2 to test() expected to be a reference, value given in %s on line %d
+bool(true)
+int(0)
+int(0)
+
+Warning: Parameter 1 to test() expected to be a reference, value given in %s on line %d
+
+Warning: Parameter 2 to test() expected to be a reference, value given in %s on line %d
+bool(true)
+int(0)
+int(0)
+
+Warning: Parameter 1 to test() expected to be a reference, value given in %s on line %d
+
+Warning: Parameter 2 to test() expected to be a reference, value given in %s on line %d
+bool(true)
+int(0)
+int(0)
index 3432064eafaeedfa7a62c6e9d7fafc2d7c9798b1..dc411af83557e084c2680777ac71a4c17b082814 100644 (file)
@@ -788,41 +788,29 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
 
                if (ARG_SHOULD_BE_SENT_BY_REF(func, i + 1)) {
                        if (UNEXPECTED(!Z_ISREF_P(arg))) {
-                               if (fci->no_separation &&
-                                       !ARG_MAY_BE_SENT_BY_REF(func, i + 1)) {
-                                       if (i) {
-                                               /* hack to clean up the stack */
-                                               ZEND_CALL_NUM_ARGS(call) = i;
-                                               zend_vm_stack_free_args(call);
-                                       }
-                                       zend_vm_stack_free_call_frame(call);
-
-                                       zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
-                                               i+1,
+                               if (!fci->no_separation) {
+                                       /* Separation is enabled -- create a ref */
+                                       ZVAL_NEW_REF(arg, arg);
+                               } else if (!ARG_MAY_BE_SENT_BY_REF(func, i + 1)) {
+                                       /* By-value send is not allowed -- emit a warning,
+                                        * but still perform the call with a by-value send. */
+                                       zend_error(E_WARNING,
+                                               "Parameter %d to %s%s%s() expected to be a reference, value given", i+1,
                                                func->common.scope ? ZSTR_VAL(func->common.scope->name) : "",
                                                func->common.scope ? "::" : "",
                                                ZSTR_VAL(func->common.function_name));
-                                       if (EG(current_execute_data) == &dummy_execute_data) {
-                                               EG(current_execute_data) = dummy_execute_data.prev_execute_data;
-                                       }
-                                       return FAILURE;
                                }
-
-                               ZVAL_NEW_REF(arg, arg);
                        }
-                       Z_ADDREF_P(arg);
                } else {
                        if (Z_ISREF_P(arg) &&
                            !(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
                                /* don't separate references for __call */
                                arg = Z_REFVAL_P(arg);
                        }
-                       if (Z_OPT_REFCOUNTED_P(arg)) {
-                               Z_ADDREF_P(arg);
-                       }
                }
+
                param = ZEND_CALL_ARG(call, i+1);
-               ZVAL_COPY_VALUE(param, arg);
+               ZVAL_COPY(param, arg);
        }
 
        if (UNEXPECTED(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
index 1a52041ab3fe5dd00266c8f786cedaff59139614..c64eebb7816ab249f7cb65fa37d95916c888d6a7 100644 (file)
@@ -4626,23 +4626,15 @@ ZEND_VM_C_LABEL(send_array):
                        if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
                                if (UNEXPECTED(!Z_ISREF_P(arg))) {
                                        if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
-
-                                               zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
+                                               /* By-value send is not allowed -- emit a warning,
+                                                * but still perform the call. */
+                                               zend_error(E_WARNING,
+                                                       "Parameter %d to %s%s%s() expected to be a reference, value given",
                                                        arg_num,
                                                        EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
                                                        EX(call)->func->common.scope ? "::" : "",
                                                        ZSTR_VAL(EX(call)->func->common.function_name));
 
-                                               if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
-                                                       OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
-                                               }
-                                               if (Z_TYPE(EX(call)->This) == IS_OBJECT) {
-                                                       OBJ_RELEASE(Z_OBJ(EX(call)->This));
-                                               }
-                                               EX(call)->func = (zend_function*)&zend_pass_function;
-                                               Z_OBJ(EX(call)->This) = NULL;
-                                               ZEND_SET_CALL_INFO(EX(call), 0, ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
-                                               break;
                                        }
                                }
                        } else {
@@ -4673,25 +4665,12 @@ ZEND_VM_HANDLER(120, ZEND_SEND_USER, VAR|CV, NUM)
        param = ZEND_CALL_VAR(EX(call), opline->result.var);
 
        if (UNEXPECTED(ARG_MUST_BE_SENT_BY_REF(EX(call)->func, opline->op2.num))) {
+               ZVAL_DEREF(arg);
                zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
                        opline->op2.num,
                        EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
                        EX(call)->func->common.scope ? "::" : "",
                        ZSTR_VAL(EX(call)->func->common.function_name));
-
-               if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
-                       OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
-               }
-               if (Z_TYPE(EX(call)->This) == IS_OBJECT) {
-                       OBJ_RELEASE(Z_OBJ(EX(call)->This));
-               }
-               ZVAL_UNDEF(param);
-               EX(call)->func = (zend_function*)&zend_pass_function;
-               Z_OBJ(EX(call)->This) = NULL;
-               ZEND_SET_CALL_INFO(EX(call), 0, ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
-
-               FREE_OP1();
-               ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
        } else {
                if (Z_ISREF_P(arg) &&
                    !(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
index 3d0bcf8a191a0681bd8519863ed49e7c6799dce0..46c87e72aa60f7f5643e616b79c199bbd5c0927a 100644 (file)
@@ -1423,23 +1423,15 @@ send_array:
                        if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
                                if (UNEXPECTED(!Z_ISREF_P(arg))) {
                                        if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
-
-                                               zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
+                                               /* By-value send is not allowed -- emit a warning,
+                                                * but still perform the call. */
+                                               zend_error(E_WARNING,
+                                                       "Parameter %d to %s%s%s() expected to be a reference, value given",
                                                        arg_num,
                                                        EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
                                                        EX(call)->func->common.scope ? "::" : "",
                                                        ZSTR_VAL(EX(call)->func->common.function_name));
 
-                                               if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
-                                                       OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
-                                               }
-                                               if (Z_TYPE(EX(call)->This) == IS_OBJECT) {
-                                                       OBJ_RELEASE(Z_OBJ(EX(call)->This));
-                                               }
-                                               EX(call)->func = (zend_function*)&zend_pass_function;
-                                               Z_OBJ(EX(call)->This) = NULL;
-                                               ZEND_SET_CALL_INFO(EX(call), 0, ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
-                                               break;
                                        }
                                }
                        } else {
@@ -15904,25 +15896,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_USER_SPEC_VAR_HANDLER(ZEN
        param = ZEND_CALL_VAR(EX(call), opline->result.var);
 
        if (UNEXPECTED(ARG_MUST_BE_SENT_BY_REF(EX(call)->func, opline->op2.num))) {
+               ZVAL_DEREF(arg);
                zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
                        opline->op2.num,
                        EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
                        EX(call)->func->common.scope ? "::" : "",
                        ZSTR_VAL(EX(call)->func->common.function_name));
-
-               if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
-                       OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
-               }
-               if (Z_TYPE(EX(call)->This) == IS_OBJECT) {
-                       OBJ_RELEASE(Z_OBJ(EX(call)->This));
-               }
-               ZVAL_UNDEF(param);
-               EX(call)->func = (zend_function*)&zend_pass_function;
-               Z_OBJ(EX(call)->This) = NULL;
-               ZEND_SET_CALL_INFO(EX(call), 0, ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
-
-               zval_ptr_dtor_nogc(free_op1);
-               ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
        } else {
                if (Z_ISREF_P(arg) &&
                    !(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
@@ -34925,24 +34904,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_USER_SPEC_CV_HANDLER(ZEND
        param = ZEND_CALL_VAR(EX(call), opline->result.var);
 
        if (UNEXPECTED(ARG_MUST_BE_SENT_BY_REF(EX(call)->func, opline->op2.num))) {
+               ZVAL_DEREF(arg);
                zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
                        opline->op2.num,
                        EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
                        EX(call)->func->common.scope ? "::" : "",
                        ZSTR_VAL(EX(call)->func->common.function_name));
-
-               if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_CLOSURE) {
-                       OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype);
-               }
-               if (Z_TYPE(EX(call)->This) == IS_OBJECT) {
-                       OBJ_RELEASE(Z_OBJ(EX(call)->This));
-               }
-               ZVAL_UNDEF(param);
-               EX(call)->func = (zend_function*)&zend_pass_function;
-               Z_OBJ(EX(call)->This) = NULL;
-               ZEND_SET_CALL_INFO(EX(call), 0, ZEND_CALL_INFO(EX(call)) & ~ZEND_CALL_RELEASE_THIS);
-
-               ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
        } else {
                if (Z_ISREF_P(arg) &&
                    !(EX(call)->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
index b83d41fd5b8dc4be713bd8f0961d1b1835535ab0..d9b8330e63fb1d3b61192e2c993a4bb6a35efde1 100644 (file)
Binary files a/ext/phar/tests/cache_list/files/frontcontroller17.phar and b/ext/phar/tests/cache_list/files/frontcontroller17.phar differ
index 85b8729f31cfbb594de29c20fd12f5d232655bc6..715479552a2ba23888503d1ae865c7ca4c09d145 100644 (file)
@@ -6,7 +6,7 @@ echo "hi";
 ';
 $a->setStub('<?php
 try {
-Phar::webPhar("test.phar", "/index.php", null, array(), "sort");
+Phar::webPhar("test.phar", "/index.php", null, array(), function() { throw new Exception; });
 } catch (Exception $e) {
 die($e->getMessage() . "\n");
 }
index 59116907a5931709d98be57ce96e12280e2358b9..cb9abb8c198c127b7a42aebadce6cf646509bdf0 100644 (file)
@@ -13,4 +13,4 @@ Content-type: text/html; charset=UTF-8
 --FILE_EXTERNAL--
 files/frontcontroller17.phar
 --EXPECTF--
-%ahar error: failed to call rewrite callback
\ No newline at end of file
+%ahar error: rewrite callback must return a string or false
index b83d41fd5b8dc4be713bd8f0961d1b1835535ab0..4dab78a9ec012dfd493af9f73fd3e0e54ba3f65a 100644 (file)
Binary files a/ext/phar/tests/files/frontcontroller17.phar and b/ext/phar/tests/files/frontcontroller17.phar differ
index 85b8729f31cfbb594de29c20fd12f5d232655bc6..715479552a2ba23888503d1ae865c7ca4c09d145 100644 (file)
@@ -6,7 +6,7 @@ echo "hi";
 ';
 $a->setStub('<?php
 try {
-Phar::webPhar("test.phar", "/index.php", null, array(), "sort");
+Phar::webPhar("test.phar", "/index.php", null, array(), function() { throw new Exception; });
 } catch (Exception $e) {
 die($e->getMessage() . "\n");
 }
index 58f6fffa00ad8764790384e8b21971de7d66a3af..032d0f571d28c7800013721f5645b951b7515963 100644 (file)
@@ -12,4 +12,4 @@ Content-type: text/html; charset=UTF-8
 --FILE_EXTERNAL--
 files/frontcontroller17.phar
 --EXPECTF--
-%ahar error: failed to call rewrite callback
\ No newline at end of file
+%ahar error: rewrite callback must return a string or false
index 2e4ade28476ef11e2a6add174b93c4494ba53d1b..21aff8d4cca105a0ccbf4d6829a9fc3a431de489 100644 (file)
@@ -27,12 +27,8 @@ echo "Done\n";
 string(9) "x.changed"
 
 Warning: Parameter 1 to C::__construct() expected to be a reference, value given in %sbug42976.php on line 15
-
-Warning: ReflectionClass::newInstance(): Invocation of C's constructor failed in %sbug42976.php on line 15
 string(10) "x.original"
 
 Warning: Parameter 1 to C::__construct() expected to be a reference, value given in %sbug42976.php on line 18
-
-Warning: ReflectionClass::newInstanceArgs(): Invocation of C's constructor failed in %sbug42976.php on line 18
 string(10) "x.original"
 Done
index 4bce3ac7f731ffb6a907bf7ca38f580ebf297535..6f05137afc1b41e681f6e9faeddc86a56748979b 100644 (file)
@@ -14,13 +14,13 @@ echo "Done\n";
 ?>
 --EXPECTF--
 Warning: Parameter 1 to sort() expected to be a reference, value given in %sbug41970.php on line 5
-NULL
+bool(true)
 
 Warning: strlen() expects parameter 1 to be string, array given in %sbug41970.php on line 6
 NULL
 
 Warning: Parameter 1 to sort() expected to be a reference, value given in %sbug41970.php on line 7
-NULL
+bool(true)
 
 Warning: strlen() expects parameter 1 to be string, array given in %sbug41970.php on line 8
 NULL