Spread operator — unpacking arrays into argument lists
Concept
Spread operator (...) — unpacks an array (or any Traversable) into individual arguments when calling a function, or merges arrays.
Two distinct uses of ...:
- In function calls (spread / unpack):
fn(...$array)— expands the array into positional arguments. - In function definitions (variadic):
fn(...$args)— collects arguments into an array.
The ... operator itself doesn't change meaning — the context tells you which mode is active.
Array unpacking (PHP 7.4+): [...$a, ...$b] — merge arrays via spread in an array literal. More explicit than array_merge().
PHP 8.1 addition: String-keyed array unpacking. Previously, array spread only worked with numeric-keyed arrays. PHP 8.1 allows [...$assoc] for associative arrays.
Named arguments + spread (PHP 8.0+): fn(...$namedArgs) where $namedArgs is an associative array — keys become argument names. fn(name: 'Alice', age: 30) equivalent to fn(...['name' => 'Alice', 'age' => 30]).
Common patterns:
- Forwarding arguments to a parent constructor:
parent::__construct(...$args). - Merging config arrays:
$config = [...$defaults, ...$overrides]. - Collecting and forwarding function arguments.
- Calling a function with dynamically built arguments.
Performance: Spread operator unpacking is slightly faster than call_user_func_array($fn, $args) (which was the pre-7.4 way).
Code Example
<?php
// Spread in function CALL — unpack array into arguments
function sum(int $a, int $b, int $c): int { return $a + $b + $c; }
$args = [1, 2, 3];
echo sum(...$args); // same as sum(1, 2, 3) — 6
// Before PHP 5.6: call_user_func_array('sum', $args); // old way
// PHP 5.6+: sum(...$args); // spread operator
// Merge arrays with spread — cleaner than array_merge
$defaults = ['color' => 'blue', 'size' => 'M', 'quantity' => 1];
$override = ['color' => 'red', 'quantity' => 3];
$merged = [...$defaults, ...$override]; // later keys override earlier ones
// ['color' => 'red', 'size' => 'M', 'quantity' => 3]
// vs array_merge:
$same = array_merge($defaults, $override); // same result
// Numeric arrays — spread appends all elements
$a = [1, 2, 3];
$b = [4, 5, 6];
$c = [...$a, 0, ...$b]; // [1, 2, 3, 0, 4, 5, 6]
// Named arguments spread (PHP 8.0+ with associative arrays)
function createUser(string $name, int $age, string $email): void
{
echo "{$name}, {$age}, {$email}\n";
}
$data = ['name' => 'Alice', 'age' => 30, 'email' => 'alice@example.com'];
createUser(...$data); // 'Alice, 30, alice@example.com'
// Named spread maps array keys to parameter names!
// Forwarding to parent constructor
class LoggedOrderService extends OrderService
{
public function __construct(mixed ...$args) // capture all args
{
parent::__construct(...$args); // forward to parent
\Log::info('OrderService created with ' . count($args) . ' args');
}
}
// Collecting then forwarding (decorator pattern)
function withTiming(callable $fn, mixed ...$args): mixed
{
$start = hrtime(true);
$result = $fn(...$args); // forward all args to the wrapped function
$ms = (hrtime(true) - $start) / 1e6;
\Log::debug("Execution time: {$ms}ms");
return $result;
}
withTiming('array_map', fn($x) => $x * 2, [1, 2, 3]); // logs timing