PHP 8.0 disponible en Baehost

Acaba de lanzarse la versión 8.0 de PHP, el lenguaje de programación más utilizado por los desarrolladores a la hora de generar sus websites.

Ademas de ser uno de los lenguajes más usados, se destaca en gran medida por ser fácil de aprender.

Antes de enumerar las novedades que nos trae la nueva versión, aclaramos que la versión 7.4 seguirá teniendo soporte hasta el 06 de diciembre de 2021.

PHP 8.0 ya se encuentra disponible en nuestros paneles para hosting compartidos: cPanel y Plesk.

Las nuevas características que se encuentran disponibles en esta versión son:

  • The real type
  • Magic quotes legacy
  • array_key_exists() with objects
  • FILTER_SANITIZE_MAGIC_QUOTES filter
  • Reflection export() methods
  • mb_strrpos() with encoding as 3rd argument
  • implode() parameter order mix
  • Unbinding $this from non-static closures
  • hebrevc() function
  • convert_cyr_string() function
  • money_format() function
  • ezmlm_hash() function
  • restore_include_path() function
  • allow_url_include ini directive

En la página oficial del proyecto PHP podés encontrar una lista completa con todos los cambios.

PHP JIT (Just in Time Compiler)

Sin duda es la mejora más destacable de esta actualización ya que trae mejoras sustanciales en el rendimiento. El compilador JIT (just in time) compila parte del código durante el tiempo de ejecución.

PHP JIT está implementado como una parte casi independiente de OPcache. Puede ser activado/desactivado en tiempo de compilación de PHP y en tiempo de ejecución. Cuando se habilita, el código nativo de los archivos PHP se almacena en una región adicional de la memoria compartida de OPcache y los manejadores op_array→opcodes[]. mantienen punteros a los puntos de entrada del código JIT.

> La Extensión de Opcache:

PHP es un lenguaje interpretado. Esto significa que cuando un script PHP se ejecuta, el intérprete analiza, compila y ejecuta el código una y otra vez en cada solicitud. Esto puede resultar en una pérdida de recursos de CPU y de tiempo adicional.

OPcache mejora el rendimiento de PHP almacenando el código de bytes de los scripts precompilados en la memoria compartida, eliminando así la necesidad de que PHP cargue y analice los scripts en cada solicitud.

Con OPcache habilitado, el intérprete de PHP pasa por el proceso de 4 etapas (Tokenizacion, Analisis, Recopilacion e interpretacion) sólo la primera vez al momento de ejecutar el script.

A partir de PHP 5.5, la extensión OPcache de Zend está disponible por defecto y puedes comprobar si está correctamente configurada simplemente llamando a phpinfo() desde un script en tu servidor o comprobando tu archivo php.ini.

> Precarga:

Recientemente se ha mejorado OPcache con la implementación de la precarga, una nueva característica de OPcache añadida con PHP 7.4. La precarga proporciona una forma de almacenar un conjunto específico de scripts en la memoria OPcache (antes de que se ejecute cualquier código de aplicación).

En resumen, JIT traduce las partes calientes del código intermedio a código de máquina. Pasando por alto la compilación, sería capaz de traer considerables mejoras en el rendimiento y el uso de la memoria.

> JIT ideal para aplicaciones web en vivo:

Las primeras pruebas muestran que el JIT haría que las cargas de trabajo intensivas de la CPU se ejecuten significativamente más rápido.

Con el JIT activado, el código no sería ejecutado por el VM de Zend, sino por la propia CPU, y esto mejoraría la velocidad de cálculo. Las aplicaciones web como WordPress también dependen de otros factores como TTFB, optimización de bases de datos, peticiones HTTP, etc.

Así que, cuando se trate de WordPress y aplicaciones similares, no deberíamos esperar un gran aumento en la velocidad de ejecución de PHP. Sin embargo, JIT podría traer varios beneficios para los desarrolladores abriendo nuevas posibilidades a PHP como lenguaje de programación de uso general, permitiendo darle uso en campos como el machine learning.

JIT es extremadamente simple, pero su gran aspecto negativo ahonda en el aumento del nivel de complejidad del PHP, el riesgo de nuevos tipos de errores y el costo de desarrollo y mantenimiento.

Propagación de propiedades automática:

Esta nueva característica nos permite reducir significativamente la cantidad de código usada.

El RFC propone una nueva y más concisa sintaxis que simplificará la declaración de la propiedad, haciéndola más corta y menos redundante.

Esta RFC propone fusionar el constructor y la definición de los parámetros. De esta manera, a partir de PHP 8.0, tenemos una forma más utilizable de declarar los parámetros .

