The Daily Commit · Section Edition Front Page PHP AI Dev EN DE FR ES

The Php Times

News — Ecosystem

PHP 8.6 Overview: Partial Application, Time\Duration and 30 Deprecations Ahead of PHP 9.0


PHP 8.6 arrives 19 November 2026 with partial function application, the Time\Duration class and roughly thirty deprecations that map out PHP 9.0.

MEDIUM · LARAVEL, September 9, 2026 curated by Giselle

A new overview covers the features, the migration work and why enabling E_DEPRECATED now pays off later.

PHP 8.6 is scheduled for 19 November 2026. The feature set is frozen as of beta 3, and the release is smaller than 8.5: no JIT, no property hooks, no new type system. An overview by author drapic88 argues the upgrade itself takes an afternoon, while the deprecation cleanup is the real work, because most of it becomes hard errors in PHP 9.0.

The headline feature is partial function application, accepted 33 to 0. Calling a function with placeholders returns a closure waiting for the rest: ? stands for exactly one argument, ... collects all remaining parameters, and named placeholders let you reorder the resulting closure's signature. That pairs directly with the pipe operator from PHP 8.5: instead of wrapping every step in fn($s) => ..., a pipeline reads as trim(?) |> str_replace(' ', '-', ?) |> mb_substr(?, 0, 50). Two caveats matter. Arguments in a partial are evaluated when the partial is created, not when it is called, unlike arrow functions. And PFA does not work on new, on __get/__set, or on scope-reading functions like compact(), extract() and func_get_arg(). A positional ? after a named argument is a fatal error. Unlike first-class callables from 8.1, a partial preserves only the attributes #[\NoDiscard] and #[\SensitiveParameter].

The second addition is final readonly class Time\Duration, accepted 35 to 1. It fixes two decades of inconsistent timeout units across sleep(), usleep(), stream_set_timeout(), cURL and Redis clients. Factories include fromNanoseconds(), fromMicroseconds(), fromMilliseconds(), fromSeconds(int $s, int $ns = 0), fromMinutes(), fromHours() and fromIso8601DurationString(). Instances expose add(), sub(), multiplyBy(int), divideBy(int), negate(), absolute(), a static compare() and readonly $seconds, $nanoseconds and $negative properties. There are deliberately no months or years; calendar arithmetic stays with DateInterval. The first core consumer is IO\Poll\Context::wait(?Time\Duration $timeout). Which existing core functions accept Duration in the shipped build was still moving during development.

PHP 8.6 also ships a low-level I/O multiplexing API: IO\Poll\Context, StreamPollHandle and Event::Read. Application code will rarely touch it, but ReactPHP, Amp, Swoole and FrankenPHP-style runtimes currently each reimplement polling, so a core primitive could eventually unify async PHP.

Smaller additions: clamp(), which also works on strings; default values for readonly properties; #[\Override] for class constants; a built-in SortDirection enum with Ascending and Descending; __debugInfo() on enums; doc comments on parameters; error messages that display function arguments and json_decode() failure positions; scope-aware Reflection isReadable() and isWriteable(); closure optimizations; < and > endianness modifiers in pack()/unpack() for floats and signed integers; plus grapheme_strrev(), mysqli_quote_string(), Locale::getDisplayKeyword(), IntlNumberRangeFormatter, TLS session resumption and 0-RTT early data for streams, Pdo\Pgsql::ATTR_CHUNK_SIZE, gmp_powm_sec(), gmp_prevprime(), finfo_file() over remote streams and URI builder classes.

The rejections are equally telling: pipe assignment (|>=), the let construct (though let is now reserved), bound-erased generics, __exists(), several array helper functions and reassigning promoted readonly properties were all declined. Deprecating list() tied 23 to 23 and failed. Function autoloading and BackedEnum::values() missed the train; ReflectionAttribute::getCurrent() was still in voting at beta.

For migration, the article recommends a set order. First, run the test suite with error_reporting=E_ALL and display_errors=1, since CI that suppresses E_DEPRECATED hides the actual workload. Second, mechanical renames: is_double() to is_float(), is_integer()/is_long() to is_int(), doubleval() to floatval(), spl_object_hash() to spl_object_id(), spl_classes() to reflection, strcoll() and SORT_LOCALE_STRING to the intl Collator, metaphone() to soundex(), and define()'s $case_insensitive flag away. Third, stop passing objects to array_walk(), array_walk_recursive(), deflate_init(), inflate_init(), mb_convert_variables() and http_build_query(); the fix is usually get_object_vars($obj). Also deprecated: is_a() and is_subclass_of() with a string first argument, SplFileObject's CSV methods and incomplete session_set_save_handler() implementations. Fourth, free up identifiers: readonly as a function name, is and let as identifiers, namespace as a class constant name and _ as a constant are all deprecated, with is reserved for future pattern matching. Fifth, the mb_ereg* family is deprecated in 8.6 and removed in 9.0 because Oniguruma stopped receiving upstream maintenance on 24 April 2025; port to preg_* with the u modifier and budget real time, as Drupal's open issue on this shows. Sixth, silent behaviour changes: session INI defaults become session.use_strict_mode=1, session.cookie_httponly=1 and session.cookie_samesite="Lax"; returning from __construct(), __destruct() or finally is deprecated; trim() and friends now strip form feed; many functions now throw instead of warn; preg_grep() returns false on error; and ?? and empty() no longer trigger __get() after __isset().

The author estimates a well-tested Laravel or Symfony app on 8.4+ needs half a day to a day, mostly waiting on dependencies, with Rector's PHP_86 set, PHPStan or Psalm and composer why doing most of the lifting. A legacy codebase using mb_ereg is a different conversation.

Read the original source ↗

Rate this article: 0

Readers’ Forum

No contributions yet — open the debate.

← Ecosystem — Page B1

"All the Code That's Fit to Ship" · The Daily Commit · Screen edition · Imprint · Privacy Policy