Cambios retroincompatibles

Cambios en el manejo de errores y excepciones

Muchos errores fatales y errores fatales recuperables han sido convertidos en excepciones en PHP 7. Estas excepciones de error heredan de la clase Error, la cual implementa en sí la interfaz Throwable, (la interfaz base que heredan todas las excepciones).

Esto significa que los manejadores de errores personalizados podrían no desencadenarse debido a que podrían lanzarse excepciones en su lugar (causando nuevos errores fatales para excepciones Error no capturadas).

Se puede encontrar una descripción más completa de cómo operan los errores en PHP 7 en la página de errores de PHP 7. Esta guía de migración simplemente enumerará los cambios que afectan a la retrocompatiblidad.

Ya no se garantiza que set_exception_handler() reciba objetos Exception

El código que implemente un manejador de excepciones registrado con set_exception_handler() empleando una declaración de tipo de Exception causará un error fatal cuando sea lance un objeto Error.

Si el manejador necesita funcionar tanto en PHP 5 como en PHP 7, se debería eliminar la declaración de tipo del manejador, mientras que el código que sea migrado para que funcione exclusivamente en PHP 7 puede simplemente reemplazar la declaración de tipo Exception con Throwable en su lugar.

<?php
// Código de la era de PHP 5 que no funcionará.
function handler(Exception $e) { ... }
set_exception_handler('handler');

// Compatible con PHP 5 y 7.
function handler($e) { ... }

// Solo PHP 7.
function handler(Throwable $e) { ... }
?>

Los constructores internos siempre lanzan excepciones en caso de fallo

Previamente, algunas clases internas devolvían NULL o un objeto inutilizable cuando el constructor fallaba. Ahora, todas las clases internas lanzan una Exception en tal caso, de la misma forma que las clases de usuario ya deben de hacerlo.

Los errores de análisis lanzan un objeto ParseError

Los errores de análisis ahora lanzan un objeto ParseError. El manejo de errores para eval() ahora debería incluir un bloque catch que permita manejar dicho error.

Cambios en la severidad de avisos E_STRICT

Todos los avisos de E_STRICT han sido reclasificados a otros niveles. Se mantiene la consntante E_STRICT, por lo que llamadas como error_reporting(E_ALL|E_STRICT) no ocasionarán ningún error.

Cambios en la severidad de avisos de E_STRICT
Situación Nuevo nivel/comportamiento
Indexación mediante un recurso E_NOTICE
Métodos estáticos abstractos Aviso eliminado, no genera un error
'Redefinición' de un constructor Aviso eliminado, no genera un error
Discordancia de la signatura durante la herencia E_WARNING
Misma propiedad (compatible) en dos rasgos empleados Aviso eliminado, no genera un error
Acceso a una propiedad estática de forma no estática E_NOTICE
Solamente las variables deberían ser asignadas por referencia E_NOTICE
Solamente las variables deberían ser pasadas por referencia E_NOTICE
Llamada a métodos no estáticos de forma estática E_DEPRECATED

Cambios en el manejo de variables

PHP 7 ahora emplea un árbol sintáctico abstracto al analizar ficheros. Esto ha permitido muchas mejoras en el lenguaje que antes eran imposibles debido a las limitaciones del analizador empleado en versiones previas de PHP, aunque ha resultado en la eliminación de unos pocos casos especiales por razones de consistencia, dando así lugar a roturas de retrocompatibilidad. Dichos casos están detallados a continuación.

Cambios en el manejo de variables, propiedades y métodos indirectos

El acceso indirecto a variables, propiedades y métodos ahora se evalúa estrictamente de izquierda a derecha, en contraste a la mezcla anterior de casos especiales. La tabla de abajo muestra los cambios en el orden de evaluación.

Antigua y nueva evaluación de expresiones indirectas
Expresión Interpretación de PHP 5 Interpretación de PHP 7
$$foo['bar']['baz'] ${$foo['bar']['baz']} ($$foo)['bar']['baz']
$foo->$bar['baz'] $foo->{$bar['baz']} ($foo->$bar)['baz']
$foo->$bar['baz']() $foo->{$bar['baz']}() ($foo->$bar)['baz']()
Foo::$bar['baz']() Foo::{$bar['baz']}() (Foo::$bar)['baz']()

