Skip to content

Commit 9aecf58

Browse files
committed
Improve error message for 'Cannot pass parameter by reference'
1 parent d57f9e5 commit 9aecf58

28 files changed

+118
-66
lines changed

UPGRADING

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,10 @@ PHP 8.0 UPGRADE NOTES
135135
. Uncaught exceptions now go through "clean shutdown", which means that
136136
destructors will be called after an uncaught exception.
137137
. Compile time fatal error "Only variables can be passed by reference" has
138-
been delayed until runtime and converted to "Cannot pass parameter by
138+
been delayed until runtime and converted to "Argument cannot be passed by
139139
reference" exception.
140140
. Some "Only variables should be passed by reference" notices have been
141-
converted to "Cannot pass parameter by reference" exception.
141+
converted to "Argument cannot be passed by reference" exception.
142142
. The generated name for anonymous classes has changed. It will now include
143143
the name of the first parent or interface:
144144

Zend/tests/bug72038.phpt

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,25 @@ Bug #72038 (Function calls with values to a by-ref parameter don't always throw
66
try {
77
test($foo = new stdClass);
88
var_dump($foo);
9-
} catch (Throwable $e) {
10-
echo "Exception: " . $e->getMessage() . "\n";
9+
} catch (Error $e) {
10+
echo $e->getMessage() . "\n";
1111
}
1212
try {
1313
test($bar = 2);
1414
var_dump($bar);
15-
} catch (Throwable $e) {
16-
echo "Exception: " . $e->getMessage() . "\n";
17-
}
18-
try {
19-
test($baz = &$bar);
20-
var_dump($baz);
21-
} catch (Throwable $e) {
22-
echo "Exception: " . $e->getMessage() . "\n";
15+
} catch (Error $e) {
16+
echo $e->getMessage() . "\n";
2317
}
2418

19+
test($baz = &$bar);
20+
var_dump($baz);
21+
2522
function test(&$param) {
2623
$param = 1;
2724
}
2825

2926
?>
3027
--EXPECT--
31-
Exception: Cannot pass parameter 1 by reference
32-
Exception: Cannot pass parameter 1 by reference
28+
test(): Argument #1 ($param) cannot be passed by reference
29+
test(): Argument #1 ($param) cannot be passed by reference
3330
int(1)

Zend/tests/bug73663_2.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ change(list($val) = $array);
1212
var_dump($array);
1313
?>
1414
--EXPECTF--
15-
Fatal error: Uncaught Error: Cannot pass parameter 1 by reference in %s:%d
15+
Fatal error: Uncaught Error: change(): Argument #1 ($ref) cannot be passed by reference in %s:%d
1616
Stack trace:
1717
#0 {main}
1818
thrown in %s on line %d