Esta propuesta solo se refiere a los parámetros promovidos, es decir, los parámetros de método prefijados con palabras clave de visibilidad pública, protegidas y privadas.

Actualmente, cuando queremos definir una propiedad en PHP estamos obligados a repetirla al menos en cuatro ocasiones antes de empezar a usarla.

> Herencia:

No tenemos ninguna limitación en el uso de la herencia en conjunto con los parámetros promovidos. De todos modos, no hay una relación particular entre los padres y los hijos constructores de clase.

Veamos un ejemplo:

class Test {
    public function __construct(
        public int $x = 0
    ) {}
}

class Child extends Test {
    public function __construct(
        $x, 
        public int $y = 0,
        public int $z = 0,
    ) {
        parent::__construct($x);
    }
}

> Limitaciones existentes:

– Constructores abstractos:

Las propiedades promocionadas no están permitidas en las clases e interfaces abstractas.

– Nulidad:

Anteriormente, cuando utilizábamos un tipo que no era explícitamente anulable pero con un valor predeterminado nulo, el tipo era implícitamente anulable. Pero con los tipos de propiedad, no tenemos este comportamiento implícito porque los parámetros promovidos requieren una declaración de propiedad, y el tipo anulable debe ser declarado explícitamente:

class Test {
    // Error: Using null default on non-nullable property
    public function __construct(public Type $prop = null) {}

    // Correct: Make the type explicitly nullable instead
    public function __construct(public ?Type $prop = null) {}

– Tipo de llamada:

Como el callable no es un tipo soportado para las propiedades, no se nos permite usar el tipo callable en las propiedades promocionadas:

class Test {
    // Error: Callable type not supported for properties.
    public function __construct(public callable $callback) {}
}

– La palabra clave var:

Solo se puede utilizar una palabra clave de visibilidad con los parámetros promocionados, por lo que no se permite declarar las propiedades del constructor con la palabra clave var. Ejemplo:

class Test {
    // Error: "var" keyword is not supported.
    public function __construct(var $prop) {}
}

– No se permiten duplicaciones:

Podemos combinar las propiedades promocionadas y las propiedades explícitas en la misma clase, pero las propiedades no pueden ser declaradas dos veces:

class Test {
    public string $prop;
    public int $explicitProp;

    // Correct
    public function __construct(public int $promotedProp, int $arg) {
        $this->explicitProp = $arg;
    }

    // Error: Redeclaration of property.
    public function __construct(public string $prop) {}
}

– No se permiten parámetros variables:

La razón aquí es que el tipo declarado es diferente del parámetro variable, que en realidad es un conjunto:

class Test {
    // Error: Variadic parameter.
    public function __construct(public string ...$strings) {}

Validación de los métodos de rasgos abstractos:

Los rasgos se definen como “un mecanismo para la reutilización del código en lenguajes de herencia única como el PHP”. Típicamente, se usan para declarar métodos que pueden ser usados en múltiples clases.

Un rasgo también puede contener métodos abstractos. Estos métodos simplemente declaran la firma del método, pero la implementación de este debe hacerse dentro de la clase que utiliza el rasgo.

El tipo y el número de argumentos requeridos deben ser los mismos.

Firmas de métodos incompatibles:

En PHP, los errores de herencia debidos a firmas de métodos incompatibles arrojan un error fatal o una advertencia, dependiendo de lo que esté causando el error.

Si una clase está implementando una interfaz, las firmas de métodos incompatibles arrojan un error fatal.

display_errors:

Una de las principales diferencias que notarás es que muchas de las advertencias y avisos que no se podían detectar ahora son excepciones o errores que se pueden detectar y registrar. Es posible que, debido a este cambio, ahora surjan muchos problemas que permanecían ocultos con las versiones anteriores de PHP. Esta es una gran mejora, ya que permitirá a los desarrolladores detectar problemas potenciales más fácilmente.

Sin embargo, ten en cuenta que puede ser una buena idea configurar display_errors = Off si decides usar PHP 8.0 en un sitio en producción para no mostrar tales errores a los visitantes de tu web.

Las matrices que comienzan con un índice negativo:

En PHP, si una matriz comienza con un índice negativo (start_index < 0), los siguientes índices comenzarán desde 0. Observá el siguiente ejemplo:

$a = array_fill(-5, 4, true);
var_dump($a);

Ahora, el RFC propone cambiar las cosas para que el segundo índice sea start_index + 1, cualquiera que sea el valor de start_index.

En PHP 8.0, el código resultaría en la siguiente matriz:

array(4) {
	[-5]=>
	bool(true)
	[-4]=>
	bool(true)
	[-3]=>
	bool(true)
	[-2]=>
	bool(true)
}

Con PHP 8.0, las matrices que comienzan con un índice negativo cambian su comportamiento.

Soporte para union types (tipos de unión):

Antes de PHP 8.0 , sólo podíamos especificar tipos de unión mediante anotaciones PHPdoc, pero la nueva versión incorporará soporte para los tipos de unión en las firmas de las funciones, ahorrándonos así, bastante código.

Los tipos de unión en el lenguaje nos permite pasar más información de tipos de phpdoc a firmas de funciones, con las ventajas habituales que esto conlleva:

  • Los tipos se aplican realmente, así que los errores se pueden detectar a tiempo.
  • Debido a que se hacen cumplir, es menos probable que la información tipo se vuelva anticuada o que se pierda en los bordes de los casos.
  • Los tipos se comprueban durante la herencia, aplicando el principio de sustitución de Liskov.
  • Los tipos están disponibles a través de Reflection.
  • La sintaxis es mucho menos “boilerplate-y” que “phpdoc”.

Los tipos de unión apoyan todos los tipos disponibles, con algunas limitaciones:

  • El tipo de void no podría formar parte de una unión, ya que void significa que una función no devuelve ningún valor.
  • El tipo null sólo se admite en los tipos de unión, pero su uso como tipo independiente no está permitido.
  • La notación de tipo null (?T) también está permitida, es decir, T|null, pero no se nos permite incluir la notación ?T en los tipos de unión (?T1|T2 no está permitida y debemos usar T1|T2|null en su lugar).
  • Como muchas funciones (es decir, strpos(), strstr(), substr(), etc.) incluyen false entre los posibles tipos de retorno, el false pseudo-tipo también está soportado.

Errores de tipo consistentes para las funciones internas:

Al pasar un parámetro de tipo ilegal, las funciones internas y las definidas por el usuario se comportan de manera diferente.

Las funciones definidas por el usuario arrojan un error TypeError, pero las funciones internas se comportan de diversas maneras, dependiendo de varias condiciones. De todos modos, el comportamiento típico es lanzar una advertencia y retornar null.

Si se habilita la opción strict_types, o si la informacion del argumento especifica los tipos, el comportamiento sería diferente. En tales escenarios, el error de tipo se detecta y resulta en un TypeError.

Para eliminar estas inconsistencias, este RFC propone hacer que las APIs de análisis de parámetros internos generen siempre un ThrowError en caso de una falta de coincidencia de tipo de parámetro.

Expresión throw:

Hasta ahora, en PHP throw era una declaración, por lo que no resultaba posible usarla en lugares donde sólo se permitiese el uso de una expresión.

Este RFC propone convertir la declaración de throw en una expresión para que pueda ser utilizada en cualquier contexto donde se permitan las expresiones. Por ejemplo, funciones de flecha, operador de coalescencia nula, operadores ternario y elvis, etc.

WeakMaps:

WeakMaps y WeakRefs pueden usarse para eliminar objetos, solo si la caché aún referencia a las clases de entidad de los objetos. Esto ofrece una administración de los objetos que ahorra recursos.

Coma de arrastre en la lista de parámetros:

Las comas finales son comillas que se añaden a las listas de artículos en diferentes contextos. PHP 7.2 introdujo las comillas en la sintaxis de las listas, PHP 7.3 introdujo las comillas en las llamadas a funciones.

PHP 8.0 introduce ahora comillas en las listas de parámetros con funciones, métodos y cierres, como se muestra en el siguiente ejemplo:

class Foo {
	public function __construct(
		string $x,
		int $y,
		float $z, // trailing comma
	) {
		// do something
	}
}

Allow ::class syntax en los objetos

Para buscar el nombre de una clase, podemos usar la sintaxis de FooBar::class. Este RFC propone extender la misma sintaxis a los objetos, de modo que ahora es posible obtener el nombre de la clase de un objeto dado, como se muestra en el siguiente ejemplo:

$object = new stdClass;
var_dump($object::class); // "stdClass"
 
$object = null;
var_dump($object::class); // TypeError

Con PHP 8.0, $object::class proporciona el mismo resultado que get_class($object). Si $object no es un objeto, lanza una excepción TypeError.

Esto ayuda a tener un código mas limpio.

Anotaciones de tipo (Type annotations)

Los atributos, también conocidos como anotaciones, son una forma de metadatos estructurados que pueden utilizarse para especificar las propiedades de los objetos, elementos o archivos.

Ahora, el RFC Attributes v2 introduce atributos para PHP definiéndolos como una forma de metadatos estructurados y sintácticos que pueden ser agregados a declaraciones de clases, propiedades, funciones, métodos, parámetros y constantes.

Los atributos se añaden antes de las declaraciones a las que se refieren.

Los atributos pueden ser añadidos antes o después de un comentario del bloque de documentos:

<<ExampleAttribute>>
/** docblock */
<<AnotherExampleAttribute>>
function foo() {}

Cada declaración puede tener uno o más atributos y cada atributo puede tener uno o más valores asociados:

<<WithoutArgument>>
<<SingleArgument(0)>>
<<FewArguments('Hello', 'World')>>
function foo() {}

Match expression:

PHP incorpora una nueva expresión de coincidencia similar a switch, pero dotada de una semántica más segura, así como de la capacidad de devolver valores.

Nuevo operador nullsafe:

Actualmente, cuando queríamos comprobar en PHP que un getter no devolvía null, estábamos obligados a anidar sucesivos ifs. Pero, a partir de ahora, el primer getter que devuelva null anulará la ejecución de toda la cadena:

Antes y después.

Interfaz encadenable (Stringable interface):

Stringable interface se añade automáticamente a las clases que implementan el método _toString(). Hasta ahora, este paso debía hacerse manualmente. En el código se leería de la siguiente manera:

class Foo
{
    public function __toString(): string
    {
        return 'foo';
    }
}
function bar(Stringable $stringable) { /* … */ }
bar(new Foo());
bar('abc');

Función fdiv():

Con la nueva función fdiv() es posible una división por 0. Ahora se obtiene INF, -ING o NAN como valor de retorno.

Nuevas funciones PHP:

