Numerosos errores fatales y recuperables se han convertido en excepciones en PHP 7. Estas excepciones de error heredan de la clase Error, que a su vez implementa la interfaz Throwable (la nueva interfaz base de la que todas las excepciones heredan).
Esto significa que los manejadores de errores personalizados podrían no invocarse, ya que las excepciones se pueden lanzar en su lugar (provocando nuevos errores irrecuperables para las excepciones Error no interceptadas).
Una descripción más completa de cómo funcionan los errores en PHP 7 se encuentra en la página de errores de PHP 7. Esta guía de migración simplemente enumerará los cambios que afectan la retrocompatibilidad.
El código que implementa un manejador de excepciones inscrito con set_exception_handler() usando una declaración de tipo Exception provocará un error fatal cuando se lanza un objeto Error.
Si el manejador debe funcionar tanto con PHP 5 como con 7, debería eliminar la declaración de tipo del manejador, mientras que el código que se migra para funcionar exclusivamente en PHP 7 puede simplemente reemplazar la declaración de tipo Exception por Throwable.
<?php
// Código para PHP 5 que fallará.
function handler(Exception $e) { /* ... */ }
set_exception_handler('handler');
// Compatible con PHP 5 y 7.
function handler($e) { /* ... */ }
// Solo PHP 7.
function handler(Throwable $e) { /* ... */ }
?>
Anteriormente, algunas clases internas devolvían null o un objeto inutilizable
cuando el constructor fallaba. Todas las clases internas lanzarán ahora una
Exception en este caso de la misma manera que
las clases de usuario.
Los errores del analizador ahora lanzan un objeto ParseError.
El manejo de errores para eval() ahora debe incluir un bloque
catch que pueda manejar este error.
Todos los avisos E_STRICT se han reclasificado a otros
niveles. La constante E_STRICT se conserva, por lo que las
llamadas como error_reporting(E_ALL|E_STRICT) no provocarán errores.
| Situación | Nuevo nivel/comportamiento |
|---|---|
| Indexación por un recurso | E_NOTICE |
| Métodos estáticos abstractos | Aviso eliminado, no dispara ningún error |
| "Redefinir" un constructor | Aviso eliminado, no dispara ningún error |
| Incompatibilidad de firma durante la herencia | E_WARNING |
| Misma propiedad (compatible) en dos rasgos usados | Aviso eliminado, no dispara ningún error |
| Acceso a una propiedad estática de manera no estática | E_NOTICE |
| Solo se deben asignar variables por referencia | E_NOTICE |
| Solo se deben pasar variables por referencia | E_NOTICE |
| Llamada a métodos no estáticos de manera estática | E_DEPRECATED |
PHP 7 ahora utiliza un árbol de sintaxis abstracta al analizar los ficheros fuente. Esto ha permitido numerosas mejoras en el lenguaje que anteriormente eran imposibles debido a las limitaciones en el analizador utilizado en versiones anteriores de PHP, pero ha conllevado la eliminación de algunos casos especiales por razones de consistencia, lo que rompe la retrocompatibilidad. Estos casos se detallan en esta sección.
El acceso indirecto a variables, propiedades y métodos ahora se evaluará estrictamente en orden de izquierda a derecha, en contraste con la combinación anterior de casos especiales. La siguiente tabla muestra cómo ha cambiado el orden de evaluación.
| Expresión | Interpretación PHP 5 | Interpretación 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 utilizaba el antiguo orden de evaluación de derecha a izquierda se debe reescribir para usar explícitamente este orden de evaluación con llaves (ver la columna del medio anterior). Esto hará que el código sea compatible con PHP 7.x y retrocompatible con PHP 5.x.
Esto también afecta a la palabra clave global. La sintaxis de llaves se puede
utilizar para emular el comportamiento anterior si es necesario:
<?php
function f() {
// Válido solo en PHP 5.
global $$foo->bar;
// Válido en PHP 5 y 7.
global ${$foo->bar};
}
?>
list() ahora asignará valores a las variables en el orden
en que se definen, en lugar de en orden inverso. En general, esto solo
afecta al caso en que list() se usa en conjunción con
el operador de array [], como se ilustra a continuación:
<?php
list($a[], $a[], $a[]) = [1, 2, 3];
var_dump($a);
?>Resultado del ejemplo anterior en PHP 5:
array(3) {
[0]=>
int(3)
[1]=>
int(2)
[2]=>
int(1)
}
Resultado 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 que ocurren las asignaciones de la función list(), ya que es un detalle de implementación que puede cambiar nuevamente en el futuro.
Las construcciones de list() ya no pueden estar vacías. Los siguientes elementos ya no están permitidos:
<?php
list() = $a;
list(,,) = $a;
list($x, list(), $y) = $a;
?>list() ya no puede desempaquetar variables de string. Debe usarse str_split() en su lugar.
El orden de los elementos en un array ha cambiado cuando estos elementos se crearon automáticamente al referenciarlos en una asignación por referencia. Por ejemplo:
<?php
$array = [];
$array["a"] =& $array["b"];
$array["b"] = 1;
var_dump($array);
?>Resultado del ejemplo anterior en PHP 5:
array(2) {
["b"]=>
&int(1)
["a"]=>
&int(1)
}
Resultado del ejemplo anterior en PHP 7:
array(2) {
["a"]=>
&int(1)
["b"]=>
&int(1)
}
En PHP 5, el uso de paréntesis redundantes alrededor de un argumento de una función podía silenciar las advertencias de estándares estrictos (strict standards) cuando el argumento de la función se pasaba por referencia. Ahora se emite siempre la advertencia.
<?php
function getArray() {
return [1, 2, 3];
}
function squareArray(array &$a) {
foreach ($a as &$v) {
$v **= 2;
}
}
// Genera una advertencia en PHP 7.
squareArray((getArray()));
?>El ejemplo anterior mostrará:
Notice: Only variables should be passed by reference in /tmp/test.php on line 13
foreach
Se han realizado cambios menores en el comportamiento de la estructura de
control foreach, principalmente en la gestión del puntero interno del array y
la modificación del array mientras se recorre.
foreach ya no modifica el puntero interno del array
Antes de PHP 7, el puntero interno del array se modificaba mientras se recorría
un array con foreach. Esto ya no es así, como se muestra
en el siguiente ejemplo:
<?php
$array = [0, 1, 2];
foreach ($array as &$val) {
var_dump(current($array));
}
?>Resultado del ejemplo anterior en PHP 5:
int(1) int(2) bool(false)
Resultado del ejemplo anterior en PHP 7:
int(0) int(0) int(0)
foreach por valor trabaja sobre una copia del array
Al utilizar el modo predeterminado (por valor), foreach ahora trabaja sobre una
copia del array en lugar del array original. Esto significa que los cambios
realizados en el array mientras se recorre no afectarán los valores que
se están iterando.
foreach por referencia
Al recorrer un array por referencia, foreach ahora identifica mejor
los cambios realizados en el array durante la iteración. Por
ejemplo, si se añaden valores a un array mientras se recorre,
estos nuevos valores también se iterarán:
<?php
$array = [0];
foreach ($array as &$val) {
var_dump($val);
$array[1] = 1;
}
?>Resultado del ejemplo anterior en PHP 5:
int(0)
Resultado del ejemplo anterior en PHP 7:
int(0) int(1)
La iteración de un objeto no-Traversable ahora es idéntica a la iteración de un array por referencia. Como resultado, la mejora en el comportamiento cuando se modifica un array durante su iteración también se aplica cuando se añaden o eliminan propiedades de un objeto.
Anteriormente, los literales octales que contenían números no válidos se
truncaban silenciosamente (0128 se interpretaba como 012).
Ahora, un literal octal no válido provocará un error de análisis.
Los desplazamientos de bits por números negativos ahora lanzarán una ArithmeticError:
<?php
var_dump(1 >> -1);
?>Resultado del ejemplo anterior en PHP 5:
int(0)
Resultado 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
Los desplazamientos de bits (en ambos sentidos) más allá del ancho de bits de un int siempre devolverán 0. Anteriormente, el comportamiento de estos desplazamientos dependía de la arquitectura.
Anteriormente, cuando se utilizaba 0 como divisor en los operadores de división (/) o
módulo (%), se emitía un E_WARNING y se devolvía
false. Ahora, el operador de división devuelve un float
como +INF, -INF o NAN, según lo especificado por IEEE 754. La advertencia E_WARNING del operador de módulo
se ha eliminado y ahora lanzará una excepción
DivisionByZeroError.
<?php
var_dump(3/0);
var_dump(0/0);
var_dump(0%0);
?>Resultado 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)
Resultado 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
Las string que contienen números hexadecimales ya no se consideran numéricas. Por ejemplo:
<?php
var_dump("0x123" == "291");
var_dump(is_numeric("0x123"));
var_dump("0xe" + "0x1");
var_dump(substr("foo", "0x1"));
?>Resultado del ejemplo anterior en PHP 5:
bool(true) bool(true) int(15) string(2) "oo"
Resultado 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"
filter_var() puede ser utilizado para verificar si una string contiene un número hexadecimal, y también para convertir una cadena de este tipo en un int:
<?php
$str = "0xffff";
$int = filter_var($str, FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX);
if (false === $int) {
throw new Exception("Invalid integer!");
}
var_dump($int); // int(65535)
?>\u{ puede causar errores
Debido a la adición de la nueva sintaxis de escape de punto de
código Unicode, las string que contienen un literal \u{ seguido de
una secuencia no válida provocarán un error fatal. Para evitar esto, la barra invertida
principal debe ser escapada.
Estas funciones se deprecaron en PHP 4.1.0 en favor
de call_user_func()
y call_user_func_array(). También se pueden utilizar las
funciones variables
y/o el
operador ....
Todas las funciones ereg se han
eliminado. PCRE es una alternativa recomendada.
La función obsoleta mcrypt_generic_end() se ha reemplazado por mcrypt_generic_deinit().
Además, las funciones obsoletas
mcrypt_ecb(), mcrypt_cbc(), mcrypt_cfb()
y mcrypt_ofb() se han reemplazado por el uso
de mcrypt_decrypt() con la constante
apropiada MCRYPT_MODE_*.
Todas las funciones ext/mysql se han eliminado. Para más información sobre la elección de otra API MySQL, consulte elegir una API MySQL.
Todas las funciones ext/mssql se han
eliminado.
Los alias obsoletos datefmt_set_timezone_id() y IntlDateFormatter::setTimeZoneID() se han eliminado y reemplazado respectivamente por datefmt_set_timezone() y IntlDateFormatter::setTimeZone().
set_magic_quotes_runtime(), así como su alias magic_quotes_runtime(), se han eliminado. Se deprecaron desde PHP 5.3.0 y no tienen efecto desde la eliminación de las comillas mágicas en PHP 5.4.0.
El alias obsoleto set_socket_blocking() se ha eliminado y reemplazado por stream_set_blocking().
dl() ya no se puede utilizar con PHP-FPM. Continúa funcionando en las SAPIs CLI y Embed.
El soporte para las fuentes PostScript Type1 se ha eliminado de la extensión GD, lo que conlleva la eliminación de las siguientes funciones:
En su lugar, se recomienda utilizar las fuentes TrueType y sus funciones asociadas.
Las siguientes directivas INI se han eliminado porque sus funcionalidades asociadas también se han eliminado:
always_populate_raw_post_data
asp_tags
xsl.security_prefs
La directiva xsl.security_prefs se ha eliminado. En
su lugar, se debe
llamar al método XsltProcessor::setSecurityPrefs() para controlar las preferencias de seguridad de forma individual para
cada procesador.
El resultado de la instrucción new ya no se puede asignar a una variable
por referencia:
<?php
class C {}
$c =& new C;
?>Resultado del ejemplo anterior en PHP 5:
Deprecated: Assigning the return value of new by reference is deprecated in /tmp/test.php on line 3
Resultado del ejemplo anterior en PHP 7:
Parse error: syntax error, unexpected 'new' (T_NEW) in /tmp/test.php on line 3
Los siguientes nombres no pueden utilizarse para nombrar clases, interfaces o traits:
Además, no deben utilizarse los siguientes nombres. Aunque no generan un error en PHP 7.0, están reservados para uso futuro y se deben considerar obsoletos.
Se ha eliminado el soporte para el uso de etiquetas ASP y script para delimitar código PHP. Las etiquetas afectadas son:
| Etiqueta de apertura | Etiqueta de cierre |
|---|---|
<% |
%> |
<%= |
%> |
<script language="php"> |
</script> |
Anteriormente no recomendado en PHP
5.6, Las llamadas estáticas a un método no estático con un contexto incompatible
ahora resultarán en que el método llamado tendrá un indefinido
$this y se emitirá una advertencia de obsolescencia.
<?php
class A {
public function test() { var_dump($this); }
}
// Nota: NO extiende A
class B {
public function callNonStaticMethodOfA() { A::test(); }
}
(new B)->callNonStaticMethodOfA();
?>Resultado del ejemplo anterior en PHP 5.6:
Deprecated: Non-static method A::test() should not be called statically, assuming $this from incompatible context in /tmp/test.php on line 8
object(B)#1 (0) {
}
Resultado del ejemplo anterior en PHP 7:
Deprecated: Non-static method A::test() 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 es ahora un operador asociativo derecho
La construcción yield ya no requiere paréntesis y ha sido sustituida por
un operador asociativo derecho con prioridad entre print
y =>. Esto puede provocar un cambio en
el comportamiento:
<?php
echo yield -1;
// Antes se interpretaba como
echo (yield) - 1;
// Y ahora se interpreta como
echo yield (-1);
yield $foo or die;
// Antes se interpretaba como
yield ($foo or die);
// Y ahora se interpreta como
(yield $foo) or die;
?>Los paréntesis pueden utilizarse para eliminar la ambigüedad en estos casos.
Ya no es posible definir dos o más parámetros de función con el
mismo nombre. Por ejemplo, la siguiente función desencadenará un
E_COMPILE_ERROR:
<?php
function foo($a, $b, $unused, $unused) {
//
}
?>func_get_arg(), func_get_args(), debug_backtrace() y las trazas de excepciones ya no devuelven el valor original que se pasó a un parámetro, sino que proporcionarán el valor actual (que podría haber sido modificado).
<?php
function foo($x) {
$x++;
var_dump(func_get_arg(0));
}
foo(1);?>Resultado del ejemplo anterior en PHP 5:
1
Resultado del ejemplo anterior en PHP 7:
2
Ya no es posible definir dos o más bloques por defecto en una instrucción
de conmutación. Por ejemplo, la siguiente instrucción switch desencadenará una
E_COMPILE_ERROR:
<?php
switch (1) {
default:
break;
default:
break;
}
?>
$HTTP_RAW_POST_DATA ya no está disponible. El flujo
php://input
debe ser utilizado en su lugar.
# en los ficheros INI
Se ha eliminado el soporte para los comentarios con el prefijo # en los ficheros
INI. En su lugar se debe utilizar ; (punto y coma). Este cambio
se aplica a los ficheros php.ini, así como a los ficheros gestionados
por parse_ini_file() y parse_ini_string().
La extensión JSON se ha reemplazado por JSOND, lo que conlleva tres incompatibilidades
menores de retrocompatibilidad. Primero, un número no debe terminar con una coma decimal (es
decir, 34. se debe cambiar a 34.0 o a
34). Segundo, al usar la notación científica, el
exponente e no debe seguir inmediatamente a un punto decimal
(es decir, 3.e3 se debe cambiar a
3.0e3 o a 3e3).
Finalmente, una cadena vacía ya no se considera como JSON válido.
Anteriormente, las funciones internas truncaban silenciosamente los números producidos a
partir de coerciones de tipo float a integer cuando el número era demasiado grande
para representarse como un integer. Ahora, se emitirá un E_WARNING y se devolverá null.
Todas las funciones de predicado implementadas por manejadores de
sesión personalizados que devuelvan false o -1 serán errores fatales. Si
se devuelve un valor de estas funciones distinto
de un booleano, -1 o 0, fallará y se emitirá un
E_WARNING.
El algoritmo de clasificación interno ha sido mejorado, lo que puede resultar en un orden de clasificación diferente de los elementos que se comparaban como iguales anteriormente.
Nota:
No dependa del orden de los elementos que se comparan como iguales; podría cambiar en cualquier momento.
Las instrucciones break y continue fuera de un
bucle o una estructura de control switch ahora se detectan en el momento de
la compilación en lugar de la ejecución como antes, y desencadenan un
E_COMPILE_ERROR.
Las instrucciones break y continue ya
no permiten que su argumento sea una constante, y desencadenan un
E_COMPILE_ERROR.
La extensión mhash ha sido completamente integrada en la extensión Hash. Por lo tanto, ya no es posible detectar el soporte mhash con extension_loaded(); utilizar function_exists() en su lugar. Además, mhash ya no se reporta por get_loaded_extensions() y las funcionalidades relacionadas.
La directiva declare(ticks) ya no se filtra en diferentes unidades de compilación.