Home: Roshan Kc

← All posts

PHP · Releases 9 min read

PHP 8.6: New Features and the RFCs to Watch

Direct answer: PHP 8.6 is scheduled for November 19, 2026 (it's at RC5 as I write this). The headline features are partial function application with ? placeholders, a native clamp(), a Time\Duration class, readonly property defaults, a native polling API (Io\Poll) and more secure session defaults. It's an incremental release: useful quality-of-life changes, no rewrite required.

This is part of my series on where PHP stands in 2026. Features below are taken from the RFCs marked Implemented for 8.6 on the PHP wiki. I'll update this post once the final release ships.

Partial function application: the headline feature#

Partial function application (PFA) lets you call a function with some arguments filled in and get back a closure that takes the rest. A ? stands for exactly one argument and ... stands for "all remaining arguments". The RFC passed 33–0.

// Before: a wrapper closure for every pre-filled argument
$toSlug = fn(string $s): string => str_replace(' ', '-', $s);

// PHP 8.6
$toSlug = str_replace(' ', '-', ?);
$toSlug('Hello World'); // "Hello-World"

$lower = array_map(strtolower(...), $titles);  // first-class callable (8.1)
$trimmed = array_map(trim(?, " \n"), $lines);  // partial application (8.6)

Where it really pays off is with the pipe operator from PHP 8.5. In 8.5 you had to wrap any multi-argument call in an arrow function. In 8.6 you don't, and the engine compiles a single-placeholder partial on the right of |> into a direct call, so there's no closure overhead:

// PHP 8.5
$slug = $title
    |> trim(...)
    |> (fn($s) => str_replace(' ', '-', $s))
    |> strtolower(...);

// PHP 8.6
$slug = $title
    |> trim(...)
    |> str_replace(' ', '-', ?)
    |> strtolower(...);

A few rules worth knowing:

  • Arguments you pass are evaluated when the partial is created, not when it's called.
  • The closure keeps the original parameter names, types and defaults.
  • new Foo(1, ?) isn't supported; use a static factory method instead.

clamp()#

Every codebase has written max($min, min($max, $value)) at some point, and gotten it backwards at least once. PHP 8.6 adds it natively:

clamp(101, min: 0, max: 100); // 100
clamp(-1,  min: 0, max: 100); // 0

It works with any comparable values, including DateTime objects, and throws a ValueError if $min is greater than $max.

The Duration class#

Time\Duration is an immutable, nanosecond-precision time span. That's useful for timeouts, retries and measuring elapsed time without juggling integers that might be seconds or might be milliseconds.

use Time\Duration;

$timeout = Duration::fromSeconds(5);
$backoff = Duration::fromMilliseconds(100)->multiplyBy(2 ** $attempt);

if ($backoff > $timeout) {
    // give up
}

Readonly property defaults#

Readonly properties can finally have a default value. Before 8.6, this was a compile-time error:

final readonly class CreateBooksTable implements Migration
{
    public string $name = '2026_01_01_create_books_table';
}

The default counts as the one allowed initialisation, so the constructor can't overwrite it. It passed 24–0.

Io\Poll: a native polling API#

The new Io\Poll namespace wraps each operating system's efficient event mechanism (epoll on Linux, kqueue on macOS/BSD, WSAPoll on Windows) behind one API. Application developers will rarely touch it directly. It matters because event loops and async libraries can stop relying on the decades-old stream_select(). More on this in PHP async in 2026.

Smaller changes worth knowing#

ChangeWhy it matters
Secure session defaults: use_strict_mode=1, cookie_httponly=1, cookie_samesite=LaxSafer native sessions out of the box. Laravel and Symfony manage their own cookies, so most framework apps won't notice.
SortDirection enum (Ascending, Descending)A shared type so libraries stop inventing their own 'asc'/'desc' strings.
#[\Override] on class constantsCatches typos and removed parent constants at compile time.
Enums can implement __debugInfo()Cleaner var_dump() output when debugging.
Function arguments shown in error messagesFaster debugging from logs.
json_decode() errors include the positionNo more guessing where malformed JSON broke.
trim() strips form-feed (\f) by defaultA minor behaviour change to watch for in tests.
Doc comments on function parameters, grapheme_strrev(), closure optimisationsBetter tooling, correct Unicode string reversal, and faster closures.

8.6 also deprecates returning values from __construct() and __destruct(), alongside a batch of smaller deprecations. Run your test suite with deprecations visible before upgrading.

RFCs to watch after 8.6#

  • Concurrency Support in the PHP Engine (under discussion): an engine-level coroutine core and pluggable scheduler extracted from the TrueAsync work. It's the most important long-term proposal for PHP right now.
  • Pattern matching with an is keyword (under discussion): a single expression to check a value's type and shape, much like instanceof extended to array shapes and literal values.
  • Partial application for $this methods (under discussion): extends PFA to instance methods.
  • Generics: there's no live generics RFC. The most recent attempt (bound-erased generic types) was declined. PHPStan and Psalm remain the answer; see PHP's type system.

How a feature gets into PHP#

Every language change goes through an RFC on wiki.php.net. It's discussed on the internals mailing list, then voted on by core contributors, and language changes need a two-thirds majority. Releases follow a fixed yearly cycle: alphas in summer, feature freeze in September, release candidates through autumn and general availability in late November. That predictability is underrated. You can plan upgrades a year ahead.

Should you upgrade to 8.6?#

  • New projects: start on 8.6 once your framework and key packages officially support it, usually within weeks.
  • Existing apps on 8.4 or 8.5: no rush. Upgrade in Q1 after the first patch releases, with deprecations turned on in CI.
  • Apps on 8.2 or older: 8.2's security support ends December 31, 2026. That's your real deadline, not 8.6. See the upgrade playbook.

Key takeaways#

  • PHP 8.6 is due November 19, 2026, and is an incremental, low-risk release.
  • Partial function application with ? and ... is the headline, and it makes the 8.5 pipe operator far more practical.
  • clamp(), Time\Duration, readonly defaults and Io\Poll remove common boilerplate and lay groundwork for async.
  • Native async, pattern matching and generics are not in 8.6. Async is the proposal to watch.

Sources#