  • str_contains:

Antes de PHP 8.0, strstr y strpos eran las opciones típicas de los desarrolladores para buscar una aguja dentro de una cadena dada. El problema es que ambas funciones no son consideradas muy intuitivas y su uso puede ser confuso para los nuevos desarrolladores de PHP.

Ahora, este RFC propone la introducción de una nueva función que permite buscar dentro de una cadena: str_contains.

str_contains ( string $haystack , string $needle ) : bool

Su uso es bastante sencillo. str_contains realiza comprobaciones si se encuentra $needle en $haystack y devuelve true o false en consecuencia.

Así que, gracias a str_contains, podemos escribir el siguiente código:

$mystring = 'Managed WordPress Hosting';
$findme   = 'WordPress';

if (str_contains($mystring, $findme)) {
	echo "The string has been found";
} else {
	echo "String not found";
}

Qué es más legible y menos propenso a errores.

Por el momento, str_contains es sensible a las mayúsculas y minúsculas, pero esto podría cambiar en el futuro.

  • str_starts_with() y str_ends_with():

Además de la función str_contains, dos nuevas funciones permiten buscar una aguja dentro de una cadena dada: str_starts_with y str_ends_with.

Estas nuevas funciones comprueban si una determinada cadena comienza o termina con otra cadena:

str_starts_with (string $haystack , string $needle) : bool
str_ends_with (string $haystack , string $needle) : bool

Ambas funciones devuelven false si $needle es más larga que el $haystack.

Gracias a ellos, ahora podemos evitar el uso de funciones subóptimas y menos intuitivas como substr, strpos. Ambas funciones son sensibles a las mayúsculas y minúsculas:

$str = "WordPress";
if (str_starts_with($str, "Word")) echo "Found!";

if (str_starts_with($str, "word")) echo "Not found!";
  • get_debug_type:

get_debug_type es una nueva función PHP que devuelve el tipo de una variable. La nueva función funciona de manera bastante similar a la función gettype, pero get_debug_type devuelve nombres de tipo nativos y resuelve nombres de clase.

Es una buena mejora para el lenguaje, ya que gettype() no es útil para la comprobación de tipos.

El RFC proporciona dos ejemplos útiles para entender mejor la diferencia entre la nueva función get_debug_type() y gettype().

Con PHP 8.0, podríamos usar get_debug_type, en su lugar:

if (!($bar instanceof Foo)) { 
	throw new TypeError('Expected ' . Foo::class . ' got ' . get_debug_type($bar));
}

RFC Adicionales:

Aquí hay una lista rápida de mejoras adicionales aprobadas que forman parte de PHP 8.0:

  • Interfaz encadenable: este RFC introduce una interfaz encadenable que se añade automáticamente a las clases que implementan el método __to String(). El objetivo principal aquí es usar el tipo de unión string|Stringable.
  • Nuevas APIs de DOM Living Standard en ext/dom: este RFC propone implementar el actual DOM Living Standard a la extensión PHP DOM introduciendo nuevas interfaces y propiedades públicas.
  • Tipo de retorno estático: PHP 8.0 introduce el uso de la static como tipo de retorno junto a los tipos self y parent.
  • Ajustes en la sintaxis variable: este RFC resuelve algunas inconsistencias residuales en la sintaxis variable de PHP.

Deja una respuesta