Zend/tests/bug78154.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,5 @@ namespace Foo {
2222

2323
?>
2424
--EXPECT--
25-
Exception: Cannot pass parameter 3 by reference
26-
Exception: Cannot pass parameter 3 by reference
25+
Exception: similar_text(): Argument #3 ($percent) cannot be passed by reference
26+
Exception: similar_text(): Argument #3 ($percent) cannot be passed by reference

Zend/tests/bug79783.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Bug #79783: Segfault in php_str_replace_common
55
str_replace("a", "b", "c", strlen("d"));
66
?>
77
--EXPECTF--
8-
Fatal error: Uncaught Error: Cannot pass parameter 4 by reference in %s:%d
8+
Fatal error: Uncaught Error: str_replace(): Argument #4 ($replace_count) cannot be passed by reference in %s:%d
99
Stack trace:
1010
#0 {main}
1111
thrown in %s on line %d

Zend/tests/closure_019.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ int(9)
2626
Notice: Only variable references should be returned by reference in %sclosure_019.php on line 4
2727
int(81)
2828

29-
Fatal error: Uncaught Error: Cannot pass parameter 1 by reference in %s:%d
29+
Fatal error: Uncaught Error: {closure}(): Argument #1 ($x) cannot be passed by reference in %s:%d
3030
Stack trace:
3131
#0 %s(%d): test()
3232
#1 {main}

Zend/tests/errmsg_022.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ foo(1);
1111
echo "Done\n";
1212
?>
1313
--EXPECTF--
14-
Fatal error: Uncaught Error: Cannot pass parameter 1 by reference in %s:%d
14+
Fatal error: Uncaught Error: foo(): Argument #1 ($var) cannot be passed by reference in %s:%d
1515
Stack trace:
1616
#0 {main}
1717
thrown in %s on line %d

Zend/tests/match/027.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ main();
3030
usesValue 0
3131
i is 0
3232

33-
Fatal error: Uncaught Error: Cannot pass parameter 1 by reference in %s027.php:20
33+
Fatal error: Uncaught Error: Test::usesRef(): Argument #1 ($x) cannot be passed by reference in %s:%d
3434
Stack trace:
35-
#0 %s027.php(23): main()
35+
#0 %s(%d): main()
3636
#1 {main}
37-
thrown in %s027.php on line 20
37+
thrown in %s on line %d

Zend/tests/match/028.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,4 @@ try {
3434
usesValue 42
3535
usesValue 42
3636
int(42)
37-
Caught Cannot pass parameter 1 by reference
37+
Caught Test::usesRef(): Argument #1 ($x) cannot be passed by reference

Zend/tests/named_params/cannot_pass_by_ref.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ try {
1010
}
1111
?>
1212
--EXPECT--
13-
Cannot pass parameter 2 by reference
13+
test(): Argument #2 ($e) cannot be passed by reference

Zend/tests/nullsafe_operator/016.phpt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ test(new Foo());
2929

3030
?>
3131
--EXPECT--
32-
Cannot pass parameter 1 by reference
33-
Cannot pass parameter 1 by reference
34-
Cannot pass parameter 1 by reference
35-
Cannot pass parameter 1 by reference
32+
set(): Argument #1 ($ref) cannot be passed by reference
33+
set(): Argument #1 ($ref) cannot be passed by reference
34+
set(): Argument #1 ($ref) cannot be passed by reference
35+
set(): Argument #1 ($ref) cannot be passed by reference

Zend/tests/variadic/by_ref_error.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ test(1);
99

1010
?>
1111
--EXPECTF--
12-
Fatal error: Uncaught Error: Cannot pass parameter 1 by reference in %s:%d
12+
Fatal error: Uncaught Error: test(): Argument #1 cannot be passed by reference in %s:%d
1313
Stack trace:
1414
#0 {main}
1515
thrown in %s on line %d

Zend/zend_API.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -319,23 +319,23 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_unexpected_extra_named_error(void)
319319

320320
static ZEND_COLD void ZEND_FASTCALL zend_argument_error_variadic(zend_class_entry *error_ce, uint32_t arg_num, const char *format, va_list va) /* {{{ */
321321
{
322-
const char *space;
323-
const char *class_name;
322+
char *func_name;
324323
const char *arg_name;
325324
char *message = NULL;
326325
if (EG(exception)) {
327326
return;
328327
}
329328

330-
class_name = get_active_class_name(&space);
329+
func_name = get_active_function_or_method_name();
331330
arg_name = get_active_function_arg_name(arg_num);
332331

333332
zend_vspprintf(&message, 0, format, va);
334-
zend_throw_error(error_ce, "%s%s%s(): Argument #%d%s%s%s %s",
335-
class_name, space, get_active_function_name(), arg_num,
333+
zend_throw_error(error_ce, "%s(): Argument #%d%s%s%s %s",
334+
func_name, arg_num,
336335
arg_name ? " ($" : "", arg_name ? arg_name : "", arg_name ? ")" : "", message
337336
);
338337
efree(message);
338+
efree(func_name);
339339
}
340340
/* }}} */
341341

Zend/zend_execute.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,9 +308,13 @@ ZEND_API void ZEND_FASTCALL zend_free_extra_named_params(zend_array *extra_named
308308

309309
/* services */
310310
ZEND_API const char *get_active_class_name(const char **space);
311+
ZEND_API const char *get_class_name(const zend_function *func, const char **space);
311312
ZEND_API const char *get_active_function_name(void);
313+
ZEND_API const char *get_function_name(const zend_function *func);
312314
ZEND_API const char *get_active_function_arg_name(uint32_t arg_num);
313315
ZEND_API const char *get_function_arg_name(const zend_function *func, uint32_t arg_num);
316+
ZEND_API char *get_active_function_or_method_name();
317+
ZEND_API char *get_function_or_method_name(const zend_function *func);
314318
ZEND_API const char *zend_get_executed_filename(void);
315319
ZEND_API zend_string *zend_get_executed_filename_ex(void);
316320
ZEND_API uint32_t zend_get_executed_lineno(void);

Zend/zend_execute_API.c

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -433,16 +433,20 @@ void shutdown_executor(void) /* {{{ */
433433
/* return class name and "::" or "". */
434434
ZEND_API const char *get_active_class_name(const char **space) /* {{{ */
435435
{
436-
zend_function *func;
437-
438436
if (!zend_is_executing()) {
439437
if (space) {
440438
*space = "";
441439
}
442440
return "";
443441
}
444442

445-
func = EG(current_execute_data)->func;
443+
return get_class_name(EG(current_execute_data)->func, space);
444+
}
445+
/* }}} */
446+
447+
/* return class name and "::" or "". */
448+
ZEND_API const char *get_class_name(const zend_function *func, const char **space) /* {{{ */
449+
{
446450
switch (func->type) {
447451
case ZEND_USER_FUNCTION:
448452
case ZEND_INTERNAL_FUNCTION:
@@ -465,12 +469,16 @@ ZEND_API const char *get_active_class_name(const char **space) /* {{{ */
465469

466470
ZEND_API const char *get_active_function_name(void) /* {{{ */
467471
{
468-
zend_function *func;
469-
470472
if (!zend_is_executing()) {
471473
return NULL;
472474
}
473-
func = EG(current_execute_data)->func;
475+
476+
return get_function_name(EG(current_execute_data)->func);
477+
}
478+
/* }}} */
479+
480+
ZEND_API const char *get_function_name(const zend_function *func) /* {{{ */
481+
{
474482
switch (func->type) {
475483
case ZEND_USER_FUNCTION: {
476484
zend_string *function_name = func->common.function_name;
@@ -491,6 +499,29 @@ ZEND_API const char *get_active_function_name(void) /* {{{ */
491499
}
492500
/* }}} */
493501

502+
ZEND_API char *get_active_function_or_method_name(void) /* {{{ */
503+
{
504+
if (!zend_is_executing()) {
505+
return "";
506+
}
507+
508+
return get_function_or_method_name(EG(current_execute_data)->func);
509+
}
510+
/* }}} */
511+
512+
ZEND_API char *get_function_or_method_name(const zend_function *func) /* {{{ */
513+
{
514+
char *name = NULL;
515+
const char *class_name, *space;
516+
517+
class_name = get_class_name(func, &space);
518+
519+
zend_spprintf(&name, 0, "%s%s%s", class_name, space, get_function_name(func));
520+
521+
return name;
522+
}
523+
/* }}} */
524+
494525
ZEND_API const char *get_active_function_arg_name(uint32_t arg_num) /* {{{ */
495526
{
496527
zend_function *func;

Zend/zend_vm_def.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4596,9 +4596,16 @@ ZEND_VM_HOT_HANDLER(65, ZEND_SEND_VAL, CONST|TMPVAR, CONST|UNUSED|NUM)
45964596
ZEND_VM_COLD_HELPER(zend_cannot_pass_by_ref_helper, ANY, ANY, uint32_t _arg_num, zval *_arg)
45974597
{
45984598
USE_OPLINE
4599+
char *func_name = get_function_or_method_name(EX(call)->func);
4600+
const char *param_name = get_function_arg_name(EX(call)->func, _arg_num);
45994601

46004602
SAVE_OPLINE();
4601-
zend_throw_error(NULL, "Cannot pass parameter %d by reference", _arg_num);
4603+
4604+
zend_throw_error(NULL, "%s(): Argument #%d%s%s%s cannot be passed by reference",
4605+
func_name, _arg_num, param_name ? " ($" : "",
4606+
param_name ? param_name : "", param_name ? ")" : ""
4607+
);
4608+
efree(func_name);
46024609
FREE_OP1();
46034610
ZVAL_UNDEF(_arg);
46044611
HANDLE_EXCEPTION();

Zend/zend_vm_execute.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2281,9 +2281,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GENERATOR_CREATE_SPEC_HANDLER(
22812281
static zend_never_inline ZEND_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_cannot_pass_by_ref_helper_SPEC(uint32_t _arg_num, zval *_arg ZEND_OPCODE_HANDLER_ARGS_DC)
22822282
{
22832283
USE_OPLINE
2284+
zval *arg;
2285+
char *func_name = get_function_or_method_name(EX(call)->func);
2286+
const char *param_name = get_function_arg_name(EX(call)->func, _arg_num);
22842287

22852288
SAVE_OPLINE();
2286-
zend_throw_error(NULL, "Cannot pass parameter %d by reference", _arg_num);
2289+
2290+
zend_throw_error(NULL, "%s(): Argument #%d%s%s%s cannot be passed by reference",
2291+
func_name, _arg_num, param_name ? " ($" : "",
2292+
param_name ? param_name : "", param_name ? ")" : ""
2293+
);
2294+
efree(func_name);
22872295
FREE_OP(opline->op1_type, opline->op1.var);
22882296
ZVAL_UNDEF(_arg);
22892297
HANDLE_EXCEPTION();

ext/opcache/jit/zend_jit_disasm_x86.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ static int zend_jit_disasm_init(void)
450450
REGISTER_HELPER(zend_jit_post_dec_typed_ref);
451451
REGISTER_HELPER(zend_jit_assign_op_to_typed_ref);
452452
REGISTER_HELPER(zend_jit_only_vars_by_reference);
453+
REGISTER_HELPER(zend_jit_cannot_pass_by_reference);
453454
REGISTER_HELPER(zend_jit_invalid_array_access);
454455
REGISTER_HELPER(zend_jit_invalid_property_read);
455456
REGISTER_HELPER(zend_jit_invalid_property_write);

ext/opcache/jit/zend_jit_helpers.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1769,6 +1769,18 @@ static void ZEND_FASTCALL zend_jit_only_vars_by_reference(zval *arg)
17691769
zend_error(E_NOTICE, "Only variables should be passed by reference");
17701770
}
17711771

1772+
static void ZEND_FASTCALL zend_jit_cannot_pass_by_reference(uint32_t arg_num)
1773+
{
1774+
const zend_execute_data *execute_data = EG(current_execute_data);
1775+
char *func_name = get_function_or_method_name(EX(call)->func);
1776+
const char *param_name = get_function_arg_name(EX(call)->func, arg_num);
1777+
1778+
zend_throw_error(NULL, "%s(): Argument #%d%s%s%s cannot be passed by reference",
1779+
func_name, arg_num, param_name ? " ($" : "", param_name ? param_name : "", param_name ? ")" : ""
1780+
);
1781+
efree(func_name);
1782+
}
1783+
17721784
static void ZEND_FASTCALL zend_jit_invalid_array_access(zval *container)
17731785
{
17741786
zend_error(E_WARNING, "Trying to access array offset on value of type %s", zend_zval_type_name(container));

ext/opcache/jit/zend_jit_x86.dasc

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1913,19 +1913,11 @@ static int zend_jit_throw_cannot_pass_by_ref_stub(dasm_State **Dst)
19131913
|1:
19141914
| mov RX, r0
19151915
|.if X64
1916-
| xor CARG1, CARG1
1917-
| LOAD_ADDR CARG2, "Cannot pass parameter %d by reference"
1918-
| mov CARG3d, dword OP:r0->op2.num
1919-
| EXT_CALL zend_throw_error, r0
1916+
| mov CARG1d, dword OP:r0->op2.num
19201917
|.else
1921-
| mov r1, dword OP:r0->op2.num
1922-
| sub r4, 4
1923-
| push r1
1924-
| push "Cannot pass parameter %d by reference"
1925-
| push 0
1926-
| EXT_CALL zend_throw_error, r0
1927-
| add r4, 16
1918+
| mov r1, dword OP:r0->op2.num
19281919
|.endif
1920+
| EXT_CALL zend_jit_cannot_pass_by_reference, r0
19291921
| cmp byte OP:RX->op1_type, IS_TMP_VAR
19301922
| jne >9
19311923
|.if X64
@@ -8990,7 +8982,7 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
89908982
}
89918983

89928984
if (call_num_args <= func->op_array.num_args) {
8993-
if (!trace || (trace->op == ZEND_JIT_TRACE_END
8985+
if (!trace || (trace->op == ZEND_JIT_TRACE_END
89948986
&& trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER)) {
89958987
uint32_t num_args;
89968988

@@ -9039,7 +9031,7 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
90399031
}
90409032
}
90419033
} else {
9042-
if (!trace || (trace->op == ZEND_JIT_TRACE_END
9034+
if (!trace || (trace->op == ZEND_JIT_TRACE_END
90439035
&& trace->stop == ZEND_JIT_TRACE_STOP_INTERPRETER)) {
90449036
if (func && zend_accel_in_shm(func->op_array.opcodes)) {
90459037
| LOAD_IP_ADDR (func->op_array.opcodes)

ext/opcache/tests/optimize_func_calls.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ Array
128128
string(7) "changed"
129129
string(7) "changed"
130130

131-
Fatal error: Uncaught Error: Cannot pass parameter 1 by reference in %soptimize_func_calls.php:%d
131+
Fatal error: Uncaught Error: ref(): Argument #1 ($b) cannot be passed by reference in %soptimize_func_calls.php:%d
132132
Stack trace:
133133
#0 {main}
134134
thrown in %soptimize_func_calls.php on line %d

ext/pcre/tests/preg_match_all_error3.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ echo "Done";
1717
--EXPECTF--
1818
*** Testing preg_match_all() : error conditions ***
1919

20-
Fatal error: Uncaught Error: Cannot pass parameter 3 by reference in %s:%d
20+
Fatal error: Uncaught Error: preg_match_all(): Argument #3 ($subpatterns) cannot be passed by reference in %s:%d
2121
Stack trace:
2222
#0 {main}
2323
thrown in %s on line %d

ext/pdo_mysql/tests/bug_37445.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ $stmt = $db->prepare("SELECT 1");
1717
$stmt->bindParam(':a', 'b');
1818
?>
1919
--EXPECTF--
20-
Fatal error: Uncaught Error: Cannot pass parameter 2 by reference in %sbug_37445.php:%d
20+
Fatal error: Uncaught Error: PDOStatement::bindParam(): Argument #2 ($param) cannot be passed by reference in %sbug_37445.php:%d
2121
Stack trace:
2222
#0 {main}
2323
thrown in %sbug_37445.php on line %d

ext/standard/tests/array/array_next_error2.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ function f() {
88
var_dump(next(array(1, 2)));
99
?>
1010
--EXPECTF--
11-
Fatal error: Uncaught Error: Cannot pass parameter 1 by reference in %s:%d
11+
Fatal error: Uncaught Error: next(): Argument #1 ($arg) cannot be passed by reference in %s:%d
1212
Stack trace:
1313
#0 {main}
1414
thrown in %s on line %d

0 commit comments

Comments
 (0)