PHP 8.6 está previsto para el 19 de noviembre de 2026. El conjunto de funciones quedó congelado en la beta 3, y la versión es más pequeña que la 8.5: sin JIT, sin property hooks, sin nuevo sistema de tipos. Un análisis del autor drapic88 sostiene que la actualización lleva una tarde, pero que el trabajo real está en las deprecaciones, porque la mayoría se convertirán en errores fatales en PHP 9.0.

La función estrella es la aplicación parcial de funciones, aprobada por 33 votos a 0. Llamar a una función con marcadores devuelve una closure que espera el resto: ? representa exactamente un argumento, ... recoge todos los parámetros restantes, y los marcadores con nombre permiten reordenar la firma de la closure resultante. Esto complementa al operador pipe de PHP 8.5: en lugar de envolver cada paso en fn($s) => ..., un pipeline se escribe trim(?) |> str_replace(' ', '-', ?) |> mb_substr(?, 0, 50). Dos trampas importantes. Los argumentos de un parcial se evalúan al crearlo, no al llamarlo, a diferencia de las funciones flecha. Y la PFA no funciona con new, con __get/__set ni con funciones que leen el scope llamante como compact(), extract() y func_get_arg(). Un ? posicional tras un argumento con nombre es error fatal. A diferencia de los first-class callables de la 8.1, un parcial solo conserva los atributos #[\NoDiscard] y #[\SensitiveParameter].

La segunda novedad es final readonly class Time\Duration, aprobada por 35 a 1. Corrige dos décadas de unidades de timeout incoherentes entre sleep(), usleep(), stream_set_timeout(), cURL y los clientes Redis. Las factorías cubren fromNanoseconds(), fromMicroseconds(), fromMilliseconds(), fromSeconds(int $s, int $ns = 0), fromMinutes(), fromHours() y fromIso8601DurationString(). Las instancias ofrecen add(), sub(), multiplyBy(int), divideBy(int), negate(), absolute(), un compare() estático y las propiedades readonly $seconds, $nanoseconds y $negative. No hay meses ni años, deliberadamente; la aritmética de calendario sigue siendo tarea de DateInterval. El primer consumidor del núcleo es IO\Poll\Context::wait(?Time\Duration $timeout). Qué funciones existentes aceptarán Duration en la versión final seguía en movimiento durante el desarrollo.

PHP 8.6 también incluye una API de bajo nivel para multiplexación de E/S: IO\Poll\Context, StreamPollHandle y Event::Read. El código de aplicación apenas la tocará, pero ReactPHP, Amp, Swoole y los runtimes estilo FrankenPHP reimplementan el polling cada uno por su cuenta. Una primitiva en el núcleo podría unificar el PHP asíncrono a largo plazo.

Entre los añadidos menores: clamp(), que también funciona con cadenas; valores por defecto para propiedades readonly; #[\Override] para constantes de clase; un enum SortDirection integrado con Ascending y Descending; __debugInfo() en enums; comentarios de documentación en parámetros; mensajes de error que muestran argumentos y la posición del fallo de json_decode(); Reflection isReadable() e isWriteable() sensibles al scope; optimizaciones de closures; los modificadores de endianness < y > en pack()/unpack() para flotantes y enteros con signo; además de grapheme_strrev(), mysqli_quote_string(), Locale::getDisplayKeyword(), IntlNumberRangeFormatter, reanudación de sesión TLS y early data 0-RTT para streams, Pdo\Pgsql::ATTR_CHUNK_SIZE, gmp_powm_sec(), gmp_prevprime(), finfo_file() sobre flujos remotos y clases constructoras de URI.

Los rechazos también informan: la asignación por pipe (|>=), la construcción let (aunque let queda reservada), los generics con borrado de cotas, __exists(), varias funciones auxiliares de arrays y la reasignación de propiedades readonly promovidas fueron declinados. Deprecar list() empató 23 a 23. El autoloading de funciones y BackedEnum::values() perdieron el tren; ReflectionAttribute::getCurrent() seguía en votación en la beta.

Para la migración, el artículo recomienda un orden. Primero, ejecutar la suite de tests con error_reporting=E_ALL y display_errors=1, porque un CI que silencia E_DEPRECATED oculta la carga real. Segundo, renombrados mecánicos: is_double() a is_float(), is_integer()/is_long() a is_int(), doubleval() a floatval(), spl_object_hash() a spl_object_id(), spl_classes() a reflexión, strcoll() y SORT_LOCALE_STRING al Collator de intl, metaphone() a soundex(), y se elimina el flag $case_insensitive de define(). Tercero, dejar de pasar objetos a array_walk(), array_walk_recursive(), deflate_init(), inflate_init(), mb_convert_variables() y http_build_query(); la solución suele ser get_object_vars($obj). También se deprecan is_a() e is_subclass_of() con cadena como primer argumento, los métodos CSV de SplFileObject y las implementaciones incompletas de session_set_save_handler(). Cuarto, liberar identificadores: readonly como nombre de función, is y let como identificadores, namespace como constante de clase y _ como constante quedan deprecados, con is reservado para el futuro pattern matching. Quinto, la familia mb_ereg* se depreca en 8.6 y se elimina en 9.0 porque Oniguruma dejó de recibir mantenimiento upstream el 24 de abril de 2025; migrar a preg_* con el modificador u exige presupuesto real, como muestra un issue abierto de Drupal. Sexto, cambios silenciosos: los valores INI de sesión pasan a session.use_strict_mode=1, session.cookie_httponly=1 y session.cookie_samesite="Lax"; devolver valores desde __construct(), __destruct() o finally queda deprecado; trim() y afines ahora eliminan el form feed; muchas funciones lanzan excepciones en lugar de avisar; preg_grep() devuelve false en caso de error; ?? y empty() ya no disparan __get() tras la materialización por __isset().

El autor estima que una aplicación Laravel o Symfony bien testada en 8.4+ necesita de medio día a un día, sobre todo esperando dependencias, con el set PHP_86 de Rector, PHPStan o Psalm y composer why haciendo la mayor parte. Un código heredado con mb_ereg es otra conversación.