El código que emplee el orden antiguo de evaluación de derecha a izquierda debe ser rescrito para utilizar explícitamente el orden de evaluación con llaves (véase la columna de en medio de arriba). Esto hará al código compatible con PHP 7.x y retrocompatible con PHP 5.x.

Cambios al manejo de list()

list() ya no asigna variables en orden inverso

list() ahora asigna valores a variables en el orden en el que estas se definen, en vez de en orden inverso. En general, esto solamente afecta al caso donde list() se emplea junto con el operador de array [], como se muestra a continuación:

<?php
list($a[], $a[], $a[]) = [123];
var_dump($a);
?>

Salida del ejemplo anterior en PHP 5:

array(3) {
  [0]=>
  int(3)
  [1]=>
  int(2)
  [2]=>
  int(1)
}

Salida del ejemplo anterior en PHP 7:

array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
}

En general, se recomienda no depender del orden en el que ocurren las asignaciones de list(), ya que este es un detalle de implementación que podría cambiar en el futuro.

Se han eliminado las asignaciones vacías de list()

Las construcciones de list() ya no pueden estar vacías. Lo siguiente ya no es válido:

<?php
list() = $a;
list(,,) = 
$a;
list(
$x, list(), $y) = $a;
?>
list() no puede desempaquetar strings

list() ya no puede desempaquetar variables de tipo string. Debería usarse str_split() en su lugar.

Cambio en el ordenamiento de arrays cuando los elementos se crean automáticamente durante las asignaciones por referencia

El orden de los elementos de un array ha cambiado cuando dichos elementos han sido creados automáticamente haciendo referencia a ellos en una asignación por referencia. Por ejemplo:

<?php
$array 
= [];
$array["a"] =& $array["b"];
$array["b"] = 1;
var_dump($array);
?>

Salida del ejemplo anterior en PHP 5:

array(2) {
  ["b"]=>
  &int(1)
  ["a"]=>
  &int(1)
}

Salida del ejemplo anterior en PHP 7:

array(2) {
  ["a"]=>
  &int(1)
  ["b"]=>
  &int(1)
}

global solamente acepta variables simples

Ya no se pueden utilizar variables variables con la palabra reservada global. Se puede emplear la sintaxis de llaves para emular el comportamiento anterior si fuera necesario:

<?php
function f() {
    
// Válido solamente en PHP 5.
    
global $$foo->bar;

    
// Válido en PHP 5 y 7.
    
global ${$foo->bar};
}
?>

Como principio general, se desaconseja el uso de global con algo que no sea una simple variable.

Los paréntesis alrededor de argumentos de una función ya no afectan al comportamiento

En PHP 5, el uso de paréntesis redundantes alrededor de un argumento de una función podía silenciar advertencias del estándar estricto cuando el argumento de dicha función era pasado por referencia. Estas advertencias ahora se emiten siempre.

<?php
function obtenerArray() {
    return [
123];
}

function 
arrayAlCuadrado(array &$a) {
    foreach (
$a as &$v) {
        
$v **= 2;
    }
}

// Genera una advertencia en PHP 7.
arrayAlCuadrado((obtenerArray()));
?>

El resultado del ejemplo sería:

Notice: Only variables should be passed by reference in /tmp/test.php on line 13

Cambios en foreach

Se han realizado cambios menores en el comportamiento de la estructura de control foreach, principalmente alrededor del manejo del puntero de arrays interno y la modificación del array en su recorrido.

foreach ya no cambia el puntero de arrays interno

Antes de PHP 7, el puntero de arrays interno era modificado mientras el array se recorría con foreach. Este ya no es el caso, como se muestra en el siguiente ejemplo:

<?php
$array 
= [012];
foreach (
$array as &$val) {
    
var_dump(current($array));
}
?>

Salida del ejemplo anterior en PHP 5:

int(1)
int(2)
bool(false)

Salida del ejemplo anterior en PHP 7:

int(0)
int(0)
int(0)

La iteración de foreach por valor opera sobre una copia del array

Al utilizar el modo predeterminado de iteración por valor, foreach ahora opera sobre una copia del array a recorrer en lugar del array en sí. Esto significa que los cambios realizados a un array durante una iteración no afectarán a los valores que se recorren.

