Going through a code review and found named argument call being used to a argument that uses the spread operator for variable args (vargs). To my suprise it works fine for 1 argument, but I cannot find any information on how I would use named arguments with additional arguments being passed:
<?php
function foo(...$vargs) {
echo implode(',', $vargs);
}
// normal usage of vargs
foo(1, 2, 3); // echo 1,2,3
// single arg fine got variable number args
foo(vargs: 4); // echo 4
// runs but puts the whole array into the 1st varg
foo(vargs: [4, 5]);
// no good all have syntax errors
foo(vargs: ...[4, 5]);
foo(vargs: 4, vargs: 5);
foo(vargs: 4, 5);
is there any way of achieving the above or should named arguments be avoided for varg?
Following on from insights in comments and an answer I've just added on a related question, it's possible to do this for an associative array.
I found the key clue in the named parameters RFC, which says:
Functions declared as variadic using the ...$args syntax will also collect unknown named arguments into $args.
and:
The foo(...$args) unpacking syntax from the argument unpacking RFC also supports unpacking named arguments
Taken together, these mean that this is valid code (live demo):
function foo($bar, ...$vargs) {
var_dump($bar, $vargs);
}
foo(42, ...['a' => 1, 'b' => 2, 'c' => 3]);
What happens here is that the unpacking turns the call into this:
foo(42, a: 1, b: 2, c: 3);
...and then the "unknown named arguments" rule re-packs them into the $vargs
parameter, with string keys, resulting in this output:
int(42)
array(3) {
["a"]=>
int(1)
["b"]=>
int(2)
["c"]=>
int(3)
}
It turns out this is why foo(vargs: 4);
works as well: vargs
is not allowed as a named argument, but since any named parameter gets collected into the variadic argument, $vargs
ends up as an associative array [ 'vargs' => 4 ]
The key lesson is that you can use spread syntax to populate the variadic argument as long as:
If you have an existing list you want to spread, you could prepare it by prefixing each key to be a unique string. Casting to string is not enough, because arrays normalise numeric string keys back to integers (except in occasional edge cases that are considered bugs in PHP, and shouldn't be relied on).
On the receiving end, you could use array_values
to discard any resulting string keys in the variadic parameter, if what you actually wanted was a straight list.