Array Spread Operator
What Is the Array Spread Operator?
In simple terms: The spread operator ... copies all elements of one array into another array literal. [...$a, ...$b] creates a new array containing the elements of $a followed by the elements of $b.
Technically: Inside an array literal, ...$expression unpacks an array or any Traversable (such as a generator) at that position. Array unpacking with integer keys was introduced in PHP 7.4; unpacking arrays with string keys is supported since PHP 8.1.
$combined = [...$first, 'extra item', ...$second];
Why It Is Useful
- Combine lists into a new list in a readable, literal-like way.
- Add items before, between, or after existing elements without modifying the originals.
- Merge settings with defaults (PHP 8.1+).
- Convert generators and iterators into arrays inline.
The same ... operator also unpacks arrays into function arguments (see Variadic Functions); this lesson focuses on its use inside array literals.
Combining Lists
Example
<?php
$fruits = ['Mango', 'Banana'];
$vegetables = ['Carrot', 'Beans'];
$groceries = [...$fruits, ...$vegetables];
$withExtras = ['Rice', ...$fruits, 'Milk'];
print_r($groceries);
echo implode(', ', $withExtras) . "\n";
echo implode(', ', $fruits) . "\n";
Output
Array
(
[0] => Mango
[1] => Banana
[2] => Carrot
[3] => Beans
)
Rice, Mango, Banana, Milk
Mango, Banana
How It Works
...$fruitsinserts'Mango'and'Banana'at that position in the new array.- Several arrays can be spread in one literal, and normal elements can be placed anywhere.
- The original arrays are not modified —
$fruitsis unchanged.
Integer Keys Are Renumbered
When spreading arrays with integer keys, PHP does not keep the original keys. Elements receive new sequential keys:
<?php
$featured = [5 => 'Desk Lamp', 9 => 'Notebook'];
$new = [2 => 'Cushion'];
print_r([...$featured, ...$new]);
Output
Array
(
[0] => Desk Lamp
[1] => Notebook
[2] => Cushion
)
This is exactly what you want for lists: no gaps and no overwritten elements. It also means the spread operator is a convenient way to re-index a list: [...$list] behaves like array_values($list) for lists with integer keys.
String Keys (PHP 8.1+)
Since PHP 8.1, arrays with string keys can be spread too. When the same string key appears more than once, the later value wins:
<?php
$defaults = ['theme' => 'light', 'language' => 'en', 'perPage' => 20];
$userSettings = ['language' => 'ta', 'perPage' => 50];
$settings = [...$defaults, ...$userSettings];
print_r($settings);
Output
Array
(
[theme] => light
[language] => ta
[perPage] => 50
)
- String keys are preserved.
languageandperPagefrom$userSettingsoverwrite the defaults, but keep the position where they first appeared.- This behaves like
array_merge($defaults, $userSettings).
Version note: In PHP 7.4 and 8.0, spreading an array with string keys throws an
Error("Cannot unpack array with string keys"). Usearray_merge()when supporting those versions.
Mixed Keys
<?php
$base = ['title' => 'Report', 10 => 'first page'];
$extra = ['author' => 'Asha', 20 => 'second page'];
print_r([...$base, ...$extra]);
Output
Array
(
[title] => Report
[0] => first page
[author] => Asha
[1] => second page
)
String keys are preserved, and integer keys are renumbered, in order of appearance.
Spreading Generators and Iterators
Any Traversable can be spread, which turns lazily produced values into an array:
<?php
function evenNumbers(int $limit): Generator
{
for ($n = 2; $n <= $limit; $n += 2) {
yield $n;
}
}
$numbers = [0, ...evenNumbers(8), 100];
echo implode(', ', $numbers) . "\n";
Output
0, 2, 4, 6, 8, 100
This is a compact alternative to iterator_to_array() when you also want to add other elements. Generators are covered in PHP Generators.
Only Arrays and Traversables
<?php
$maybeList = null;
try {
$result = [...$maybeList];
} catch (Error $e) {
echo $e->getMessage() . "\n";
}
$safe = [...($maybeList ?? [])];
var_dump($safe);
Output
Only arrays and Traversables can be unpacked
array(0) {
}
Data from functions or optional input may be null. Default it to an empty array with ?? [] before spreading.
Spread vs array_merge() vs +
[...$a, ...$b] | array_merge($a, $b) | $a + $b | |
|---|---|---|---|
| Integer keys | Renumbered | Renumbered | Kept; duplicates from $b ignored |
| String keys, duplicates | Later value wins (PHP 8.1+) | Later value wins | First value wins |
| Can add individual elements inline | Yes | No | No |
| Works with generators | Yes | No (arrays only) | No |
| Typical use | Building lists, combining arrays | Combining arrays, settings | Filling in missing defaults |
Example: The Difference for Lists
<?php
$a = ['A', 'B'];
$b = ['C', 'D', 'E'];
echo implode(', ', [...$a, ...$b]) . "\n";
echo implode(', ', array_merge($a, $b)) . "\n";
echo implode(', ', $a + $b) . "\n";
Output
A, B, C, D, E
A, B, C, D, E
A, B, E
$a + $b keeps keys 0 and 1 from $a and adds only key 2 ('E') from $b — the union operator is not suitable for combining lists. See Merge Arrays for a full comparison.
Spreading Creates a Shallow Copy
The spread operator copies the elements of an array:
- Nested arrays are copied as values (arrays are always copied on assignment in PHP), so changing the new array does not affect the original.
- Objects inside the array are copied as handles, so both arrays refer to the same objects.
<?php
$original = [['qty' => 1], new ArrayObject(['qty' => 1])];
$copy = [...$original];
$copy[0]['qty'] = 5; // nested array: independent
$copy[1]['qty'] = 5; // object: shared
echo $original[0]['qty'] . "\n";
echo $original[1]['qty'] . "\n";
Output
1
5
Use clone when objects inside an array must be copied independently.
Practical Example: Building Select Options
<?php
$cities = ['MAA' => 'Chennai', 'BLR' => 'Bengaluru', 'HYD' => 'Hyderabad'];
$selected = 'BLR';
$options = ['' => 'Select a city', ...$cities];
foreach ($options as $code => $label) {
$isSelected = $code === $selected ? ' selected' : '';
printf(
"<option value=\"%s\"%s>%s</option>\n",
htmlspecialchars($code),
$isSelected,
htmlspecialchars($label)
);
}
Output
<option value="">Select a city</option>
<option value="MAA">Chennai</option>
<option value="BLR" selected>Bengaluru</option>
<option value="HYD">Hyderabad</option>
Step-by-Step Explanation
['' => 'Select a city', ...$cities]builds a new array with a placeholder option first, followed by all cities. The string keys (city codes) are preserved (PHP 8.1+).$citiesitself is unchanged and can be reused elsewhere — for example, to validate a submitted value withisset($cities[$_POST['city']]).- The loop outputs one
<option>per entry, marking the selected city and escaping all values.
Common Mistakes
Expecting Integer Keys to Be Preserved
Effect: IDs used as keys ([101 => 'Asha']) are replaced with 0, 1, ….
Fix: Use + or array_replace() when integer keys carry meaning.
Spreading null or Scalars
Effect: Error: Only arrays and Traversables can be unpacked.
Fix: Default with ?? [].
Spreading String Keys on PHP < 8.1
Effect: Error: Cannot unpack array with string keys.
Fix: Use array_merge() for older PHP versions.
Assuming a Deep Copy
Effect: Objects inside the copied array are shared.
Fix: Clone objects explicitly when independent copies are required.
Best Practices
- Use
[...$a, ...$b]to combine lists and to insert elements inline. - Use string-key spreading (PHP 8.1+) for simple "defaults + overrides" merges.
- Use
+orarray_replace()when integer keys must be preserved. - Guard possibly-
nullvalues with?? []before spreading. - Remember that spreading creates a new array; the originals are never modified.
Official reference: https://www.php.net/manual/en/language.types.array.php#language.types.array.unpacking