Table of Contents

PHP RFC: Additional Usage for the Splat Operator

Introduction

This RFC proposes an additional usage in array literal declarations for the argument unpacking operator introduced in PHP 5.6.

Proposal

The argument unpacking operator ...$var (hereafter referred to as the “splat” operator) currently permits unpacking arrays into an argument list when variadic functions are called, as well as collecting variadic arguments into an array in the callee. However, there are other cases where it can make sense to use a similar construct for brevity and readability. This RFC covers one of these cases which is commonly available in other languages that support a similar operator. This feature is already supported by ECMAScript 6.

Combining arrays in literal declaration syntax

A common operation is to merge an existing array stored in a variable with another array that is being declared literally. Currently this is usually done using array_merge() or the + operator:

$arr1 = ['d' => 4, 'e' => 5, 'f' => 6];
$arr2 = array_merge(['a' => 1, 'b' => 2, 'c' => 3], $arr1);
$arr3 = ['a' => 1, 'b' => 2, 'c' => 3] + $arr1;

array_merge() is quite verbose and carries the overheads of invoking a function. The + operator is not always suitable for such an operation and has a cognitive overhead when reading the code, because of the differences in behaviour from array_merge() and may be confusing because you don't always know if you are dealing with arrays or numbers.

This RFC proposes allowing the splat operator to be used in array literals as another way to perform this operation:

$arr1 = ['d' => 4, 'e' => 5, 'f' => 6];
$arr2 = ['a' => 1, 'b' => 2, 'c' => 3, ...$arr1];

The new syntax gives equivalent behaviour to the array_merge() example above, where the contents of $arr1 are appended to the array literal, and stored in $arr2. The same rules for merging are followed; in the case of duplicated string keys, the later value overwrites the earlier when read from left to right, and numerically indexed arrays are appropriately re-keyed and appended. Any number of variables can be unpacked at any position in the array literal, and may be combined with regular element declarations in any order.

As such, the following application of array_merge() and of the splat operator and should be equivalent:

$array1 = ["color" => "red", "model" => "Corolla"];
$array2 = ["type" => "car", "year" => "2002"];
 
$result = array_merge($array1, $array2);
$result2 = [...$array1, ...$array2];

A mixture of applying the splat operator to previously assigned variables and other literal should work just as fine:

$array1 = ["color" => "red", "model" => "Corolla"];
$array2 = ["type" => "car", "year" => "2002"];
 
$result = array_merge($array1, ["category" => "suv", "condition" => "good"], $array2);
$result2 = [...$array1, ...["category" => "suv", "condition" => "good"], ...$array2];

The operator should have the same behaviour for numeric and associative arrays. Multidimensional arrays should not be flattened: the splat operator should have effect in the first level keys only.

Numeric

$threetofive = [3,4,5];
$zerotofive = [0, 1, 2, ...$threetofive];

Associative

$addressData = ["street" => "George St.", "number" => 2];
$cityData = ["city" => "Brisbane", "postcode" => 4000];
 
$propertyData = ["name" => "QUT", ...$addressData, ...$cityData];

Multidimensional

$old_marks = [
 "john" => [
    "maths" => 0,
    "english" => 8
 ],
 "jane" => [
    "maths" => 10,
    "english" => 7
 ],
 "joe" => [
    "maths" => 0,
    "english" => 8
  ]
];
 
$new_marks = [
  "matthew" => [
    "maths" => 5,
    "english" => 5
  ],
  ...$old_marks
]; /* array(4) { ["matthew"]=> ... ["john"]=> ... ["jane"]=> ... ["joe"]=> ... }*/

Using the splat operator with elements that are not arrays and not Traversable should cause an error.

Backward Incompatible Changes

None.

Proposed PHP Version(s)

This RFC targets PHP 7.2.

RFC Impact

To SAPIs

None.

To Existing Extensions

None.

To Opcache

TODO.

Open Issues

Unaffected PHP Functionality

No existing functionality is affected by this, other than the new capabilities outlined in the main proposal.

Future Scope

Another feature common in languages that implement a similar construct is the ability to store “the rest” of the elements in a list() assignment operation, in such a manner that the following operations would be equivalent:

$arr = [1, 2, 3, 4, 5, 6];
 
// how we can currently do this
$theRest = array_splice($arr, 2);
list($one, $two) = $arr;
 
// something that could be implemented instead
list($one, $two, ...$theRest) = $arr;

This case is not covered by this RFC, because the nature of PHP arrays combined with the way the list operator works makes this ambiguous with respect to how it should function in the case of arrays which are not simply contiguously 0-indexed. A future RFC could look to implement this, if an agreement could be reached on how it should function.

Proposed Voting Choices

Extend the splat operator usage yes/no? Requires a 2/3 + 1 majority.

Patches and Tests

Marcelo Camargo is currently writing a patch for this RFC.

References