Se ha mejorado el comportamiento de la iteración de foreach por referencia

Cuando se itera por referencia, foreach ahora realizará un trabajo mejor realizando un seguimiento de los cambios hechos en el array durante la iteración. Por ejemplo, al añadir valores a un array mientras se recorre dará como resultado también el recorrido de los valores añadidos:

<?php
$array 
= [0];
foreach (
$array as &$val) {
    
var_dump($val);
    
$array[1] = 1;
}
?>

Salida del ejemplo anterior en PHP 5:

int(0)

Salida del ejemplo anterior en PHP 7:

int(0)
int(1)

Iteración de objetos no Traversables

Recorrer un objeto no Traversable ahora tendrá el mismo comportamiento que recorrer arrays por referencia. Esto resulta en que la mejora del comportamiento al modificar un array durante la iteración se aplique también cuando se añaden propiedades a o se eleminan de un objeto.

Cambios en el manejo del tipo integer

Literales de octal inválidos

Previamente, los literales de octal que contenían números inválidos eran truncados silenciosamente (0128 era tomado como 012). Ahora, un literal de octal inválido causará un error de análisis.

Desplazamientos negativos de bits

Los desplazamientos de bit mediante números negativos ahora lanzan un ArithmeticError:

<?php
var_dump
(>> -1);
?>

Salida del ejemplo anterior en PHP 5:

int(0)

Salida del ejemplo anterior en PHP 7:

Fatal error: Uncaught ArithmeticError: Bit shift by negative number in /tmp/test.php:2
Stack trace:
#0 {main}
  thrown in /tmp/test.php on line 2

Desplazamientos de bits fuera de rango

Los desplazamientos de bits (en cualquier dirección) fuera del ancho de bit de un integer siempre resultarán en 0. Anteriormente, el comportamiento de dichos desplazamientos dependía de la arquitectura.

Cambios en la División por Cero

Anteriormente, cuando se empleaba 0 como divisor de los operadores de división (/) o módulo (%), se emitía un error de tipo E_WARNING y se devolvía false. Ahora, el operador de división devuelve un valor de tipo float así como +INF, -INF, o NAN tal como se especifica en el IEEE 754. El error E_WARNING del operador de módulo ha sido eliminado, lanzando ahora una excepción DivisionByZeroError.

<?php
var_dump
(3/0);
var_dump(0/0);
var_dump(0%0);
?>

Salida del ejemplo anterior en PHP 5:

Warning: Division by zero in %s on line %d
bool(false)

Warning: Division by zero in %s on line %d
bool(false)

Warning: Division by zero in %s on line %d
bool(false)

Salida del ejemplo anterior en PHP 7:

Warning: Division by zero in %s on line %d
float(INF)

Warning: Division by zero in %s on line %d
float(NAN)

PHP Fatal error:  Uncaught DivisionByZeroError: Modulo by zero in %s line %d

Cambios en el manejo del tipo string

Un string hexadecimal ya no se considera numérico

Los string que contienen números hexadecimales ya no se consideran numéricos. Por ejemplo:

<?php
var_dump
("0x123" == "291");
var_dump(is_numeric("0x123"));
var_dump("0xe" "0x1");
var_dump(substr("foo""0x1"));
?>

Salida del ejemplo anterior en PHP 5:

bool(true)
bool(true)
int(15)
string(2) "oo"

Salida del ejemplo anterior en PHP 7:

bool(false)
bool(false)
int(0)

Notice: A non well formed numeric value encountered in /tmp/test.php on line 5
string(3) "foo"

Se puede emplear filter_var() para comprobar si un string contiene un numero hexadecimal, y también para convertir un string de dicho tipo en un integer:

<?php
$str 
"0xffff";
$int filter_var($strFILTER_VALIDATE_INTFILTER_FLAG_ALLOW_HEX);
if (
false === $int) {
    throw new 
Exception("¡Número entero inválido!");
}
var_dump($int); // int(65535)
?>

