PHPVariadic Functions & Spread

Variadic Functions & Spread

Most of the time a function has a fixed list of parameters, but PHP also lets you write functions that accept any number of arguments. This is done with the ... (splat) operator placed before the last parameter, which is called a variadic parameter. The same ... operator works in reverse too: placed in front of an array at a call site, it spreads that array's elements out into individual arguments. Together, variadics and spread let you write flexible functions like sum(), max(), or printf() without forcing the caller to build an array first.

Collecting arguments with ...$args

When you prefix the last parameter in a function signature with ..., PHP gathers every extra positional argument passed to the call into a plain array with that name. Inside the function body you just loop over it like any other array.

A variadic sum() function

PHP
<?php
function sum(...$numbers)
{
    $total = 0;
    foreach ($numbers as $n) {
        $total += $n;
    }
    return $total;
}

echo sum(1, 2, 3);
echo "\n";
echo sum(10, 20, 30, 40);
echo "\n";
echo sum(); // no arguments at all is perfectly legal
6
100
0

Notice that calling sum() with zero arguments does not raise any error — $numbers is simply an empty array, and the loop body never runs, so the running total stays 0. This is one of the reasons variadics are nicer than requiring the caller to always pass an array: the "no arguments" case falls out naturally.

Typed variadic parameters

A variadic parameter can carry a type declaration just like a normal one. PHP checks the type of every argument collected into the array, not just the first, so function sum(int ...$numbers) will reject a stray string or float passed in the middle of the call (unless it is a numeric string PHP can coerce, or strict_types is enabled, in which case it throws a TypeError immediately).

Type-checked variadics

PHP
<?php
function sum(int ...$numbers): int
{
    return array_sum($numbers);
}

echo sum(1, 2, 3);
echo "\n";

try {
    echo sum(1, 2, "not-a-number");
} catch (TypeError $e) {
    echo "Caught: " . $e->getMessage();
}
6
Caught: sum(): Argument #3 ($numbers) must be of type int, string given, called in ...
Mixing fixed parameters with a variadic tail

A variadic parameter must be the last one in the signature, but it is fine to have ordinary required parameters before it. This is a common pattern for functions like a logger that always needs a message but can accept an arbitrary number of extra context values.

Fixed parameter followed by a variadic tail

PHP
<?php
function logMessage(string $level, ...$context)
{
    echo strtoupper($level) . ": " . implode(", ", $context) . "\n";
}

logMessage("info", "user logged in", "id=42");
logMessage("error");
INFO: user logged in, id=42
ERROR: 
Spreading an array into a call

The reverse situation is when you already have an array and want to pass each of its elements as a separate argument. Prefixing the array with ... at the call site unpacks it. This is exactly what array_merge(...$arrays) style code relies on when you have a list of arrays you want combined without hardcoding how many there are.

Unpacking arrays into array_merge()

PHP
<?php
$defaults = ["color" => "blue", "size" => "M"];
$overrides = ["size" => "L", "material" => "cotton"];
$extra = ["price" => 25];

$groups = [$defaults, $overrides, $extra];

$merged = array_merge(...$groups);
print_r($merged);
Array
(
    [color] => blue
    [size] => L
    [material] => cotton
    [price] => 25
)

The same spread also works for plain indexed arrays passed into a variadic-style function like sum():

Spreading numbers into sum()

PHP
<?php
function sum(...$numbers)
{
    return array_sum($numbers);
}

$values = [4, 8, 15, 16, 23, 42];
echo sum(...$values);
108
Combining spread with named arguments

Named arguments (func(name: $value)) and spread can be combined when you spread an associative array — each key is matched to the parameter of the same name, so order stops mattering. This is useful when configuration lives in an array but the target function has named parameters rather than a variadic signature.

Spreading an associative array as named arguments

PHP
<?php
function createUser(string $name, int $age, string $role = "member")
{
    return "{$name} ({$age}) - {$role}";
}

$data = ["age" => 34, "name" => "Priya", "role" => "admin"];

echo createUser(...$data);
Priya (34) - admin
PHP 8.3: named arguments after a variadic spread
Before PHP 8.3, once you spread positional arguments into a call you could not also pass named arguments afterward. PHP 8.3 lifted that restriction, so code like `sum(...$values, extra: 5)` (against a function that accepts a named `$extra` parameter alongside a variadic) is now valid where it previously raised a compile error.
Variadics do not accept a mix of scalar types silently
  • An untyped variadic parameter (...$args with no type hint) accepts absolutely anything, including mixed types in the same call.

  • A typed variadic parameter (int ...$args) validates each individual value against that type, not just the collected array as a whole.

  • You cannot make a variadic parameter also have a default value — its "default" is always an empty array when nothing is passed.

  • Only one variadic parameter is allowed per function, and it must be the last parameter in the list.

Variadic parameter must come last
Writing `function bad(...$rest, $name)` is a fatal compile-time error. PHP needs to know exactly where the fixed parameters end before it can start collecting the rest, so the variadic parameter is only ever allowed at the very end of the signature.
Tip
Reach for a variadic parameter when the *number* of arguments is genuinely open-ended (like `sum()` or a logging helper's context values). If you find yourself always passing the same handful of related values together, an array parameter or a dedicated value object usually communicates intent better than `...$args`.