PHP 8.0: Una introducción a la nueva versión de PHP
Estamos encantados de anunciar que PHP 8.0 ya está disponible en los servidores de Hostinger. Esta versión viene con nuevas funciones y cambios que brindarán el mejor rendimiento posible a tus proyectos web.
Dado que se trata de una actualización importante, animamos a todos los usuarios a que migren a la última versión y a familiarizarse con los nuevos cambios, los cuales estaremos cubriendo en este artículo.
¿Por qué necesitas PHP 8.0?
Las últimas pruebas referenciales realizadas por Phoronix muestran que PHP 8.0 se está desempeñando un 10% mejor que sus predecesores. Estos datos sugieren un futuro prometedor para los sitios web y aplicaciones basadas en PHP.
Mejor aún, la prueba ni siquiera se realizó con el compilador JIT, una nueva función de optimización introducida con PHP 8.0. Así que, los desarrolladores pueden esperar un rendimiento mucho más óptimo si este está habilitado.
Además, la versión implementa nuevas características para hacer que la codificación sea mucho más rápida y limpia, lo que reduce la cantidad de código repetitivo y redundante.
Dado que se trata de una actualización de versión, es probable que tu sitio web experimente cambios que podrían dejarlo fuera de servicio si realizas la transición a PHP 8.0 sin aplicar modificaciones previas. Para ayudarte a prepararte para la migración, te guiaremos a través de las últimas funciones.
Cómo cambiar tu versión de PHP
Los clientes de Hostinger pueden seguir estos pasos para actualizar su versión de PHP:
- Iniciar sesión en hPanel y abrir el panel de la Cuenta de Hosting.
- En la sección Avanzado, hacer clic en Configuración de PHP.
- La pestaña Versión de PHP mostrará qué versión se está utilizando. Para cambiar a la última versión, seleccionar PHP 8.0 y presionar Guardar.
Ten en cuenta que la actualización puede tardar un par de minutos en completarse y hacer que tu sitio web no esté disponible temporalmente.
Qué hay de nuevo en PHP 8.0
Hay muchos cambios de calidad de vida que vienen con la nueva actualización junto con muchas características nuevas. Exploremos las novedades y los cambios en PHP 8.0.
Compilador JIT (Just-in-Time)
El compilador JIT es una de las actualizaciones más interesantes de PHP 8.0. Esta función tiene como objetivo trabajar con opcache para mejorar el rendimiento en la ejecución de scripts.
¿Qué es JIT?
JIT, abreviatura de just-in-time, compila el código de operación en código de máquina justo antes de ejecutarlo para su salida. Para comprender lo que eso significa y cómo funciona, necesitamos comprender las etapas de ejecución del script PHP, que son:
- Análisis léxico. Este paso es donde Zend Engine, el intérprete de PHP, traduce el código en tokens legibles por máquina.
- Analizador. El intérprete analiza los tokens para generar el árbol de sintaxis abstracta (AST), una representación en forma de árbol que muestra cómo funciona el código.
- Compilación. El intérprete convierte los nodos AST en código de operación, que es una instrucción legible por máquina que le dice a la máquina virtual Zend (VM) qué operación realizar.
- Ejecución. El intérprete entrega el código de operación a Zend VM, que compilará el código de operación en código de máquina para su ejecución.
Este proceso utiliza una cantidad significativa de recursos del servidor, especialmente si un script PHP recibe solicitudes repetidas.
Es por eso que PHP 5.5 introdujo la extensión opcache, que almacena el código de operación desde la etapa de compilación.
Cuando el servidor recibe una nueva solicitud para el mismo script, el intérprete puede ejecutar inmediatamente el código de operación desde el opcache. De esa forma, no es necesario reiniciar el proceso de ejecución desde el principio.
PHP 7.4 agregó una función de precarga varios años después, para convertir los scripts de opcache pre-compilados en código de operación durante el inicio. Como resultado, el intérprete puede entregar inmediatamente el código de operación para su ejecución cuando el servidor recibe por primera vez una solicitud del script.
A pesar de estas ventajas, existen varias desventajas. Una es que hacia el final del proceso, Zend VM todavía necesita convertir el código de operación en código de máquina antes de ejecutarlo, lo que puede requerir mucho tiempo y recursos.
Ahí es donde entra el compilador JIT. Compilará el código de operación en código de máquina durante su primera ejecución para prepararse para la siguiente ejecución.
Cuando hay una solicitud de un script compilado con JIT, PHP lo ejecutará directamente por la CPU en lugar de la Zend VM, lo que da como resultado un funcionamiento más rápido. Así es como se verán las etapas de ejecución del script en PHP 8.0, en comparación con la versión anterior:
Hay dos motores de compilación JIT:
- Función. Este enfoque identificará y compilará una función completa sin averiguar qué partes son llamadas con frecuencia.
- Rastreo. Este modo solo analizará y compilará las partes más utilizadas en la función para ahorrar tiempo y memoria. Este es el motor predeterminado en PHP 8.0.
¿Qué significa JIT en pocas palabras?
Según RFC, habilitar JIT es la mejor forma de aumentar el rendimiento de PHP. Por lo tanto, omitir esta función puede hacer que se pierda un beneficio importante.
Afortunadamente, las pruebas recientes muestran que el compilador JIT ha podido mejorar el proceso de ejecución del script, principalmente si se usa el motor de seguimiento.
Los puntos de referencia sintéticos de PHP revelan un aumento de velocidad tres veces mayor después de habilitar el modo de Seguimiento. En aplicaciones de larga duración, puedes esperar ver una mejora en el rendimiento de hasta dos veces.
Para los usuarios y desarrolladores de WordPress, el compilador JIT también puede agregar un ligero impulso, aunque puede que no sea tan significativo.
Deberás reducir el TTFB, optimizar la base de datos y reducir el número de solicitudes HTTP para obtener el mejor rendimiento posible. Dicho esto, los desarrolladores de PHP pueden continuar con los esfuerzos de mejora mediante el uso de perfiles y optimizaciones especulativas.
Si deseas habilitar JIT, asegúrate de que la extensión opcache esté activa.
Los clientes con hosting compartido pueden hacer esto abriendo el panel de la Cuenta de Hosting -> Configuración de PHP. En la pestaña Extensiones de PHP, asegúrate de marcar la casilla “opcache“.
Nuevas funciones en PHP 8.0
Hay toneladas de funciones interesantes además de JIT. En esta sección, brindamos una descripción general de las principales adiciones y cambios en PHP 8.0.
Tipos de unión 2.0
En muchos casos, una función puede usar más de un tipo, pero no era posible especificar esto en versiones anteriores de PHP a menos que declararas los tipos usando DocComments.
Este es un ejemplo de cómo se vería:
class Number { /** * @var int|float $number */ private $number; /** * @param int|float $number */ public function setNumber($number) { $this->number = $number; } /** * @return int|float */ public function getNumber() { return $this->number; } }
Las versiones anteriores de PHP introdujeron dos tipos de unión especiales: Nullable (usando la sintaxis ?Type) e Iterable (para array y Traversable).
Sin embargo, lo que faltaba era el soporte nativo para tipos de unión arbitrarios, que es una característica que viene con PHP 8.0. Ahora, simplemente puedes escribir los tipos que la función puede usar y separarlos usando la sintaxis T1|T2|…así:
class Number { private int|float $number; public function setNumber(int|float $number): void { $this->number = $number; } public function getNumber(): int|float { return $this->number; } }
Nota que en el ejemplo ya no se incluye @var, @param o @return, lo que hace que el código sea mucho más limpio.
Puedes usar tipos de unión para propiedades, argumentos y tipos de retorno, aunque hay algunas limitaciones a tener en cuenta. Consulta el RFC para obtener más información.
Argumentos de nombre
En versiones anteriores de PHP, pasar múltiples argumentos a una función requería usar las posiciones de orden en las que se declararon los parámetros, así:
array_fill(0, 100, 50);
Un reto con esto es que es posible que no recuerdes el orden de los parámetros. Además, puede ser difícil entender a qué se refiere cada uno cuando vuelves a visitar el código.
Con PHP 8.0, tienes la opción de agregar un nombre al lado del parámetro para que puedas pasarlo a una función usando su nombre en su lugar. Así es como suele verse:
// Usando argumentos posicionales: array_fill(0, 100, 50); // Usando argumentos con nombre: array_fill(start_index: 0, num: 100, value: 50);
Una ventaja de esta función es que, al aprender lo que hace cada parámetro, el proceso general será mucho más rápido.
Además, los argumentos con nombre son independientes del orden, por lo que no tendrás que recordar las posiciones de orden de cada parámetro en tus declaraciones. Por lo tanto, el ejemplo siguiente tendrá el mismo significado que el anterior:
array_fill(value: 50, num: 100, start_index: 0);
También es posible mezclar argumentos de nombre y posicionales, siempre que los nombrados ocupen el segundo lugar. En otras palabras, el siguiente código es aceptable:
test($foo, param: $bar);
Por otro lado, este código resultará en un error:
test(param: $bar, $foo);
Finalmente, con argumentos de nombre, solo necesitas escribir parámetros cuyos valores predeterminados deseas sobrescribir. No dudes en omitir los que tienen valores predeterminados que te gustaría mantener. A continuación, se ofrece un ejemplo en el RFC:
htmlspecialchars($string, default, default, false); // vs htmlspecialchars($string, double_encode: false);
La sintaxis estándar para argumentos de nombre es NombreDeParametro: $valor. No escribas el nombre de forma dinámica, como se ilustra a continuación, ya que se producirá un error.
// NO admitido. function_name($variableStoringParamName: $value);
Expresiones de coincidencia
Una expresión de coincidencia es similar a una declaración de cambio, en el sentido de que su propósito es comparar varios valores. Sin embargo, la semántica es mucho más eficiente y menos propensa a errores.
Considera el siguiente ejemplo de declaración de cambio del RFC:
switch ($this->lexer->lookahead['type']) { case Lexer::T_SELECT: $statement = $this->SelectStatement(); break; case Lexer::T_UPDATE: $statement = $this->UpdateStatement(); break; case Lexer::T_DELETE: $statement = $this->DeleteStatement(); break; default: $this->syntaxError('SELECT, UPDATE or DELETE'); break; }
Con una expresión de coincidencia, la misma declaración se verá mucho más corta:
$statement = match ($this->lexer->lookahead['type']) { Lexer::T_SELECT => $this->SelectStatement(), Lexer::T_UPDATE => $this->UpdateStatement(), Lexer::T_DELETE => $this->DeleteStatement(), default => $this->syntaxError('SELECT, UPDATE or DELETE'), };
El bloque de código anterior muestra que una expresión de coincidencia puede devolver un valor. Esto es diferente a una declaración de cambio en la que debes asignar $resultado hacia el final.
Además, no es necesario agregar una llave (o break, en inglés) después de cada línea porque queda implementada implícitamente.
Además de eso, esta función llevará a cabo una comparación estricta “===” en lugar de una “==” suelta. Una comparación imprecisa, realizada por declaraciones de cambio, a menudo puede dar resultados impredecibles, creando errores en tu código. Veamos el código a continuación:
switch ('foo') { case 0: $result = "¡Oh no!\n"; break; case 'foo': $result = "Esto es lo que esperaba\n"; break; } echo $result; //> ¡Oh no!
Una expresión de coincidencia presentará un resultado más apropiado:
echo match ('foo') { 0 => "Oh no!\n", 'foo' => "Esto es lo que esperaba\n", }; //> Esto es lo que esperaba
Algo importante que debes recordar al utilizar esta función es que una de las líneas debe tener un valor que coincida con la condición. Alternativamente, debería haber una por defecto declarado. De lo contrario, arrojará un UnhandledMatchError.
Atributos
Un atributo es una función de metadatos que puede documentar lo que significa una sección de código. Los desarrolladores pueden colocarlo en funciones, clases, constantes de clase, propiedades de clase, métodos de clase y parámetros de función o método.
Esta función es similar a DocComments, aunque existen algunas diferencias.
En primer lugar, el hecho de que sea nativo del sistema PHP lo hace legible para herramientas como analizadores estáticos y resaltadores de sintaxis. Es por eso que el RFC propone atributos como una forma más estructurada y sintáctica de metadatos.
Para indicar atributos, encierra el texto entre dos símbolos menor que y mayor que, así <<Ejemplo>>. Puedes agregarlos antes de las declaraciones a especificar, así:
<<AtributoEjemplo>> class Foo { <<AtributoEjemplo>> public const FOO = 'foo'; <<AtributoEjemplo>> public $x; <<AtributoEjemplo>> public function foo(<<AtributoEjemplo>> $bar) { } }
Además de esto, los usuarios pueden incluir uno o varios valores asociados en el atributo, como se ilustra a continuación:
<<SinArgumento>> <<ArgumentoSencillo(0)>> <<PocosArgumentos('Hola', 'Mundo')>> function foo() {}
También es posible adjuntar atributos antes o después de un DocComment.
<<AtributoEjemplo>> /** docblock */ <<OtroAtributoEjemplo>> function foo() {}
Promoción de propiedad para constructor
Esta función es una nueva sintaxis abreviada que tiene como objetivo reducir la cantidad de texto repetitivo cuando se usa la función de constructor.
En versiones anteriores de PHP, se definían objetos de valor simples repitiendo las propiedades: una vez en la declaración de propiedad, una vez en los parámetros del constructor y dos veces en las asignaciones de propiedad. Echa un vistazo al siguiente ejemplo:
class Point { public float $x; public float $y; public float $z; public function __construct( float $x = 0.0, float $y = 0.0, float $z = 0.0, ) { $this->x = $x; $this->y = $y; $this->z = $z; } }
Con PHP 8.0, puedes declarar la propiedad una vez y combinarla con la firma del constructor, ofreciendo una experiencia de escritura de código más efectiva.
class Point { public function __construct( public float $x = 0.0, public float $y = 0.0, public float $z = 0.0, ) {} }
El script se ejecutará de la misma forma que el ejemplo anterior. Si los parámetros tienen un prefijo con un marcador de visibilidad, PHP traducirá la sintaxis a la versión tradicional y la ejecutará después de eso.
La RFC explica que hay varias restricciones a considerar con la promoción de propiedad de constructor. Una es evitar el uso de palabras clave var, ya que la función ya requiere la promoción de propiedades con un marcador de visibilidad.
class Test { // Error: Palabra clave "var" no es compatible. public function __construct(var $prop) {} }
En segundo lugar, cuando el tipo es nulo, debe declararse explícitamente utilizando la sintaxis que acepta valores ?Type.
class Test { // Error: Usando propiedad null por defecto o no-null public function __construct(public Type $prop = null) {} // Correcto: Hacer que el tipo sea explícitamente null en su lugar public function __construct(public ?Type $prop = null) {} }
Los parámetros promocionados sólo pueden ocurrir en constructores no abstractos. Por lo tanto, el siguiente ejemplo dará como resultado un error:
// Error: No es un constructor. function test(private $x) {} abstract class Test { // Error: Constructor abstracto. abstract public function __construct(private $x); }
Una clase puede tener propiedades promovidas por el constructor y declaraciones de propiedad explícitas. Sin embargo, no es posible usar la misma propiedad en ambos, así:
class Test { public $prop; // Error: Redeclaración de propiedad. public function __construct(public $prop) {} }
Tampoco hay promoción de parámetros variadic en constructores.
class Test { // Error: Parámetro variadic. public function __construct(public string ...$strings) {} }
Finalmente, no es posible usar el tipo invocable en la declaración de propiedad del constructor.
class Test { // Error: Tipo invocable no es compatible con las propiedades. public function __construct(public callable $callback) {} }
Operador Nullsafe
La verificación de un valor nulo mediante una declaración condicional generalmente da como resultado un código que está profundamente anidado y es difícil de leer. Considera el siguiente ejemplo:
$country = null; if ($session !== null) { $user = $session->user; if ($user !== null) { $address = $user->getAddress(); if ($address !== null) { $country = $address->country; } } } // hace algo con $country
Para combatir este problema, PHP 8.0 presenta el operador nullsafe. Con esta función, puedes escribir el código del ejemplo anterior utilizando la sintaxis ?->.
$country = $session?->user?->getAddress()?->country; // hace algo con $country
Según el RFC, PHP comprobará si el primer operador $session da como resultado un valor nulo. Si no es así, la ejecución continuará hasta el último operador. PHP detendrá el proceso de ejecución cuando uno de los operadores resulte nulo.
Una limitación del uso de un operador nullsafe es que no puede existir en un contexto de escritura. Como resultado, lo siguiente creará un error:
foreach ([1, 2, 3] as $foo?->bar->baz) {} // No se puede usar el operador nullsafe en contexto de escritura Además, no se permite tomar una referencia de una cadena nullsafe. // 1 $x = &$foo?->bar; // Error del compilador: no se puede hacer referencia a una cadena nullsafe // 2 takes_ref($foo?->bar); // Error: no se puede pasar el parámetro 1 por referencia // 3 function &return_by_ref($foo) { return $foo?->bar; // Error del compilador: no se puede hacer referencia a una cadena nullsafe }
Mapas débiles
Con PHP 7.4, los desarrolladores pueden hacer referencias débiles a un objeto clave para que, a diferencia de las relaciones fuertes, no aumenten el contador de referencia del objeto y eviten la eliminación del objeto cuando ya no se usa.
Un mapa débil es una mejora de esa función. Almacena valores arbitrarios utilizados como referencias débiles a un objeto. De esa manera, cuando el objeto cae fuera de alcance o se desarma, PHP puede borrar cualquier valor asociado en el mapa débil.
El siguiente ejemplo, procedente del RFC, muestra cómo se verán los mapas débiles:
$map = new WeakMap; $obj = new stdClass; $map[$obj] = 42; var_dump($map); // object(WeakMap)#1 (1) { // [0]=> // array(2) { // ["key"]=> // object(stdClass)#2 (0) { // } // ["value"]=> // int(42) // } // } // El objeto se destruye aquí y la clave se elimina automáticamente del mapa débil. unset($obj); var_dump($map); // objeto(WeakMap)#1 (0) { // }
Esta función será más beneficiosa para las aplicaciones de ejecución prolongada, especialmente si desesa evitar pérdidas de memoria e implementar el almacenamiento en caché.
Nuevas características menores
Aquí hay algunas funciones adicionales menores que puedes esperar de PHP 8.0:
- Comas finales en listas de parámetros. Puedes listar los parámetros en un formato vertical en lugar del estándar, horizontal.
- str_contains(). Esta función examinará si una cadena se incorpora como elemento de otra cadena. Devolverá un valor booleano dependiendo de si la condición es verdadera o falsa. Puedes considerarlo como una alternativa más legible a str_pos().
- str_starts_with(). Verificará si una cadena comienza con una subcadena especificada.
- str_ends_with(). Determinará si una cadena termina con una subcadena determinada.
- Interfaz encadenable. Las clases que usan el método __toString () ahora tendrán la interfaz Stringable implementada automáticamente. El objetivo es permitir el tipo de unión string|Stringable, lo que significa que PHP podrá aceptar una cadena o un objeto usando el método __toString().
- fdiv(). PHP 8.0 acepta la división por cero y devolverá INF, -INF o NAN en lugar de un error.
- get_debug_type(). Esta función opera de manera similar a gettype(), aunque devolverá los nombres de los tipos nativos y los nombres de las clases. La siguiente tabla de referencia muestra en qué se diferencian los dos.
- get_resource_id(). Esta función es una alternativa de tipo seguro a (int) $resource. Obtendrás el ID de un recurso externo, como una conexión de base de datos.
- PhpToken::tokenize(). Este método reemplazará la función token_get_all(). En lugar de devolver cadenas o matrices de valores, devolverá una matriz de objetos. El código resultante será más legible y más eficiente en memoria.
- Nuevo DOM Living Standard en ext/dom. Nuevas interfaces y propiedades públicas para adaptarse a las API DOM actuales, que se han vuelto necesarias cuando se trata de documentos XML y HTML.
Funciones obsoletas en PHP 8.0
PHP 8.0 dejará de usar las siguientes funciones:
- Incluir un valor predeterminado en un parámetro seguido de un parámetro obligatorio, ya que el valor predeterminado no tiene ningún efecto
- Usar get_defined_functions() con exclude_disabled establecido explícitamente en falso
- función enchant_broker_set_dict_path()
- función enchant_broker_get_dict_path()
- función enchant_dict_add_to_personal()
- función enchant_dict_is_in_session()
- función enchant_broker_free()
- función enchant_broker_free_dict()
- Constante ENCHANT_MYSPELL
- Constante ENCHANT_ISPELL
- función libxml_disable_entity_loader()
- La constante PGSQL_LIBPQ_VERSION_STR, junto con algunos alias de funciones en la extensión pgsql
- Uso de funciones de comparación por orden que devuelven verdadero o falso
- Uso de un archivo vacío como ZipArchive
- API de procedimiento de Zip
- Función ReflectionFunction::isDisabled()
- Función ReflectionParameter::getClass()
- Función ReflectionParameter::isArray()
- Función ReflectionParameter::isCallable()
Consulta este manual para obtener más información sobre las funciones obsoletas.
Cambios incompatibles con versiones anteriores a PHP 8.0
La migración a PHP 8.0 trae muchos cambios que son incompatibles con versiones anteriores de PHP. Algunos de ellos incluyen:
- Comparación de cadena a número. La comparación de números con cadenas no numéricas ahora devolverá resultará en falso. Consulta la tabla de referencia a continuación:
- Sin espacios en blanco en los nombres con espacios. Por ejemplo, nombres como “Foo \ bar” no serán aceptables. Utiliza “Foo\bar” en su lugar.
- Recurso a la migración de objetos. Algunas funciones ahora devolverán un objeto en lugar de un recurso.
- La eliminación de las banderas FILTER_FLAG_SCHEME_REQUIRED y FILTER_FLAG_HOST_REQUIRED. Esto se debe a que FILTER_VALIDATE_URL ya usa estas banderas por defecto cuando se combina con la función filter_var().
- E_ALL es ahora el nivel de informe de errores predeterminado. PHP 8.0 incluirá avisos y cambios obsoletos en los mensajes de error.
Asegúrate de leer la documentación oficial para ver otros cambios incompatibles con versiones anteriores.
Otros cambios en PHP 8.0
Los cambios a continuación no darán como resultado una incompatibilidad con versiones anteriores, pero aún es esencial tenerlos en cuenta. Algunos de ellos incluyen:
- Puedes filtrar los resultados de ReflectionClass::getConstants() y ReflectionClass::getReflectionConstants() utilizando el nuevo parámetro filter.
- Los métodos ZipArchive::addGlob() y ZipArchive::addPattern() ahora aceptan flags, comp_method, comp_flags, env_method y enc_password en la matriz de opciones.
- Hay un nuevo argumento de banderas para los métodos ZipArchive::addEmptyDir(), ZipArchive::addFile() y ZipArchive::addFromString().
- Debido a su función esencial en PHP, la extensión JSON ahora está habilitada de forma predeterminada.
- La función array_slice() no tendrá que encontrar la posición inicial si la matriz no tiene espacios.
Este material brinda más información sobre los cambios.
WordPress y PHP 8.0
WordPress 5.6 y superior es compatible con PHP 8.0. El equipo de desarrolladores ha eliminado muchas funciones obsoletas para lograr esto.
Sin embargo, no pueden reclamar compatibilidad total ya que es posible que muchos plugins y temas aún no sean compatibles con PHP 8.0. Si deseas cambiar a PHP 8.0, asegúrate de que puedes realizar la transición sin problemas. De lo contrario, si estás trabajando con un desarrollador web profesional, pídele que te ayude con la transición.
Si eres un desarrollador de plugins o temas, WordPress recomienda adaptar el código de tu producto a la nueva versión de PHP. De esa manera, los usuarios del plugin o tema no experimentarán cambios importantes en su sitio web si actualizan el software.
Este artículo describe los principales cambios que pueden aplicar los desarrolladores de WordPress. Dicho esto, recomendamos leer este anuncio de WordPress para ver a cuáles debes prestar especial atención.
Recuerda que siempre puedes volver a la versión PHP 7.4 si quedan demasiados problemas por resolver.
Conclusión
PHP 8.0 tiene algunos cambios y mejoras interesantes para los desarrolladores de PHP. Desde el compilador JIT, promoción de propiedades del constructor, hasta mapas débiles. Espera ver un aumento en el rendimiento del sitio web y en la experiencia de escritura de código.
Asegúrate de consultar las RFCs y la documentación para que tus proyectos sean totalmente compatibles con la última versión de PHP. ¡Buena suerte!