PHP 8.6.0 RC3 was tagged on 8 October 2026. The release schedule targets general availability for 19 November 2026, with RC4 and RC5 still planned. Details may change before the final release. This assessment uses the UPGRADING and NEWS files from the RC3 tag, together with merged implementation status, and excludes material published after 11 October 2026.

The most visible addition is partial function application, accepted unanimously and merged in alpha3. A call containing ? for exactly one missing argument or ... for zero or more remaining arguments produces a Closure. The familiar strlen(...) syntax is conceptually the same feature with no bound arguments. Partial application can make array_map() calls more direct, and UPGRADING records a compiled loop for array_map() when it receives a first-class callable or a partial application.

PHP 8.5 introduced the pipe operator, whose right side must accept one argument. Partial application removes the need for an arrow function when a piped call needs several fixed arguments. A single-placeholder partial application placed directly after |> can be compiled as a direct call without creating a Closure. Forms with two or more placeholders still create one.

Bound arguments are evaluated when the partial application is created. An arrow function evaluates its body each time it runs. This difference matters when a bound value comes from a changing expression such as date('H:i:s'). Every ? creates a required parameter, even when the original parameter has a default. Arguments supplied through ... retain their optionality, and untouched optional parameters keep their defaults. Older RFC examples that show ? inheriting defaults no longer match RC3.

Readonly properties can now declare default values, including in readonly classes. The default counts as the initial assignment, so a constructor cannot assign that property again. A promoted constructor parameter with a default suits configurable values. __clone() and clone-with may initialise the property once more. The new clamp(mixed $value, mixed $min, mixed $max): mixed function uses normal PHP comparisons and works with values such as strings and DateTimeInterface objects. It throws ValueError when the lower bound exceeds the upper bound or when a bound is NAN.

Time\Duration is a final readonly class for exact nanosecond-precision durations. Named factories create instances, including from seconds, minutes and ISO 8601 duration strings. The class supports comparison and add() or sub() operations. Operators + and - are unavailable. There is no integration with DateTime or DateInterval, and functions such as sleep() do not accept Duration. Its current value lies in typed timeout configuration and in the polling API.

Session defaults carry the largest immediate migration risk. session.use_strict_mode changes from 0 to 1, session.cookie_httponly from 0 to 1, and session.cookie_samesite from an unset value to Lax. PHP will reject session IDs it did not create, JavaScript will lose access through document.cookie, and cookies will no longer accompany cross-site POST requests. Custom session handlers, shared sessions across subdomains or services, SAML POST bindings and payment callbacks need explicit testing. Applications that rely on the old values should set them explicitly.

Argument validation becomes stricter across the runtime. Invalid modes and flags now affect calls such as array_filter(), array_change_key_case(), pathinfo() and scandir(). Out-of-range values can make number_format(), sleep() and usleep() throw ValueError. NUL bytes in getenv(), putenv(), parse_str(), setlocale() and filename functions such as file_exists(), is_file(), filesize() and stat() now raise exceptions instead of being truncated or quietly rejected. trim(), ltrim() and rtrim() also remove form feed by default. base_convert(), bindec(), hexdec() and octdec() issue a notice after imprecise conversion, and preg_grep() returns false on an execution error. GMP, Intl, GD, Sodium and other extensions apply similar validation changes.

The new error_include_args INI setting adds function arguments to error messages and is disabled by default. Values marked with #[\SensitiveParameter] are redacted, but other arguments can reach logs. Hard out-of-memory failures now call abort(), producing exit code 134 and potentially a core dump. Supervisors and alerts that inspect exit codes need review. A by-reference foreach can revisit elements after array compaction, and ?? or empty() on magic properties no longer calls __get() after __isset() has written the property.

Most deprecations are preparation for PHP 9. Scalar aliases is_integer(), is_long(), is_double() and doubleval() have canonical replacements, and spl_object_hash() gives way to spl_object_id(). The latter returns an integer, which matters for serialized data and cache keys. The third argument of define(), spl_classes() and mysqli_get_charset() is deprecated. Returning from finally, returning values from __construct() or __destruct(), string calls to is_a() or is_subclass_of() when allow_string is false, and passing objects to array_walk() or http_build_query() require an intent review.

Several ArrayIterator sorting, flag and serialization methods are deprecated. The affected SPL surface includes asort(), ksort(), uasort(), setFlags() and serialize(). SplFileObject CSV methods such as fgetcsv(), fputcsv(), setCsvControl() and getCsvControl() also need attention. Custom session handlers without create_sid() and validateId() are included. mbregex is the strategic item because Oniguruma is unmaintained. The mb_ereg(), mb_eregi(), mb_ereg_replace(), mb_split() and mb_regex_encoding() family is deprecated, with removal planned for PHP 9.0. Migration to PCRE with the u modifier is common, but dialect differences require tests. The proposed list() and _() gettext alias deprecations failed their votes.

Io\Poll adds an object-oriented event-loop primitive with Context, automatic backend selection for epoll, kqueue, poll, event ports and WSAPoll, typed watchers for stream handles, and wait() with a Time\Duration timeout. The RFC page still displays an older integer-based wait() signature. The API targets async runtimes, socket servers and process managers. Stream contexts can opt into exception-based errors through StreamErrorMode::Exception, giving HTTP clients, storage adapters and import jobs a catchable StreamException with collected errors. The default remains warnings.

The URI extension receives fluent Uri\Rfc3986\UriBuilder and Uri\WhatWg\UrlBuilder classes, host and URI type inspection, and a WHATWG percent-encoding function. These changes target redirects, signed URLs and SSRF checks. Performance work includes stateless closure caching, compiled printf() and array_map() calls, a faster TAILCALL VM on Windows with Clang, faster JSON encoding, and faster array and string functions. The expected benefit is incremental and workload-dependent.

True Async is absent because its RFC was cancelled. Automatic static closure inference was not merged; only stateless closure caching from the closure optimisation RFC landed, with explicit static closures receiving the full benefit. SortDirection adds Ascending and Descending, but existing sorting functions still use SORT_ASC and SORT_DESC, and scandir() constants remain unchanged. The new reflection method is ReflectionProperty::isWritable().

The practical upgrade path starts with RC testing in CI, visible E_DEPRECATED logging and dependency checks for PHP 8.6 support. Teams should compare effective session settings and exercise login, SSO and payment callbacks end to end. They should trace user input into filesystem, environment and locale functions, then inventory mbregex, return in finally and deprecated SPL methods. Production-like staging traffic should be monitored for errors and exit codes before rollout. Codebases several PHP versions behind need a broader legacy migration plan.