2 de septiembre de 2026

PHP 8.6: qué se rompe de verdad al actualizar

Foto de Marco Orta Marco Orta | 11 min de lectura
Compartir
Ilustración 3D del elefante de PHP junto a un escudo agrietado, que representa los defaults de seguridad que cambian en PHP 8.6
Tabla de Contenidos

    PHP 8.6 sale el 19 de noviembre de 2026, y lo que más probablemente te va a tumbar producción no es ninguna función eliminada — es que cambian los defaults de tres directivas de sesión. session.use_strict_mode, session.cookie_httponly y session.cookie_samesite cambian de valor, y el modo de falla es silencioso: nada lanza excepción, tus usuarios simplemente dejan de mantener la sesión iniciada en peticiones cross-site.

    Todo lo demás son deprecaciones, es decir, avisos y no errores fatales. Pero hay cuatro cambios de comportamiento genuinos escondidos entre ellas, y dos de esos cambian el resultado sin decir una palabra.

    Esta es la lista de rupturas. Si buscas las novedades — Partial Function Application, clamp(), el enum SortDirection — están en todas las novedades de PHP 8.6.

    Qué tan cerrada está esta lista

    Vale la pena aclararlo, porque los artículos de “qué rompe PHP 8.6” empezaron a publicarse antes de que hubiera algo que reportar.

    El calendario es público: la alpha 1 salió el 2 de julio, la beta 1 con el soft feature freeze llegó el 13 de agosto, el hard feature freeze es el 22 de septiembre, la RC1 llega el 24 de septiembre y la RC4 el 5 de noviembre, y la disponibilidad general es el 19 de noviembre de 2026.

    Lo que eso significa para este artículo:

    • Las deprecaciones ya están cerradas. Vienen de RFCs que ya se votaron, y una RFC votada no se desvota.
    • La lista todavía puede crecer. Entre hoy y el hard freeze pueden entrar cambios aprobados por los release managers, y algunos bugfixes posteriores añaden notas de actualización.
    • Todavía no hay página oficial de migración. php.net/manual/es/migration86.php no existe al momento de escribir esto — la fuente de verdad hoy es el archivo UPGRADING en la rama master de php-src, de donde sale todo lo de abajo.

    Voy a actualizar este artículo después del hard freeze y la RC1 del 24 de septiembre, cuando la lista quede prácticamente cerrada.

    La que sí va a tumbar producción

    Los defaults de seguridad de sesión cambian

    Tres valores de php.ini cambian:

    DirectivaAntesEn PHP 8.6
    session.use_strict_mode01
    session.cookie_httponly01
    session.cookie_samesite"""Lax"

    Cada uno por separado es el default correcto. Juntos son el cambio más disruptivo de la versión, porque ninguno de los tres lanza un error. Tu aplicación sigue corriendo y algunos usuarios simplemente dejan de estar logueados.

    cookie_samesite = "Lax" es el que muerde. Una cookie Lax no viaja en peticiones POST cross-site. Si algo le hace POST a tu aplicación desde otro origen — una pasarela de pago que regresa al usuario por POST, el callback de un proveedor de identidad, un formulario embebido, un webhook que depende de la sesión — esa petición ahora llega sin sesión. El usuario aparece en la pantalla de login sin ninguna razón visible.

    cookie_httponly = 1 significa que JavaScript ya no puede leer la cookie de sesión con document.cookie. Es lo correcto, y rompe cualquier código de front-end que leyera el ID de sesión directamente — algunos snippets viejos de analítica y auth por AJAX hecho a mano hacen justo eso.

    use_strict_mode = 1 hace que PHP rechace IDs de sesión que él mismo no generó, que es la corrección real contra la fijación de sesión (session fixation). Rompe flujos que meten un ID de sesión desde afuera.

    Si necesitas el comportamiento anterior de forma temporal, fíjalo explícitamente en vez de depender del default previo:

    ; solo como parche mientras arreglas la causa real
    session.cookie_samesite = "None"
    session.cookie_secure = 1   ; obligatorio siempre que uses SameSite=None
    

    Ojo: los navegadores rechazan SameSite=None sin Secure, así que esto no es un revert de una línea.

    El consejo honesto: no lo reviertas. Fija estas tres directivas explícitamente en tu php.ini hoy mismo, en PHP 8.5, y averigua qué se rompe mientras todavía controlas cuándo. Eso convierte una sorpresa del día del lanzamiento en una tarde de martes cualquiera.

    Los cambios de comportamiento que no hacen ruido

    trim() ahora elimina el form feed

    trim(), ltrim() y rtrim() agregan \f (form feed, 0x0C) a su lista de caracteres por defecto. Era el único carácter de espacio en blanco ASCII que no eliminaban.

    trim("hola\f");
    // PHP 8.5 → "hola\f"
    // PHP 8.6 → "hola"
    

    Casi siempre es lo que querías. Lo listo aquí porque cambia la salida en silencio, y si tienes pruebas que comparan cadenas exactas al parsear archivos de ancho fijo, streams de impresión viejos o cualquier cosa que arrastre form feeds, van a empezar a fallar sin causa obvia.

    Los NUL bytes ahora lanzan en vez de tolerarse

    Un grupo grande de funciones ahora lanza ValueError cuando reciben una cadena con un byte NUL, en vez de truncar o comportarse de forma impredecible. La lista incluye getenv(), putenv(), parse_str(), setlocale(), dl(), openlog(), proc_open() (el argumento $cwd), y cerca de veinte funciones de sistema de archivos — file_exists(), is_file(), filesize(), stat() y sus parientes.

    Es un endurecimiento de seguridad: los NUL bytes en esos argumentos han sido históricamente un vector de path traversal. El impacto práctico es que el código que pasa entrada de usuario sin sanear a validaciones de archivos ahora recibe una excepción ruidosa en vez de un silencio sin sentido:

    // PHP 8.5: devuelve false, sin ninguna señal
    // PHP 8.6: lanza ValueError
    file_exists($_GET['path']);   // cuando $_GET['path'] contiene "\0"
    

    Si tu aplicación hace esto, la excepción te está avisando de un bug que ya existía.

    unpack() reinterpreta < y >

    unpack() ahora trata < y > después de un código de formato como modificadores de endianness, no como parte del nombre. Esto cambia cómo se parsean cadenas de formato que ya tenías:

    unpack("s<valor", $data);
    // PHP 8.5 → clave "<valor"
    // PHP 8.6 → short little-endian, clave "valor"
    
    unpack("C>nombre", $data);
    // PHP 8.6 → lanza excepción (C no tiene endianness)
    

    Es un caso puntual, pero si parseas formatos binarios es una ruptura dura, no una deprecación. Busca unpack( en tu código y revisa si algún formato tiene < o > dentro de un nombre.

    SessionHandlerInterface pide dos métodos más

    Los manejadores de sesión personalizados que no implementan create_sid() ni validateId() ahora emiten deprecaciones. Si escribiste a mano un manejador de sesiones en base de datos o Redis, lo más probable es que implemente los seis métodos originales y no estos dos — y validateId() es justo lo que hace funcionar de verdad a use_strict_mode, así que este punto se conecta directo con el cambio de arriba.

    Las deprecaciones

    Ninguna de estas es fatal en 8.6. Emiten E_DEPRECATED, y son la lista de eliminación para PHP 9.

    return dentro de finally. Regresar un valor desde un bloque finally descarta cualquier valor de retorno o excepción del try, que casi siempre está tapando un bug. Ahora está deprecado.

    function f() {
        try { return 1; }
        finally { return 2; }   // deprecado — gana en silencio, regresa 2
    }
    

    Regresar un valor desde un constructor. Ya está cubierto con detalle en el post de novedades, pero la versión corta: es deprecación en tiempo de compilación, la ves aunque el código nunca se ejecute, y pasó la votación 39 a 0.

    array_filter() lanza excepción con un $mode inválido. Antes ignoraba en silencio un modo incorrecto; ahora lanza ValueError. Es una mejora estricta, pero puede tumbar código que llevaba años pasando una constante equivocada sin que nadie se diera cuenta.

    php://filter limita los filtros encadenados. Endurecimiento contra una técnica conocida de explotación por cadenas de filtros. Solo te toca si construyes cadenas de filtros a propósito.

    Todo mbregex. Cada función mb_ereg* queda deprecada, porque Oniguruma, la librería detrás de ellas, dejó de tener mantenimiento. Es la deprecación más grande de la versión en superficie de código. La migración es a PCRE — preg_match() y compañía, con el modificador u para Unicode. El post de novedades trae el detalle completo de las 14 funciones y el checklist de migración; aquí basta con saber que no es opcional para siempre: se elimina en PHP 9.

    Alias de verificación y conversión de tipos. is_double(), is_long(), is_integer() y doubleval() quedan deprecadas a favor de is_float(), is_int() y floatval(). También se deprecan metaphone(), strcoll(), spl_object_hash(), spl_classes() y la bandera de ordenamiento SORT_LOCALE_STRING.

    spl_object_hash() es la que vale la pena marcar: reemplázala por spl_object_id(), que casi seguro es lo que en realidad querías.

    Métodos de CSV en SPL y de ArrayIterator. SplFileObject::fgetcsv(), fputcsv(), setCsvControl() y getCsvControl() quedan deprecados, junto con diez métodos de ArrayIterator: getFlags(), setFlags(), asort(), ksort(), uasort(), uksort(), natsort(), natcasesort(), serialize() y unserialize().

    Argumentos de objeto donde nunca tuvieron sentido. array_walk(), mb_convert_variables() y los filtros de stream de zlib y bz2 ahora deprecan argumentos tipo objeto.

    Cabos sueltos de mysqli. mysqli_get_charset() y mysqli_stmt_init() quedan deprecadas. La opción classmap de SOAP ahora rechaza claves enteras.

    Qué hacer en realidad, en orden

    1. Fija las tres directivas de sesión explícitamente en PHP 8.5, hoy. Es todo el riesgo de la versión concentrado en un solo cambio, y es el único que puedes probar antes de que 8.6 exista siquiera. Revisa cada POST cross-site que le llegue a tu aplicación.
    2. Activa el reporte de deprecaciones y corre tu suite de tests. error_reporting(E_ALL) con un log que de verdad revises. La mayor parte de esta versión aparece ahí.
    3. Busca los nombres específicos. mb_ereg, is_double, is_long, is_integer, doubleval, spl_object_hash, metaphone, strcoll, unpack(. Es una pasada de veinte minutos y cubre casi toda la lista de deprecaciones.
    4. Revisa tus manejadores de sesión personalizados en busca de create_sid() y validateId().
    5. No toques trim() a menos que falle alguna prueba. Si falla una, aprendiste algo sobre tus datos de entrada.

    Lo que NO se rompe

    Vale la pena decirlo, porque la ansiedad de actualizar rellena huecos que no existen. PHP 8.6 no elimina nada que estuviera deprecado en 8.x — las eliminaciones llegan hasta PHP 9. Tus propiedades tipadas, enums, clases readonly, fibers y atributos siguen intactos. Si tu aplicación corre limpia en 8.5 con las deprecaciones silenciadas, el peor caso realista para 8.6 es un log ruidoso más el cambio de sesión.

    Ese cambio de sesión, eso sí, no es un detalle menor. Es la razón por la que existe este artículo.

    Preguntas frecuentes

    ¿Cuándo se libera la versión estable de PHP 8.6? El 19 de noviembre de 2026. El hard feature freeze es el 22 de septiembre y las release candidates corren del 24 de septiembre al 5 de noviembre.

    ¿Cuál es el cambio más peligroso de PHP 8.6? Los defaults de sesión: session.use_strict_mode y session.cookie_httponly pasan a 1, y session.cookie_samesite pasa a "Lax". Fallan en silencio — los usuarios dejan de mantener sesión en POSTs cross-site en vez de recibir un error.

    ¿PHP 8.6 elimina funciones existentes? No. Deprecia muchísimo — todo mbregex, varios alias de verificación de tipos, métodos CSV de SPL — pero las eliminaciones están programadas para PHP 9. Las deprecaciones emiten avisos, no errores fatales.

    ¿De verdad va a desaparecer mb_ereg? Está deprecada en 8.6 porque Oniguruma, la librería detrás de ella, dejó de tener mantenimiento, pero todavía funciona. Migra a PCRE (preg_match y compañía con el modificador u). Los patrones no siempre se traducen uno a uno, así que conviene probar en vez de hacer un find-and-replace.

    Para seguir

    Compartir

    Buscar

    Etiquetas

    PHP Laravel IA Tutorial JavaScript Migración Desarrollo Web Buenas Prácticas Upgrade Seguridad Laravel 13 Backend SEO Claude Herramientas