Partial Function Application in PHP 8.6: Pre-Fill Arguments Without Closures cover image

Partial Function Application in PHP 8.6: Pre-Fill Arguments Without Closures

Scott Keck-Warren • November 6, 2026

PHP 8.6 is coming this month, and we'll spend the month highlighting some of its more interesting features. To start, we'll discuss Partial Function Application (PFA) and how it, combined with the pipe operator, can clean up our code.

We can write something like this:

array_map(static fn(string $string): string => str_replace('Scott', 'Alice', $string), $names)

It's not terrible, but it can be a challenge to read.

PHP 8.6 makes it much more compact:

array_map(str_replace('Scott', 'Alice', ?), $names)

This is a PFA, and it's going to make our code a lot easier to read in PHP 8.6+. It's one of the features in this release I expect to use most.

The Foundation: First-Class Callables

Before we get too far into PFAs, we need to discuss First-Class Callables (this topic is just heavy with Computer Science terms that require a ton of research).

First-Class Callables were added in PHP 8.1 as a way of creating anonymous functions from a callable.

This makes it so we can define an anonymous function like:

$name = "Scott Keck-Warren";
$function = strtolower(...);
// output scott keck-warren
echo $function($name);

The ... allows you to tell PHP that you want to leave every argument open, but it doesn't work if you want to "partially" leave arguments open. This raises its head when we want to use something with multiple arguments like str_replace().

How the Placeholders Work

PHP 8.6 adds ?, which allows us to fill in one argument slot for later and then use ... for zero or more remaining arguments.

// from https://wiki.php.net/rfc/partial_function_application_v2
// Given
function foo(int $a, int $b, int $c, int $d): int
{
    return $a + $b + $c + $d;
}

$f = foo(1, ?, 3, 4);
$f = static fn(int $b): int => foo(1, $b, 3, 4);

$f = foo(1, ?, 3, ?);
$f = static fn(int $b, int $d): int => foo(1, $b, 3, $d);

$f = foo(1, ...);
$f = static fn(int $b, int $c, int $d): int => foo(1, $b, $c, $d);

$f = foo(1, 2, ...);
$f = static fn(int $c, int $d): int => foo(1, 2, $c, $d);

$f = foo(1, ?, 3, ...);
$f = static fn(int $b, int $d): int => foo(1, $b, 3, $d);

The result is a Closure where reflection and static analysis can see the types but without us needing to list all of the parameters every time (especially if we're using it multiple times).

Named Arguments as Placeholders

Named placeholders aren't left out of this new feature, so we can do:

$makeAlice = str_replace(search: ?, replace: 'Alice', subject: ?);
// output "Alice Keck-Warren";
echo $makeAlice('Scott', 'Scott Keck-Warren');

PFA and the Pipe Operator

Where this really gets fun is when we use it with the pipe operator. The pipe operator only works on single-argument Callables, so we were forced to use the arrow function (which again works, but it's a little hard to read).

$name = "Scott Keck-Warren";
$fixed = $name
    |> (fn($s) => str_replace('Scott', 'Alice', $s))
    |> strtolower(...);
// alice keck-warren
echo $fixed;

Compare that to the version using PFAs.

$fixed = $name
    |> str_replace('Scott', 'Alice', ?)
    |> strtolower(...);

I think you would argue it's easier to read.

What you need to know

Ultimately, PFAs turn a "let's call this later when I have the arguments" section into a one-liner and are an amazing augmentation of the pipe operator. It's going to be another tool in our toolbox to create readable code (once everyone's on board with it).

It's also just one of several new features I covered in my PHP 8.6 roundup. Watch the video at https://www.youtube.com/watch?v=U9Jrcg1Ns1k, or read the written version at https://www.phparch.com/2026/09/stop-writing-php-like-this-8-6-fixes-it/ . We're also going to cover clamp() and SortDirection next week, so make sure you subscribe.

AI notice:

The outline for this post was generated via AI, then I wrote the prose and ran it through Grammarly