Skip to content

Commit 9975986

Browse files
committed
Improve error messages mentioning parameters instead of arguments
Closes GH-5999
1 parent c5b42be commit 9975986

File tree

143 files changed

+473
-452
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

143 files changed

+473
-452
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/bug46106.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ try {
2323
?>
2424
DONE
2525
--EXPECT--
26-
str_pad() expects at least 2 parameters, 1 given
26+
str_pad() expects at least 2 arguments, 1 given
2727
DONE

Zend/tests/bug51827.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ register_shutdown_function('exploDe');
1616
--EXPECT--
1717
int(1)
1818

19-
Fatal error: Uncaught ArgumentCountError: explode() expects at least 2 parameters, 0 given in [no active file]:0
19+
Fatal error: Uncaught ArgumentCountError: explode() expects at least 2 arguments, 0 given in [no active file]:0
2020
Stack trace:
2121
#0 [internal function]: explode()
2222
#1 {main}

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/bug72107.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ try {
1313
}
1414
?>
1515
--EXPECT--
16-
func_get_args() expects exactly 0 parameters, 4 given
16+
func_get_args() expects exactly 0 arguments, 4 given

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/function_arguments/argument_count_incorrect_internal.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ try {
99
}
1010
?>
1111
--EXPECT--
12-
substr() expects at least 2 parameters, 1 given
12+
substr() expects at least 2 arguments, 1 given

Zend/tests/function_arguments/argument_count_incorrect_internal_strict.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ try {
1919
?>
2020
--EXPECT--
2121
ArgumentCountError
22-
substr() expects at least 2 parameters, 1 given
22+
substr() expects at least 2 arguments, 1 given
2323
ArgumentCountError
24-
At least 2 parameters are required, 1 given
24+
At least 2 arguments are required, 1 given

Zend/tests/generators/errors/count_error.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ try {
1515

1616
?>
1717
--EXPECTF--
18-
Warning: count(): Parameter must be an array or an object that implements Countable in %s on line %d
18+
Warning: count(): Argument #1 ($var) must be of type Countable|array, Generator given 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/013.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ int(0)
5050
string(98) "call_user_func_array(): Argument #1 ($function) must be a valid callback, no array or string given"
5151
string(77) "call_user_func_array(): Argument #2 ($args) must be of type array, null given"
5252
string(69) "get_class(): Argument #1 ($object) must be of type object, null given"
53-
string(56) "get_called_class() expects exactly 0 parameters, 1 given"
53+
string(55) "get_called_class() expects exactly 0 arguments, 1 given"
5454
string(4) "NULL"
55-
string(53) "func_num_args() expects exactly 0 parameters, 1 given"
56-
string(53) "func_get_args() expects exactly 0 parameters, 1 given"
55+
string(52) "func_num_args() expects exactly 0 arguments, 1 given"
56+
string(52) "func_get_args() expects exactly 0 arguments, 1 given"
5757
string(69) "array_slice(): Argument #1 ($array) must be of type array, null given"
5858
array(1) {
5959
[0]=>

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: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -183,33 +183,29 @@ ZEND_API zend_string *zend_zval_get_legacy_type(const zval *arg) /* {{{ */
183183
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameters_none_error(void) /* {{{ */
184184
{
185185
int num_args = ZEND_CALL_NUM_ARGS(EG(current_execute_data));
186-
zend_function *active_function = EG(current_execute_data)->func;
187-
const char *class_name = active_function->common.scope ? ZSTR_VAL(active_function->common.scope->name) : "";
186+
zend_string *func_name = get_active_function_or_method_name();
188187

189-
zend_argument_count_error(
190-
"%s%s%s() expects exactly 0 parameters, %d given",
191-
class_name, \
192-
class_name[0] ? "::" : "", \
193-
ZSTR_VAL(active_function->common.function_name),
194-
num_args);
188+
zend_argument_count_error("%s() expects exactly 0 arguments, %d given", ZSTR_VAL(func_name), num_args);
189+
190+
zend_string_release(func_name);
195191
}
196192
/* }}} */
197193

198194
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameters_count_error(uint32_t min_num_args, uint32_t max_num_args) /* {{{ */
199195
{
200196
uint32_t num_args = ZEND_CALL_NUM_ARGS(EG(current_execute_data));
201-
zend_function *active_function = EG(current_execute_data)->func;
202-
const char *class_name = active_function->common.scope ? ZSTR_VAL(active_function->common.scope->name) : "";
197+
zend_string *func_name = get_active_function_or_method_name();
203198

204199
zend_argument_count_error(
205-
"%s%s%s() expects %s %d parameter%s, %d given",
206-
class_name, \
207-
class_name[0] ? "::" : "", \
208-
ZSTR_VAL(active_function->common.function_name),
209-
min_num_args == max_num_args ? "exactly" : num_args < min_num_args ? "at least" : "at most",
210-
num_args < min_num_args ? min_num_args : max_num_args,
211-
(num_args < min_num_args ? min_num_args : max_num_args) == 1 ? "" : "s",
212-
num_args);
200+
"%s() expects %s %d argument%s, %d given",
201+
ZSTR_VAL(func_name),
202+
min_num_args == max_num_args ? "exactly" : num_args < min_num_args ? "at least" : "at most",
203+
num_args < min_num_args ? min_num_args : max_num_args,
204+
(num_args < min_num_args ? min_num_args : max_num_args) == 1 ? "" : "s",
205+
num_args
206+
);
207+
208+
zend_string_release(func_name);
213209
}
214210
/* }}} */
215211

@@ -325,23 +321,23 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_unexpected_extra_named_error(void)
325321

326322
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) /* {{{ */
327323
{
328-
const char *space;
329-
const char *class_name;
324+
zend_string *func_name;
330325
const char *arg_name;
331326
char *message = NULL;
332327
if (EG(exception)) {
333328
return;
334329
}
335330

336-
class_name = get_active_class_name(&space);
331+
func_name = get_active_function_or_method_name();
337332
arg_name = get_active_function_arg_name(arg_num);
338333

339334
zend_vspprintf(&message, 0, format, va);
340-
zend_throw_error(error_ce, "%s%s%s(): Argument #%d%s%s%s %s",
341-
class_name, space, get_active_function_name(), arg_num,
335+
zend_throw_error(error_ce, "%s(): Argument #%d%s%s%s %s",
336+
ZSTR_VAL(func_name), arg_num,
342337
arg_name ? " ($" : "", arg_name ? arg_name : "", arg_name ? ")" : "", message
343338
);
344339
efree(message);
340+
zend_string_release(func_name);
345341
}
346342
/* }}} */
347343

@@ -1005,16 +1001,17 @@ static zend_result zend_parse_va_args(uint32_t num_args, const char *type_spec,
10051001

10061002
if (num_args < min_num_args || num_args > max_num_args) {
10071003
if (!(flags & ZEND_PARSE_PARAMS_QUIET)) {
1008-
zend_function *active_function = EG(current_execute_data)->func;
1009-
const char *class_name = active_function->common.scope ? ZSTR_VAL(active_function->common.scope->name) : "";
1010-
zend_argument_count_error("%s%s%s() expects %s %d parameter%s, %d given",
1011-
class_name,
1012-
class_name[0] ? "::" : "",
1013-
ZSTR_VAL(active_function->common.function_name),
1014-
min_num_args == max_num_args ? "exactly" : num_args < min_num_args ? "at least" : "at most",
1015-
num_args < min_num_args ? min_num_args : max_num_args,
1016-
(num_args < min_num_args ? min_num_args : max_num_args) == 1 ? "" : "s",
1017-
num_args);
1004+
zend_string *func_name = get_active_function_or_method_name();
1005+
1006+
zend_argument_count_error("%s() expects %s %d argument%s, %d given",
1007+
ZSTR_VAL(func_name),
1008+
min_num_args == max_num_args ? "exactly" : num_args < min_num_args ? "at least" : "at most",
1009+
num_args < min_num_args ? min_num_args : max_num_args,
1010+
(num_args < min_num_args ? min_num_args : max_num_args) == 1 ? "" : "s",
1011+
num_args
1012+
);
1013+
1014+
zend_string_release(func_name);
10181015
}
10191016
return FAILURE;
10201017
}

Zend/zend_execute.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,19 @@ static zend_never_inline ZEND_COLD bool zend_wrong_assign_to_variable_reference(
583583
return 1;
584584
}
585585

586+
ZEND_API ZEND_COLD void zend_cannot_pass_by_reference(uint32_t arg_num)
587+
{
588+
const zend_execute_data *execute_data = EG(current_execute_data);
589+
zend_string *func_name = get_function_or_method_name(EX(call)->func);
590+
const char *param_name = get_function_arg_name(EX(call)->func, arg_num);
591+
592+
zend_throw_error(NULL, "%s(): Argument #%d%s%s%s cannot be passed by reference",
593+
ZSTR_VAL(func_name), arg_num, param_name ? " ($" : "", param_name ? param_name : "", param_name ? ")" : ""
594+
);
595+
596+
zend_string_release(func_name);
597+
}
598+
586599
static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(zend_property_info *prop, const char *type) {
587600
zend_string *type_str = zend_type_to_string(prop->type);
588601
zend_type_error(

Zend/zend_execute.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,11 +311,14 @@ ZEND_API const char *get_active_class_name(const char **space);
311311
ZEND_API const char *get_active_function_name(void);
312312
ZEND_API const char *get_active_function_arg_name(uint32_t arg_num);
313313
ZEND_API const char *get_function_arg_name(const zend_function *func, uint32_t arg_num);
314+
ZEND_API zend_string *get_active_function_or_method_name();
315+
ZEND_API zend_string *get_function_or_method_name(const zend_function *func);
314316
ZEND_API const char *zend_get_executed_filename(void);
315317
ZEND_API zend_string *zend_get_executed_filename_ex(void);
316318
ZEND_API uint32_t zend_get_executed_lineno(void);
317319
ZEND_API zend_class_entry *zend_get_executed_scope(void);
318320
ZEND_API zend_bool zend_is_executing(void);
321+
ZEND_API ZEND_COLD void zend_cannot_pass_by_reference(uint32_t arg_num);
319322

320323
ZEND_API void zend_set_timeout(zend_long seconds, bool reset_signals);
321324
ZEND_API void zend_unset_timeout(void);

Zend/zend_execute_API.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,7 @@ ZEND_API const char *get_active_class_name(const char **space) /* {{{ */
443443
}
444444

445445
func = EG(current_execute_data)->func;
446+
446447
switch (func->type) {
447448
case ZEND_USER_FUNCTION:
448449
case ZEND_INTERNAL_FUNCTION:
@@ -470,7 +471,9 @@ ZEND_API const char *get_active_function_name(void) /* {{{ */
470471
if (!zend_is_executing()) {
471472
return NULL;
472473
}
474+
473475
func = EG(current_execute_data)->func;
476+
474477
switch (func->type) {
475478
case ZEND_USER_FUNCTION: {
476479
zend_string *function_name = func->common.function_name;
@@ -491,6 +494,24 @@ ZEND_API const char *get_active_function_name(void) /* {{{ */
491494
}
492495
/* }}} */
493496

497+
ZEND_API zend_string *get_active_function_or_method_name(void) /* {{{ */
498+
{
499+
ZEND_ASSERT(zend_is_executing());
500+
501+
return get_function_or_method_name(EG(current_execute_data)->func);
502+
}
503+
/* }}} */
504+
505+
ZEND_API zend_string *get_function_or_method_name(const zend_function *func) /* {{{ */
506+
{
507+
if (func->common.scope) {
508+
return zend_create_member_string(func->common.scope->name, func->common.function_name);
509+
}
510+
511+
return func->common.function_name ? zend_string_copy(func->common.function_name) : zend_string_init("main", sizeof("main") - 1, 0);
512+
}
513+
/* }}} */
514+
494515
ZEND_API const char *get_active_function_arg_name(uint32_t arg_num) /* {{{ */
495516
{
496517
zend_function *func;

Zend/zend_vm_def.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4596,7 +4596,8 @@ ZEND_VM_COLD_HELPER(zend_cannot_pass_by_ref_helper, ANY, ANY, uint32_t _arg_num,
45964596
USE_OPLINE
45974597

45984598
SAVE_OPLINE();
4599-
zend_throw_error(NULL, "Cannot pass parameter %d by reference", _arg_num);
4599+
4600+
zend_cannot_pass_by_reference(_arg_num);
46004601
FREE_OP1();
46014602
ZVAL_UNDEF(_arg);
46024603
HANDLE_EXCEPTION();
@@ -8918,7 +8919,7 @@ ZEND_VM_COLD_CONST_HANDLER(190, ZEND_COUNT, CONST|TMPVAR|CV, UNUSED)
89188919
} else {
89198920
count = 1;
89208921
}
8921-
zend_error(E_WARNING, "%s(): Parameter must be an array or an object that implements Countable", opline->extended_value ? "sizeof" : "count");
8922+
zend_error(E_WARNING, "%s(): Argument #1 ($var) must be of type Countable|array, %s given", opline->extended_value ? "sizeof" : "count", zend_zval_type_name(op1));
89228923
break;
89238924
}
89248925

0 commit comments

Comments
 (0)