0

Variadic — a function that accepts any number of arguments

Beginner5 min read·eng-12-017

Concept

Variadic functions — functions that accept a variable number of arguments.

The syntax: function name(string ...$args) — the ... (splat operator) before the parameter name collects all remaining arguments into an array.

Variadic parameter rules:

  • A function can have only ONE variadic parameter.
  • The variadic parameter must be LAST.
  • It can have a type hint — all arguments must match that type.
  • If no arguments are passed for the variadic parameter, the array is empty.
  • It can be combined with regular parameters: function process(string $prefix, int ...$numbers).

PHP built-in variadic functions: printf(), sprintf(), array_map(), implode(), etc.

When to use variadic:

  • When the number of arguments is genuinely open-ended: sum(1, 2, 3, 4, 5).
  • When accepting a list of the same type: addTagsToPost(int $postId, string ...$tags).
  • When wrapping another variadic function.

Variadic vs array parameter: sum(int ...$nums) vs sum(array $nums). Variadic: caller writes sum(1, 2, 3). Array: caller writes sum([1, 2, 3]). Variadic is more fluent for small counts; array is more practical for programmatically built lists.

Spread operator for calling: $args = [1, 2, 3]; sum(...$args) — unpacks the array into positional arguments. This is the inverse of ... in parameter definitions.

Named arguments + variadic: fn(...$args) collects named arguments too in some contexts. PHP 8.1 added more flexibility here.

Code Example

php
<?php
// Basic variadic function
function sum(int ...$numbers): int
{
    return array_sum($numbers); // $numbers is an array
}

echo sum(1, 2, 3, 4, 5); // 15
echo sum();               // 0 — empty array, valid

// Variadic with type hint — PHP enforces each argument's type
function concatStrings(string ...$parts): string
{
    return implode('', $parts);
}
echo concatStrings('Hello', ' ', 'World'); // 'Hello World'
// concatStrings(1, 2) would throw TypeError — must be strings

// Mixed regular + variadic
function tag(string $element, string ...$classes): string
{
    $cls = implode(' ', $classes);
    return "<{$element} class='{$cls}'>";
}
echo tag('div', 'container', 'mt-4', 'text-center'); // <div class='container mt-4 text-center'>

// Wrapper — pass-through variadic args
function logAndCall(callable $fn, mixed ...$args): mixed
{
    \Log::info('Calling', ['fn' => get_debug_type($fn), 'args' => $args]);
    return $fn(...$args); // unpack variadic array as arguments to $fn
}

logAndCall('strlen', 'hello'); // logs, then returns 5
logAndCall('implode', ',', ['a', 'b', 'c']); // returns 'a,b,c'

// Spread operator for calling (inverse of variadic declaration)
$numbers = [1, 2, 3, 4, 5];
echo sum(...$numbers); // same as sum(1, 2, 3, 4, 5) — unpacks array

// Real-world: query builder-style
class QueryBuilder
{
    private array $wheres = [];

    public function where(string ...$conditions): static
    {
        $this->wheres = array_merge($this->wheres, $conditions);
        return $this;
    }
}

$query = (new QueryBuilder())
    ->where('active = 1')
    ->where('deleted_at IS NULL', 'created_at > NOW() - INTERVAL 30 DAY');

// PHP built-in with variadic internally
printf("Hello %s, you are %d years old", 'Alice', 30); // variadic internally
$formatted = sprintf("(%s) %s-%s", '555', '123', '4567');