0

Spread operator — unpacking arrays into argument lists

Beginner5 min read·eng-12-018
compare

Concept

Spread operator (...) — unpacks an array (or any Traversable) into individual arguments when calling a function, or merges arrays.

Two distinct uses of ...:

  1. In function calls (spread / unpack): fn(...$array) — expands the array into positional arguments.
  2. 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
<?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