\u{ puede ocasionar errores

Debido a la incorporación de la nueva sintaxis de escape de puntos de código de Unicode, los string que contienen un literal \u{ seguido de una secuencia inválida causarán un error fatal. Para evitarlo, se debería escapar la barra inicial.

Funciones eliminadas

call_user_method() y call_user_method_array()

Estas funciones estaban obsoletas en PHP 4.1.0 en favor de call_user_func() y call_user_func_array(). Se podría también considerar el uso de funciones variables y/o el operador ....

Todas las funciones ereg*

Se han eliminado todas las funciones de ereg. Se recomienda PCRE como alternativa.

Alias de mcrypt

Se ha eliminado la función obsoleta mcrypt_generic_end() en favor de mcrypt_generic_deinit().

También se han eliminado las funciones obsoletas mcrypt_ecb(), mcrypt_cbc(), mcrypt_cfb() y mcrypt_ofb() en favor del uso de mcrypt_decrypt() con la constante MCRYPT_MODE_* apropiada.

Todas las funciones de ext/mysql

Se han eliminado todas las funciones de ext/mysql. Para más detalles sobre elegir una API de MySQL diferente, Véase Elegir una API de MySQL.

Todas las funciones ext/mssql

Se han eliminado todas las funciones de ext/mssql. Para un listado de alternativas, veáse la Introudcción a MSSQL.

Alias de intl

Se han eliminado los alias obsoletos datefmt_set_timezone_id() y IntlDateFormatter::setTimeZoneID() en favor de datefmt_set_timezone() y IntlDateFormatter::setTimeZone(), respectivamente.

set_magic_quotes_runtime()

Se ha eliminado set_magic_quotes_runtime(), junto con su alias magic_quotes_runtime(). Estaban obsoletos en PHP 5.3.0, y de hecho se quedaron sin funcionalidad con la eliminación de las comillas mágicas en PHP 5.4.0.

set_socket_blocking()

Se ha eliminado el alias obsoleto set_socket_blocking() en favor de stream_set_blocking().

dl() en PHP-FPM

dl() ya no se puede utilizar en PHP-FPM. Permanece funcional en CLI y en SAPI embebidas.

Funciones de Type1 de GD

Se ha eliminado el soporte para fuentes Type1 de PostScript de la extensión GD, resultado en la eliminación de las siguientes funciones:

Se recomienda utilizar fuentes TrueType y sus asociadas en su lugar.

Directivas INI eliminadas

Características eliminadas

Las siguientes directivas INI han sido eliminadas, así como sus características asociadas:

xsl.security_prefs

Ha sido eliminada la directiva xsl.security_prefs. En su lugar, debería llamarse al método XsltProcessor::setSecurityPrefs() para controlar las preferencias de seguridad en función de cada procesador.

Otros cambios retroincompatibles

No se pueden asignar nuevos objetos por referencia

El resultado de la sentencia new ya no se puede asignar a una variable por referencia:

<?php
class {}
$c =& new C;
?>

Salida del ejemplo anterior en PHP 5:

Deprecated: Assigning the return value of new by reference is deprecated in /tmp/test.php on line 3

Salida del ejemplo anterior en PHP 7:

Parse error: syntax error, unexpected 'new' (T_NEW) in /tmp/test.php on line 3

Nombres de clase, interfaz y rasgo inválidos

Los siguientes nombres no se pueden emplear para nombrar a clases, interfaces o rasgos:

Tampoco deberían utilizarse los siguientes nombres. Aunque no generan un error en PHP 7.0, están reservados para un uso futuro, por lo que deberían considerarse obsoletos.

Eliminación de las etiquetas ASP y de script de PHP

Se ha eliminado el soporte para usar etiquetas ASP y de script para delimitar código de PHP. Las etiquetas afectadas son:

Etiquetas ASP y de script eliminadas
Etiqueta de apertura Etiqueta de cierre
<% %>
<%= %>
<script language="php"> </script>

Eliminación de llamadas desde contextos incompatibles

Ya obsoletas en PHP 5.6, las llamadas estáticas realizadas a un método no estático con un contexto incompatible resultarán ahora en la tenencia del método llamado de una variable $this indefinida y en la emisión de una advertencia de obsolescencia.

<?php
class {
    public function 
prueba() { var_dump($this); }
}

// Nota: NO extiende a A
class {
    public function 
llamadaAMétodoNoEstáticoDeA() { A::prueba(); }
}

(new 
B)->llamadaAMétodoNoEstáticoDeA();
?>

Salida del ejemplo anterior en PHP 5.6:

Deprecated: Non-static method A::prueba() should not be called statically, assuming $this from incompatible context in /tmp/test.php on line 8
object(B)#1 (0) {
}

Salida del ejemplo anterior en PHP 7:

Deprecated: Non-static method A::prueba() should not be called statically in /tmp/test.php on line 8

Notice: Undefined variable: this in /tmp/test.php on line 3
NULL

yield ahora es un operador asociativo derecho

El constructor yield ya no requiere paréntesis, ya que ha pasado a ser un operador asociativo derecho con precedencia entre print y =>. Esto puede resultar en un cambio de comportamiento:

<?php
echo yield -1;
// Anteriormente era interpretado como
echo (yield) - 1;
// Y ahora es intrepretado como
echo yield (-1);

yield 
$foo or die;
// Anteriormente era interpretado como
yield ($foo or die);
// Y ahora es intrepretado como
(yield $foo) or die;
?>

Los paréntesis se pueden emplear para desambiguar estos casos.

Las funciones no pueden tener varios parámetros con el mismo nombre

Ya no es posible definir dos o más parámetros de función con el mismo nombre. Por ejemplo, el siguiente método generará un error E_COMPILE_ERROR:

<?php
function foo($a$b$sin_usar$sin_usar) {
    
//
}
?>

Las funciones que inspeccionan argumentos informan del valor del parámetro actual

func_get_arg(), func_get_args(), debug_backtrace() y las excepciones de información de rastreo ya no informarán del valor original que fue pasado a un paramétro, en su lugar proporcionarán el valor actual (el cual podría haber sido modificado).

<?php
function foo($x) {
    
$x++;
    
var_dump(func_get_arg(0));
}
foo(1);?>

Salida del ejemplo anterior en PHP 5:

1

Salida del ejemplo anterior en PHP 7:

2

Las sentencias switch no pueden tener varios bloques default

Ya no es posible definir dos o más bloques default en una sentencia switch. Por ejemplo, la siguiente senetencia switch generará un error E_COMPILE_ERROR:

<?php
switch (1) {
    default:
    break;
    default:
    break;
}
?>

Eliminación de $HTTP_RAW_POST_DATA

$HTTP_RAW_POST_DATA ya no está disponible. Debería usarse el flujo php://input en su lugar.

Eliminación de los comentarios # en ficheros INI

Se ha eliminado el soporte para los comentarios prefijados con # en los ficheros INI. Se debe emplear en su lugar ; (punto y coma). Este cambio se aplica a php.ini, así como a ficheros manejados por parse_ini_file() y parse_ini_string().

JSON extension replaced with JSOND

Se ha reemplazado la extensión JSON por JSOND ocasionando tres roturas de RC (retocompatibilidad). La primera, un número no puede finalizar con un punto decimal (esto es, 34. debe cambiarse para que sea o 34.0 o 34). La segunda, al utilizar notación científica, el exponente e no debe seguir inmediatamente a un punto decimal (esto es, 3.e3 se debe cambiar para que sea o 3.0e3 o 3e3). Finalmente, una cadena vacía ya no se considera un JSON válido.

Fallo de funciones internas en un desbordamiento

Anteriormente, las funciones internas truncaban de forma silenciosa números producidos desde coacciones de tipo float a integer cuando el float era demasiado grande de representar como un integer. Ahora, se emitirá un E_WARNING y se devolverá NULL.

Reparaciones en los valores de devolución de gestores de sesión personalizados

Cualquier función declarada implementada por gestores de sesión personalizados que devuelvan o FALSE o -1 serán errores fatales. Si desde estas funciones se devuelve cualquier valor distinto de un booleano, -1, o 0, fallarán y se emitirá un E_WARNING.

Sort order of equal elements

El algoritmo de ordenación interno ha sido mejorado, lo cual puede resultar en un orden diferente de elementos, que se comparan como iguales, que antes.

Nota:

No dependa del orden de los elementos que se comparan como iguales; podría cambiar en cualquier momento.