A partir de hoy, 26 de noviembre, PHP 8 ya está disponible. La nueva versión trae muchas novedades destinadas a mejorar y simplificar la forma de programar en este lenguaje. Algunos de los cambios más destacados son el nuevo compilador JIT (Just in Time), nuevos tipos de unión, la simplificación de los constructores de clases, la extensión JSON permanente, la transformación de la declaración ‘throw’ en expresión y nuevas funciones. A continuación, ofreceremos más detalles sobre los más importantes.
Nuevo Compilador JIT
Al tratarse de un lenguaje interpretado, cada vez que se ejecuta un script PHP, el intérprete tiene que seguir una serie de pasos: analizar, compilar y ejecutar. Esto se traduce en una pérdida de tiempo y de recursos en muchos casos, ya que el proceso se repite cada vez que el desarrollador desea poner su código en funcionamiento.
Sin embargo, el nuevo compilador promete grandes mejoras de rendimiento porque compilaría parte del código durante el tiempo de ejecución de manera similar a la que hace la caché al almacenar datos, reduciendo significativamente la velocidad.
Nuevos tipos de unión
Los tipos de unión son una colección de dos o más tipos que indican que se puede utilizar cualquiera de ellos por lo que pueden ser bastante útiles en PHP. Por ello, La RFC plantea el uso de una sintaxis T1|T2 en los tipos de unión.
public function foo(Foo|Bar $input): int|float;
Hay algunas excepciones en los que no pueden aplicarse, por ejemplo, el tipo ‘void’ no puede pertenecer a esta unión ya que hace alusión a una función que no devuelve ningún valor. Sin embargo, Las uniones nullables pueden realizarse utilizando |null o ?.
public function foo(Foo|null $foo): void; public function bar(?Bar $bar): void;
Mapas débiles
En PHP 7.4, se introdujo una clase de referencias débiles (WeakRef) que permite al programador retener una referencia a un objeto, aunque ese objeto haya sido destruido por el Garbage Collector (recolector de basura) por razones de ahorro de recursos. Estas suelen ser útiles para implementar estructuras similares a la caché, sin embargo, tienen un uso limitado.
Por ello, ahora se ha agregado una clase WeakMap que hace posible asociar objetos con valores arbitrarios, sin forzarlos a permanecer vivos para, de esta forma, liberar memoria en procesos de larga duración y, en consecuencia, mejorar el rendimiento del proceso.
class FooBar { private WeakMap $cache; public function getSomethingWithCaching(object $obj) { return $this->cache[$obj] ??= $this->computeSomethingExpensive($obj); } // ... }
Cambios en las matrices con índices negativos
Anteriormente, si el primer índice (start_index) de una matriz (array) era negativo, el siguiente continuaba por cero. En el siguiente ejemplo, se usa un método array_fill() que utiliza como parámetros el primer índice, el número de elementos y el valor con el que rellenará esos elementos:
<?php $a = array_fill(-3, 4, 'tomate'); print_r($a); ?>
En las últimas versiones, el resultado sería el siguiente:
Array ( [-3] => tomate [0] => tomate [1] => tomate [2] => tomate )
Sin embargo, en PHP 8, se propone el uso de start_index+1 para que el segundo índice siga una secuencia lógica, independientemente del valor del primer índice. Así que ahora el resultado sería el siguiente:
Array ( [-3] => tomate [-2] => tomate [-1] => tomate [0] => tomate )
Errores de tipo consistentes para funciones internas
En versiones anteriores, en caso de error, las funciones internas y las creadas por el usuario se comportaban de manera distinta. Mientras las primeras devolvían un mensaje de advertencia o un valor nulo (null), las segundas lanzaban un mensaje TypeError.
var_dump(strlen(new stdClass)); // Warning: strlen() expects parameter 1 to be string, object given // NULL
Ahora, con la nueva versión, también las funciones internas son capaces de arrojar TypeErrors si no coinciden los tipos de parámetros al habilitar la opción strict_types.
declare(strict_types=1); var_dump(strlen(new stdClass)); // TypeError: strlen() expects parameter 1 to be string, object given
Constructores de clases simplificados
La nueva versión introduce una sintaxis abreviada, que para evitar repetir las propiedades de una clase como antiguamente, ahora permite combinar la definición de propiedades y el constructor.
De esta manera, lo que antes se tenía que realizar así:
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 ; } }
Ahora puede ser resulto de una forma más simplificada:
class Point { public function __construct ( public float $ x = 0.0 , public float $ y = 0.0 , public float $ z = 0.0 , ) { } }
Interfaz encadenable
Se ha introducido una nueva interfaz “encadenable” que es añadida automáticamente a clases que implementen el método __toString(). Esta medida tiene, sobre todo, la finalidad de poder usar el tipo de unión string|stringable.
Herencia de métodos privados
Antes, por lo general, PHP aplicaba las mismas comprobaciones de herencia para métodos públicos, protegidos y privados. Sin embargo, esto presentaba problemas ya que las clases secundarias no pueden acceder a métodos privados. En la nueva versión, se resuelve esta cuestión, deshabilitando la comprobación de herencia en este tipo de métodos.
Conversión de objetos DateTime
Los desarrolladores han agregado las opciones DateTime::createFromInterface() y DatetimeImmutable::createFromInterface() para que sea más sencillo convertir un DateTime en DateTimeInmutable o viceversa.
DateTime::createFromInterface(DateTimeInterface $other); DateTimeImmutable::createFromInterface(DateTimeInterface $other);
Expresiones Throw
Como ‘throw’ era una declaración, antes su uso estaba limitado a un reducido número de situaciones. Ahora, PHP 8, la convierte en expresión para que pueda ser utilizada en contextos diferentes y no soportados en sus versiones anteriores. En la RFC, nos ponen el siguiente ejemplo:
// Operadores de flecha $callable = fn() => throw new Exception(); // Operadores de coalescencia nula $value = $nullableValue ?? throw new InvalidArgumentException(); // Operadores ternarios $value = $falsableValue ?: throw new InvalidArgumentException(); // $value sólo se establecerá si la array no está vacía $value = !empty($array) ? reset($array) : throw new InvalidArgumentException();
Ahora se puede usar ::class en los objetos
En versiones anteriores, sólo se podía utilizar get_class() para buscar el nombre de una clase, pero ahora se puede aplicar ::class también a los objetos para lograr el mismo resultado. En caso de que una variable no corresponda a una clase, se obtendrá un TypeError.
$object = new stdClass; var_dump($object::class); // "stdClass" $object = null; var_dump($object::class); // TypeError
Nuevos atributos
Los atributos, llamados anotaciones en otros lenguajes, permiten especificar las propiedades de los objetos, elementos o archivos. Antes los comentarios eran la única manera de añadir metadatos, pero, en PHP 8, estos atributos plantean otra posibilidad. En la nueva versión, pueden utilizarse en funciones, clases, interfaces, constantes, propiedades, métodos y parámetros de funciones. Su sintaxis abre con “<<” y se cierra con “>>”, como se puede ver en el siguiente ejemplo:
<<ExampleAttribute>> class Foo { <<ExampleAttribute>> public const FOO = 'foo'; <<ExampleAttribute>> public $x; <<ExampleAttribute>> public function foo(<<ExampleAttribute>> $bar) { } } $object = new <<ExampleAttribute>> class () { }; <<ExampleAttribute>> function f1() { } $f2 = <<ExampleAttribute>> function () { }; $f3 = <<ExampleAttribute>> fn () => 1;
Tipos de retorno estáticos
La nueva versión convierte a ‘static’ en un tipo de retorno válido, junto a ‘self’ y ‘parent‘.
class Foo { public function test(): static { return new static(); } }
La extensión JSON
Debido a que el uso de la extensión JSON es bastante común, los desarrolladores de PHP lo han habilitado de manera permanente. Si antes era posible compilar sin hacer uso de ella, ahora no hay otra alternativa.
Mejoras en la concatenación
Ahora PHP se comportará de manera más lógica ante múltiples operadores.
echo "sum: " . $a + $b;
En versiones anteriores, PHP interpretaría este ejemplo de la siguiente forma:
echo ("sum: " . $a) + $b;
Sin embargo, en PHP 8, se resuelve así:
echo "sum: " . ($a + $b);
Nuevas funciones
str_contains()
Se ha incluido una nueva función para que sea más sencillo buscar una determinada secuencia dentro de una cadena. Las versiones anteriores incluían strstr y strpos con esta finalidad, pero eran bastante más complejas que la nueva str_contains y, además, tenían el problema de ser bastante exquisitas con el uso de mayúsculas y minúsculas.
str_contains("abc", "a"); // true str_contains("abc", "d"); // false
str_starts_with() y str_ends_with
Se han agregado dos funciones similares que permiten conocer el inicio y el final de una cadena más fácilmente.
str_starts_with('haystack', 'hay'); // true str_ends_with('haystack', 'stack'); // true
get_debug_type()
Ahora podemos usar una nueva función para obtener el tipo de una variable y simplifica mucho más el procedimiento. Puede aplicarse para obtener errores más específicos como en el siguiente ejemplo:
$bar = $arr['key']; if (!($bar instanceof Foo)) { // En versiones anteriores throw new TypeError('Expected ' . Foo::class . ' got ' . (is_object($bar) ? get_class($bar) : gettype($bar))); } // En PHP 8 if (!($bar instanceof Foo)) { throw new TypeError('Expected ' . Foo::class . ' got ' . get_debug_type($bar)); } $bar->someFooMethod();
f_div()
La nueva función f_div() es parte de la familia intdiv() y fmod() ya que hace algo similar. Las divisiones con cero como divisor serán consideradas como bien definidas y se devolverán los valores INF, -INF o NAN dependiendo del caso.
get_resource_id()
En ocasiones, hay que acceder a recursos externos al código, como en el caso de una conexión MySQL. Cada uno de esos recursos tiene un identificador. Anteriormente, podíamos averiguarlo de una forma más compleja:
$resourceId = (int) $resource;
PHP 8 permite obtener este resultado más fácilmente.
$resourceId = get_resource_id($resource);