PHP Composer Web DevelopmentBackend DevelopmentBackend Engineering 5 min read

PHP 8.6 Scheduled for November 19, 2026 With Partial Function Application

PHP 8.6 introduces partial function application, a new Time\Duration class, and performance optimizations ahead of its November 19, 2026, release date.

PHP 8.6 is due on November 19, 2026. Its main feature is partial function application, which lets developers replace many one-off callback closures with a single call that uses ? as a placeholder. The feature freeze passed in September, so the feature list is final. An analysis of the diff between the PHP-8.5 and PHP-8.6 branches of php-src, the language's source repository, produced the details below. Release candidate 5 is scheduled for November 5.

Partial function application

A call such as str_starts_with(?, 'admin.') now returns a closure that takes the missing argument. The code $posts->map(fn (string $title) => Str::limit($title, 10)) can be written as $posts->map(Str::limit(?, 10)). Three placeholders exist: ? for exactly one argument, ... for all remaining arguments, and a named placeholder such as limit: ? for a single parameter.

The returned closure keeps the original function's types, defaults and parameter names, so reflection-based tools such as dependency injection containers and routers can still read it. A ? always makes the parameter required, even if the original had a default, while ... keeps the defaults. PHP raises errors for misuse, such as a named placeholder that overwrites an earlier one.

The feature also fits the pipe operator added in PHP 8.5. A step such as str_replace(' ', '-', ?) can now sit directly in a pipe chain. When a partial with one placeholder is on the right side of a pipe, OPcache compiles it into a direct function call, and no closure is created.

New types and functions

PHP 8.6 adds a Time\Duration class, a final readonly class with seconds, nanoseconds and negative properties. It can be built from hours down to nanoseconds or from an ISO 8601 duration string, and it supports add(), sub(), multiplyBy(), divideBy(), negate(), absolute() and compare(). The new polling API already requires it: passing an integer as the timeout to Io\Poll\Context::wait() throws a TypeError.

A built-in clamp() function replaces the max($min, min($max, $value)) pattern and works on any comparable values, including strings. It throws an error if the minimum is greater than the maximum. A global SortDirection enum with Ascending and Descending cases is also added. No built-in function uses it yet; it is meant to give libraries a shared type in place of 'asc' and 'desc' strings.

Language and library changes

  • Readonly properties can declare a default value in place.
  • Properties of an object held in a constant can be written, though the constant cannot be reassigned. Enum case properties stay immutable.
  • #[\Override] now works on class constants and enum cases, and a missing parent constant is a compile-time fatal error.
  • Enums can define __debugInfo() to control var_dump() output.
  • JSON syntax errors report a line and column position, for example "Syntax error near location 1:25". JsonException carries the same detail.
  • Streams can throw a StreamException when configured with StreamErrorMode::Exception. The old warning behavior remains the default.
  • fclose(), file_put_contents() and copy() return false when a flush or close fails.
  • A URI builder complements the parsing extension from PHP 8.5, with Uri\Rfc3986\UriBuilder and Uri\WhatWg\UrlBuilder.

The Io\Poll API wraps epoll on Linux and kqueue on macOS behind one object interface, with no extension needed. It targets event-loop libraries such as ReactPHP, Amp and Revolt, which currently fall back to stream_select() or need an extension.

Performance

  • array_map() with a first-class callable or a partial is compiled into a foreach loop.
  • printf() calls that use only %s and %d are compiled into string interpolation.
  • Static closures that capture nothing are created once and reused.
  • json_encode() is faster, as are array_sum(), str_pad() and several other functions.
  • The JIT now works on ZTS builds on Apple Silicon, and ZTS builds in general are faster. FrankenPHP uses ZTS builds.

The RFC for closure caching measured about 3% on a real application template. That figure includes a second optimization that was not merged, so it is not a reliable estimate for the release.

Upgrade risks and deprecations

Session defaults are stricter: session.use_strict_mode is 1, session.cookie_httponly is 1, and session.cookie_samesite is "Lax". Applications that read the session cookie from JavaScript or rely on it in cross-site POST requests will break. Several functions now throw a ValueError where they used to warn. Examples are a NUL byte in a path passed to file_exists() and a negative value passed to sleep(). preg_grep() returns false on a PCRE error. trim(), ltrim() and rtrim() now strip form feed characters by default. SplFileObject changes how next(), fgets() and current() share the current line. A hard out-of-memory condition now exits with code 134 instead of 1.

Deprecations aim at PHP 9. They include is_integer(), is_long(), is_double(), doubleval() and spl_object_hash(), along with returning a value from a constructor and returning from a finally block. The names let and is become reserved for classes, functions and constants, and the mb_ereg*() functions are deprecated because the Oniguruma library has no maintainer. The full list of breaking changes is in section 1 of the UPGRADING file in php-src. Developers who want to test early can build the PHP-8.6 branch from the php-src repository.