2

I am using the ...$args to populate my function with arguments, but i came across a weird behavior.

I have two test functions:

function withToken(...$arguments)
{
    $params = [];

    $params['first'] = 'first';

    if (count($arguments) > 0) {

        foreach ($arguments as $key => $value) {

            $params[$key] = $value;

        }
    }

    return $params;
}

and

function normal($arguments)
{
    $params = [];

    $params['first'] = 'first';

    if (count($arguments) > 0) {

        foreach ($arguments as $key => $value) {

            $params[$key] = $value;

        }
    }

    return $params;
}

In my head they do the same thing, so why calling

   withToken([
       'second' => 'second',
       'third' => 'third'
   ]);

returns

Array
(
    [first] => first
    [0] => Array
        (
            [second] => second
            [third] => third
        )

)

and calling

    normal([
       'second' => 'second',
       'third' => 'third'
    ]);

returns

Array
(
    [first] => first
    [second] => second
    [third] => third
 )

On PHP documentation i didn't found anything about it, someone could explain me why such behavior is happening?

3 Answers 3

1

You are specifying that the function accept a variable number of arguments:

function withToken(...$arguments)

However, you pass ONE argument (an array). This doesn't expand into multiple arguments. You can attempt unpacking:

withToken(...[
       'second' => 'second',
       'third' => 'third'
   ]);

However you get:

Catchable fatal error: Cannot unpack array with string keys

This will work, but you won't get the string keys:

withToken(...[
       'second',
       'third'
   ]);

Same with this (no string keys):

call_user_func_array('withToken', [
       'second' => 'second',
       'third' => 'third'
   ]);

If you need string keys and values, stick with passing an array and not variable number of arguments.

Sign up to request clarification or add additional context in comments.

Comments

1

It has to do with how PHP is passing the arguments. If you dump out the first function you'll get

array(1) { [0]=> array(2) { ["second"]=> string(6) "second" ["third"]=> string(5) "third" } }

The reason why it does this is you're telling PHP to accept a variable number of arguments. Your array parameter only counts as one argument

In the second instance it's being passed as the array itself. As it's the only argument, PHP doesn't need to transform the data.

If you want it to work the way it's written, do this

call_user_func_array('withToken', [
   'second' => 'second',
   'third' => 'third'
]);

This works because it converts your array into separate arguments

Comments

0

The accepted answer explained why you get this behavior, but the recommended solution misses the point: It suggests that you change the way you call the function. This is exactly wrong: a function that accepts a variable number of arguments should handle its arguments accordingly, so that it can be called like any other function.

In particular, you know that your withToken() function accepts a variable number of arguments, all of which will be available in the list $arguments. So all you need to do is scan this list properly:

function withToken(...$arguments)
{
    $params = [];
    $params['first'] = 'first';

    if (count($arguments) > 0) {
        $real_arg = $arguments[0]  

        foreach ($real_arg as $key => $value) {
            $params[$key] = $value;
        }
    }

    return $params;
}

With this definition, you can call your function with an array with string keys, and it will behave as you expected:

   withToken([
   'second' => 'second',
   'third' => 'third'
  ]);

Of course this does not call withToken() with a variable number of arguments, but with just one argument (an array). And my version expects at most one argument. If withToken() was designed to be used with multiple arguments, you might invoke it like this:

withToken('second', 'third');

These are collected in to the array $arguments (necessarily with numerical indexes), which should be processed with the loop in the original version of your function.

2 Comments

Actually, the accepted answer recommends If you need string keys and values, stick with passing an array and not variable number of arguments. As in OP's examples it does not appear that variable number of arguments are needed.
Yes, "... and not [a] variable number of arguments." My answer shows how it's done with the variable number of arguments feature. You're right that the number of arguments in the OP's examples doesn't vary (as I also mention in my answer), but the OP is trying to learn how to use this feature.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.