231

While I am installing Magento 2 on my Server, I got an error. After investigating the code and found that there are three dots (...), which is producing the error. I included the code I found below:

return new $type(...array_values($args));

What is this operator called, and what is its purpose?

Marcio Mazzucato
  • 8,841
  • 9
  • 64
  • 79
abu abu
  • 6,599
  • 19
  • 74
  • 131
  • array packing/unpacking introduced in PHP 5.6 – Mark Baker Dec 13 '16 at 14:54
  • Did you check the requirements prior to installing : http://devdocs.magento.com/guides/v2.0/install-gde/system-requirements-tech.html – Mathieu de Lorimier Dec 13 '16 at 20:24
  • 1
    **See also:** [PHP splat operator](https://duckduckgo.com/?q=php+splat+operator) – dreftymac Jul 30 '19 at 03:05
  • See also that [PHP8 allows the unpacking of an associative array as named parameters in function/method calls](https://stackoverflow.com/a/64997399/2943403). – mickmackusa Jun 29 '22 at 01:26
  • 1
    Did you work out the error you are getting? I am having an error with the exact line of code on a Magento 2 site after upgrading to PHP 8.1 and Magento 2.4.4. – johnsnails Mar 09 '23 at 06:15

9 Answers9

320

This is literally called the ... operator in PHP, but is known as the splat operator from other languages. From a 2014 LornaJane blog post on the feature:

This feature allows you to capture a variable number of arguments to a function, combined with "normal" arguments passed in if you like. It's easiest to see with an example:

function concatenate($transform, ...$strings) {
    $string = '';
    foreach($strings as $piece) {
       $string .= $piece;
    }
    return($transform($string));  
 }

echo concatenate("strtoupper", "I'd ", "like ", 4 + 2, " apples");

(This would print I'D LIKE 6 APPLES)

The parameters list in the function declaration has the ... operator in it, and it basically means " ... and everything else should go into $strings". You can pass 2 or more arguments into this function and the second and subsequent ones will be added to the $strings array, ready to be used.

Marcin Orlowski
  • 72,056
  • 11
  • 123
  • 141
Saumya Rastogi
  • 13,159
  • 5
  • 42
  • 45
  • 3
    Thanks :), why should I use SPLAT operator, instead of this I can pass all those strings in an array as the second argument??? – bikash.bilz Jun 04 '18 at 16:54
  • 3
    @bikash.bilz I'll speak for the answerer: I think it's just [syntactic sugar](https://en.wikipedia.org/wiki/Syntactic_sugar). The splat operator saves you from surrounding the arguments with `[` and `]`. It's not much of a benefit but I think it looks nice. – Cave Johnson Jun 21 '18 at 04:43
  • 46
    You can type-hint the variadic parameters too. So on PHP 7.2, you can define `function myFunc($foo, string ...$bar)`. Then `$bar` gives your function an array of strings and nothing else, guaranteed at run-time. You cannot do that with a single array parameter. – Jason Aug 16 '18 at 15:44
  • 7
    This is more than just syntax sugar because it allows an array in contexts that would otherwise require a fixed number of strings. Using a fixed number of strings requires you to re-factor your source code every time the number of strings might change. – dreftymac Jul 30 '19 at 03:08
  • @dreftymac, do you have an example? – heykatieben Sep 10 '19 at 19:09
  • 5
    An easy example is a function signature used to query a database. `function get_data($fname,$lname,$age)` will have to change if you want fields other than those three `function get_data(...$fields)` does not have to change, you need only specify the fields you want in `$fields`. @heykatieben – dreftymac Sep 10 '19 at 20:58
  • 2
    @dreftymac How is that different than just doing `function get_data($fields)` as an array? It's nice for typing, and it's nice for convenience, but if you actually have a function that takes a set number of named parameters you should not be changing it over to a vararg just so that you can avoid refactoring. If you have a new field that is required, it really does call for a refactor. In your example, is the caller supposed to somehow know the first param is fname, the second is lname? – Guybrush Apr 28 '20 at 21:27
  • 2
    @Guybrush I leave the `you should not`s or the `you should`s for [code review](https://codereview.stackexchange.com) discussion time with the relevant team members. The language feature is there, and there are many reasons why (or why not) to use it. For SO, it is sufficient to explain things to people who do not understand, and let them handle the `should`s for themselves. – dreftymac Apr 29 '20 at 22:30
  • @bikash.bilz you can specify what datatype the params are that get accepted by the splat operator, which will throw a php error if they're wrong, which one can't do with an array or associative array – Fi Horan Aug 14 '20 at 11:12
97

There are TWO uses for the ellipsis (...) PHP token—think of them as packing an array and unpacking an array. Both purposes apply to function arguments.


Pack

When defining a function, if you need a dynamic number of variables provided to the function (i.e., you don't know how many arguments will be provided to that function when called in your code), prefix the ellipsis (...) token to a variable name—e.g., ...$numbers—to capture all (remaining) arguments provided to that function into an array assigned to the named variable—in this case $numbers—that is accessible inside the function block. The number of arguments captured by prefixing ellipsis (...) can be zero or more.

For example:

// function definition
function sum (...$numbers) { // use ellipsis token when defining function
    $acc = 0;
    foreach ($numbers as $nn) {
        $acc += $nn;
    }
    return $acc;
}

// call the function
echo sum(1, 2, 3, 4); // provide any number of arguments

> 10

// and again...
echo sum(1, 2, 3, 4, 5);

> 15

// and again...
echo sum();

> 0

When packing is used in function instantiation, prefixing ellipsis (...) to a variable name captures all remaining arguments, i.e., you can still have any number—zero or more—of initial, fixed (positional) arguments:

function sum ($first, $second, ...$remaining_numbers) {
    $acc = $first + $second;
    foreach ($remaining_numbers as $nn) {
        $acc += $nn;
    }
    return $acc;
}

// call the function
echo sum(1, 2); // provide at least two arguments

> 3

// and again...
echo sum(1, 2, 3, 4); // first two are assigned to fixed arguments, the rest get "packed"

> 10

...the prefixed ellipsis variable captures all the rest. For this reason it must be the final function argument.


Unpack

Alternatively, when calling a function, if the arguments you provide to that function are previously combined into an array use a ellipsis (...) token prefixed variable "inline" to convert that array variable into individual arguments provided to the function. When any number of function arguments are replaced with an ellipsis prefixed variable, each array element is assigned to the respective function argument variable named in the function definition.

For example:

function add ($aa, $bb, $cc) {
    return $aa + $bb + $cc;
}

$arr = [1, 2, 3];
echo add(...$arr); // use ellipsis token when calling function

> 6

$first = 1;
$arr = [2, 3];
echo add($first, ...$arr); // used with positional arguments

> 6

$first = 1;
$arr = [2, 3, 4, 5]; // array can be "oversized"
echo add($first, ...$arr); // remaining elements are ignored

> 6

Unpacking is particularly useful when using array functions to manipulate arrays or variables.

For example, unpacking the result of array_slice:

function echoTwo ($one, $two) {
    echo "$one\n$two";
}

$steaks = array('ribeye', 'kc strip', 't-bone', 'sirloin', 'chuck');

// array_slice returns an array, but ellipsis unpacks it into function arguments
echoTwo(...array_slice($steaks, -2)); // return last two elements in array

> sirloin
> chuck
bloodyKnuckles
  • 11,551
  • 3
  • 29
  • 37
29

Every answer refers to the same blog post, besides them, here is the official documentation about variable-length argument lists:

http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list

In PHP 5.6 and later, argument lists may include the ... token to denote that the function accepts a variable number of arguments. The arguments will be passed into the given variable as an array

It seems "splat" operator is not an official name, still it's cute!

rap-2-h
  • 30,204
  • 37
  • 167
  • 263
17

Meaning is that it decomposes an associative array to a list. So you do not need to type N parameters to call a method, just one. If method allows a decomposed parameter and if parameters are of the same type.

For me, the most important thing about splat operator is that it can help to typehint array parameters:

$items = [
    new Item(), 
    new Item()
];

$collection = new ItemCollection();
$collection->add(...$items); // !

// what works as well:
// $collection->add(new Item());
// $collection->add(new Item(), new Item(), new Item()); // :(

class Item  {};
 
class ItemCollection {
    
    /**
     * @var Item[]
     */
    protected $items = [];
    
    public function add(Item ...$items)
    {
        foreach ($items as &$item) {
            $this->items[] = $item;
        }
    }
} 

it saves some effort on type control, especially while working with huge collections or very object-oriented.

Important to notice is that ...$array do decompose an array despite the type of its items, so you can go the ugly way also:

function test(string $a, int $i) {
    echo sprintf('%s way as well', $a);
    
    if ($i === 1) {
        echo('!');
    }
}

$params = [
    (string) 'Ugly',
    (int) 1
];

test(...$params);

// Output:
// Ugly way as well!

But please don't.


PHP 8 update - named arguments

Since PHP 8 you can now decompose associative arrays, it is, arrays that have keys for them values. If you use it in function that has arguments named the same, PHP will transfer values to proper variables:

$arr = [
  'pi' => 3.14,
  'r' => 4,
];

function circ($r, $pi) {
    return 2*$pi*$r;
}

// so that call
circ(...$arr);

// will be effectively a call of
circ(pi: 3.14, r: 4);

and you can slightly less bother about order of parameters.


PHP 8.1 update - new syntax for creating callable

Despite usage with arrays, ... earned a whole new, very useful functionality that help creating callable from any context:

$f = strtoupper(...); // creating a callable
echo $f('fo');

class test {
    public function func($a) {
        echo $a . PHP_EOL;
    }
}

$f = (new test)
         ->func(...); // creating a callable

$f('x');
yergo
  • 4,761
  • 2
  • 19
  • 41
  • It's not that ugly. Like with Doctrine's OrX functions that needs a list, but you *need* to passe an array (because you don't know how many elements you will pass). I find this way better than using call_user_func_array – Erdal G. Jul 17 '20 at 11:18
  • @ErdalG. yet this is an array of one type conditional objects I assume? – yergo Nov 22 '20 at 14:15
  • Well honestly I don't remember the example I had in head 3 years before haha (I still should have written it). It must have been some artbitrary array of generated expressions. For the array of unique conditions you can obviously replace with with `->in()` or similar methods – Erdal G. Nov 22 '20 at 14:45
  • `echo sprintf()` is an antipattern. Whenever you think your script needs it, don't use it, just type `printf()`. – mickmackusa Jun 29 '22 at 01:21
17

In PHP 7.4 the ellipsis is also the Spread operator:

$parts = ['apple', 'pear'];
$fruits = ['banana', 'orange', ...$parts, 'watermelon'];
// ['banana', 'orange', 'apple', 'pear', 'watermelon'];

Source: https://wiki.php.net/rfc/spread_operator_for_array

jawira
  • 3,668
  • 2
  • 17
  • 27
13

In PHP 8.1, this syntax "(...)" is used as a new way to create callables.

Before PHP 8.1:

$callable = [$this, 'myMethod'];

After PHP 8.1:

$callable = $this->myMethod(...);

Source: https://wiki.php.net/rfc/first_class_callable_syntax

jawira
  • 3,668
  • 2
  • 17
  • 27
  • 8
    This is not the answer to the author's original question, but it may be relevant to anyone who googles 'php 3 dots' – michal3377 Oct 24 '21 at 21:43
  • Now, it's in the doc: https://www.php.net/manual/en/functions.first_class_callable_syntax.php – ssi-anik Sep 29 '22 at 01:23
  • And, if want to check the usage: https://github.com/symfony/console/blob/17524a64ebcfab68d237bbed247e9a9917747096/Question/Question.php#L185 – ssi-anik Sep 29 '22 at 01:25
  • @michal3377 the author's original question was what the `...` operator is, and what is its purpose. While the author did not encounter it in this context originally, this is the same operator with the same name and an additional purpose so techincally the author did also ask about this even if they didn't realise at the time – apokryfos Jul 27 '23 at 04:37
6

To use this feature, just warn PHP that it needs to unpack the array into variables using the ... operator. See here for more details, a simple example could look like this:

$email[] = "Hi there";
$email[] = "Thanks for registering, hope you like it";

mail("someone@example.com", ...$email);
Mahendra Jella
  • 5,450
  • 1
  • 33
  • 38
4

This is the so called "splat" operator. Basically that thing translates to "any number of arguments"; introduced with PHP 5.6

See here for further details.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
1

It seems no one has mentioned it, so here to stay[It will also help Google (& Other SEs) guide devs who asking for Rest Parameters in PHP]:

As indicated here its called Rest Parameters on JS & I prefer this meaningful naming over that splat thing!

In PHP, The functionality provided by ...args is called Variadic functions which's introduced on PHP5.6. Same functionality was used to be implemented using func_get_args().

In order to use it properly, you should use rest parameters syntax, anywhere it helps reducing boilerplate code.

behkod
  • 2,647
  • 2
  • 18
  • 33