PHP 8.6 adds partial function application, which creates a closure with some function arguments already filled in. The RFC by Larry Garfield and Arnaud Le Blanc passed 33-0 and was merged for PHP 8.6 alpha 3.
Instead of passing every argument in a function call, developers can leave one or more placeholders. PHP returns a closure that accepts the missing arguments later.
The ? Placeholder
A ? stands for one argument that will be supplied later:
function tag(string $element, string $cssClass, string $content): string { return "<$element class=\"$cssClass\">$content</$element>"; } $alert = tag('div', 'alert', ?); echo $alert('Your session expired.'); // <div class="alert">Your session expired.</div>
$alert is a closure that accepts one string. The first two arguments are already set. The same behavior is possible with an arrow function, but partial application avoids repeating the parameter name and type.
More than one argument can be left open. Positional placeholders keep their original order:
$div = tag('div', ?, ?); echo $div('alert', 'Your session expired.');
The ... Placeholder
? covers one argument. ... covers all the remaining ones:
$div = tag('div', ...); echo $div('alert', 'Your session expired.');
PHP 8.1 introduced first-class callable syntax, where strtolower(...) creates a closure for the full function. PHP 8.6 keeps that behavior and allows arguments before .... In tag('div', ...), the first argument is already set and the closure accepts the rest.
Named arguments can also use placeholders. This can make longer function signatures easier to read:
$alert = tag(element: 'div', cssClass: 'alert', content: ?);
The Closure Keeps the Signature
The generated closure keeps the parameter names, types, return type, and by-reference behavior of the original function. Reflection and static-analysis tools can inspect that signature.
A later optional-placeholder RFC changed how defaults work. Every ? placeholder becomes a required closure parameter, even when the original parameter has a default. Parameters pulled in through ... keep their optional status and default values.
Why It Matters for the Pipe Operator
The pipe operator added in PHP 8.5 needs a callable that accepts one argument. Many useful functions accept two or more, so developers have had to wrap them in closures.
Partial application can create the one-argument closure that a pipe needs. The placeholder marks where the piped value will go:
$total = $lineItems |> array_map(fn (Item $item) => $item->price, ?) |> array_sum(...);
array_map() accepts a callback and an array. Here, ? marks the array argument. The partial call becomes a one-argument closure that the pipe can use.
PHP can remove the intermediate closure when a partial call appears directly on the right side of a pipe and the compiler can prove that it accepts one argument. More complex partial calls still create a closure as normal.
The Vote
The RFC needed a two-thirds majority and passed 33-0. It introduces new syntax and does not list any backward-incompatible changes.
Read More
The Partial Function Application v2 RFC explains the full behavior, including variadic and named placeholders. The follow-up RFC covers optional placeholder parameters. We have also covered PHP 8.6 closure optimizations, the wider feature roundup, and the PHP 8.6 beta.