Variadic — a function that accepts any number of arguments
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
// 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');