Todo lo nuevo de PHP 8

Todo lo nuevo de PHP 8

PHP es lenguaje que recientemente ha cumplido 25 años y no se puede negar lo mucho que ha madurado con el tiempo. En este artículo quiero mostrarte todo lo nuevo de PHP 8.

Ya falta muy poco para conocer todo lo nuevo de PHP 8, se espera que sea liberada en Diciembre de 2020, es por ello que he querido preparar este artículo en el que resumiré cuales son las mejoras y nuevas características que traerá consigo muy pronto.

Vale la pena destacar, que toda esta información ha sido obtenida durante sus fases de desarrollo por lo que si llega haber un cambio posterior a la publicación de este post, lo estaré actualizando. Te recomiendo estes pendiente de esta entrada por si hay novedades al respecto.

Indice de todo lo nuevo de PHP 8

JIT (RFC)

JIT, ó Just In Time por sus siglas, es un compilador que promete significantes mejoras de rendimiento, aunque no siempre dentro del contexto de peticiones web.

Con el compilador JIT activado, el código no sería ejecutado por el VM de Zend, sino directamente por el CPU, lo que supondría una mejora notable en la velocidad de cálculo.

Las mejoras que se obtendrían según Nikita Popov son:

  • Un rendimiento significativamente mejor para el código numérico.

  • Un rendimiento ligeramente mejor para el código «típico» de una aplicación web PHP.

  • El potencial para mover más código de C a PHP, porque PHP será ahora suficientemente rápido.»

Mejoras y nuevas características

Union Types (RFC)

Existen muchos casos en donde es muy útil manejar varios tipos de parámetros utilizando los union types. Bien sea en la definición de los parámetros de entrada, como en los parámetros de retorno de una función

public function foo(Foo|Bar $input): int|float;

Nótese también que el tipo void para métodos que no retornan valores, también puede ser parte de los union types.

Además, las uniones anulables se pueden escribir usando |null, o usando la existente notación ?:

public function foo(Foo|null $foo): void;

public function bar(?Bar $bar): void;

Attributes (RFC)

Los atributos, o comúnmente conocidos como anotaciones en otros lenguajes, ofrecen una vía para añadir o especificar mediante metadatos, las propiedades de una clase, sus propiedades y funciones, sin necesidad de crear los docblock (Bloques de documentación).

Un ejemplo de cómo lucirán los atributos en una clase:

use App\\Attributes\\ExampleAttribute;

<<ExampleAttribute>>
class Foo
{
    <<ExampleAttribute>>
    public const FOO = 'foo';

    <<ExampleAttribute>>
    public $x;

    <<ExampleAttribute>>
    public function foo(<<ExampleAttribute>> $bar) { }
}

<<Attribute>>
class ExampleAttribute
{
    public $value;

    public function __construct($value)
    {
        $this->value = $value;
    }
}

Constructor property promotion

Esta nueva característica es una de las que más me gusta. Personalmente utilizo Value Objects y DTO’s (Data Transfer Objects ó Objetos de Transferencia de Datos) en mis proyectos como parte de los principios de Domain Driven Design y esta novedad potencia aún más su uso.

En resumen, “property promotion” te permite combinar la definición de propiedades de una clase, la definición del constructor y la asignación de las respectivas variables a cada propiedad, todo en una sola sintaxis en la lista de parámetros del constructor.

Por lo cual, en vez de hacer esto:

class CustomerDTO
{
    public string $name;

public string $email;

public DateTimeImmutable $birth_date;

public function __construct(
        string $name, 
        string $email, 
        DateTimeImmutable $birth_date
    ) {
        $this->name = $name;
        $this->email = $email;
        $this->birth_date = $birth_date;
    }
}

Podrás escribirlo de esta forma:

class CustomerDTO
{
    public function __construct(
        public string $name, 
        public string $email, 
        public DateTimeImmutable $birth_date,
    ) {}
}

Con esta nueva característica, vienen algunas consideraciones importantes:

Solo en constructores

“Promoted properties” pueden ser usadas solo en constructores, esto puede que sea obvio para muchos pero nunca esta demás dejarlo en claro.

No se permiten duplicados

No tendrás permitido declarar propiedades de una clase y “promoted properties” por lo que esto generará un error:

class MyClass
{
    public string $a;

public function __construct(
        public string $a,
    ) {}
}

Propiedades no tipadas están permitidas

Esta permitido utilizar propiedades no tipadas, aún cuando es recomendable y buena práctica mantener nuestras propiedades tipadas:

class MyDTO
{
    public function __construct(
        public $untyped,
    ) {}
}

Valores por defecto simples

“Promoted properties” pueden tener valores por defecto, pero expresiones como new … no están permitidas, por lo que el siguiente ejemplo nos arrojaría un error:

public function __construct(
    public string $name = 'Brent',
    public DateTimeImmutable $date = new DateTimeImmutable(),
) {}

Combinar “promoted properties” y propiedades normales

No todos los constructores deben usar “promotion properties” por lo que puedes hacer un mix de la siguiente forma.

class MyClass
{
    public string $b;

public function __construct(
        public string $a,
        string $b,
    ) {
        $this->b = $b;
    }
}

Lo que si es que hay que tener cuidado de mezclar estas sintaxis, si notas que el código se torna difícil de leer, lo mejor es utilizar constructores normales en su lugar.

Acceso a “promoted properties” desde el cuerpo del constructor

Esta permitido leer los “”promoted properties en el cuerpo del constructor, esto es útil si necesitas añadir validación extra. Puedes utilizar ambas, la variable local y la variable de instancia, ambas funcionan bien.

public function __construct(
    public int $a,
    public int $b,
) {
    assert($this->a >= 100);

if ($b >= 0) {
        throw new InvalidArgumentException('…');
    }
}

Comentarios en promoted properties

Puedes añadir bloques de comentarios en las promoted properties, las cuales estarán disponibles vía reflection:

class MyClass 
{
    public function __construct(
        /** @var string */public $a,
    ) {}
}

$property = new ReflectionProperty(MyClass::class, 'a');

$property->getDocComment(); // "/** @var string */"

Atributos

Tal como los bloques de comentarios, los atributos son permitidos en las promoted properties, este ejemplo:

class MyClass
{
    public function __construct(
        <<MyAttribute>>
        public $a,  
    ) {}
}

Será convertido a:

class MyClass 
{
    <<MyAttribute>>
    public $a;

    public function __construct(
        <<MyAttribute>>
        $a,
    ) {
        $this->a = $a;
    }
}

No esta permitido usarlas en constructores abstractos

El siguiente ejemplo nos arrojaría un error:

abstract class A
{
    abstract public function __construct(
        public string $a,
    ) {}
}

Esta permitido en Traits

No solo esta permitido en clases si no que también en Traits:

trait MyTrait
{
    public function __construct(
        public string $a,
    ) {}
}

var no está soportado

Solo public, protected y private son las únicas palabras válidas, el siguiente ejemplo nos arrojaría un error:

public function __construct(
    var string $a,
) {}

Variadic parameters no pueden ser promovidos

El siguiente ejemplo nos arrojará un error:

public function __construct(
    public string ...$a,
) {}

Reflection para isPromoted

Ambos, ReflectionProperty y ReflectionParameter poseen un nuevo método isPromoted para verificar si una propiedad o método de una clase es promovida

Herencia

Dado que los constructores PHP no necesitan seguir la declaración de su constructor padre, hay poco que decir: se permite la herencia. Sin embargo, si necesita pasar propiedades del constructor secundario al constructor primario, deberá pasarlas manualmente:

class A
{
    public function __construct(
        public $a,
    ) {}
}

class B extends A
{
    public function __construct(
        $a,
        public $b,    
    ) {
        parent::__construct($a);
    }
}

Nuevas funciones en PHP

Nuevo tipo de retorno static (RFC)

Mientras que era posible retornar self, static no era un tipo de retorno válido hasta PHP 8:

class Foo
{
    public function test(): static
    {
        return new static();
    }
}

Nuevo tipo mixed (RFC)

Algunos podrían creer que es un mal necesario: el tipo “mixto” hace que muchos tengan sentimientos encontrados. Sin embargo, hay un muy buen argumento para hacerlo: un tipo faltante puede significar muchas cosas en PHP:

  • Una función no devuelve nada o es nula

  • Estamos esperando uno de varios tipos

  • Esperamos un tipo que no pueda ser insinuado en PHP

Debido a las razones antes mencionadas, es bueno que el tipo mixed sea añadido. mixed en si mismo significa uno de estos tipos:

  • array

  • bool

  • callable

  • int

  • float

  • null

  • object

  • resource

  • string

Note that mixed can also be used as a parameter or property type, not just as a return type.

Also note that since mixed already includes null, it's not allowed to make it nullable. The following will trigger an error:

Ten en cuenta que mixed también se puede usar como parámetro o tipo de propiedad, no solo como tipo de retorno.

También tenga en cuenta que dado que mixed ya incluyenull, no está permitido hacerlo anulable. Lo anterior arrojaría un error como este:

// Fatal error: Mixed types cannot be nullable, null is already part of the mixed type.function bar(): ?mixed {}

Throw expression (RFC)

Este RFC cambia el throw de ser un statement a ser una expresión, lo cual hace posible arrojar una excepción en multiples lugares:

$triggerError = fn () => throw new MyError();

$foo = $bar['offset'] ?? throw new OffsetDoesNotExist('offset');

Permitiendo ::class en objetos (RFC)

Una pequeña pero super útil nueva característica: Ahora es posible usar ::class en objetos, en lugar de tener que usar la función get_class() sobre ellos. Esta nueva característica funciona igual a get_class().

$foo = new Foo();

var_dump($foo::class);

Catches sin captura (RFC)

Cada vez que se requería capturar una excepción antes de PHP 8, se debía almacenar en una variable, independientemente de si usaba esa variable o no. Con Catches sin captura, se puede omitir la variable, por lo que en lugar de esto:

try {
    // Something goes wrong
} catch (MySpecialException $exception) {
    Log::error("Something went wrong");
}

Podremos hacer esto:

try {
    // Something goes wrong
} catch (MySpecialException) {
    Log::error("Something went wrong");
}

Ten en cuenta que es necesario especificar siempre el tipo, no puede tener un catch vacío. Si desea capturar todas las excepciones y errores, puede usar Throwable como tipo de captura.

Coma final en listado de parámetros (RFC)

Ya posible cuando se llama a una función, todavía faltaba el soporte de coma final en las listas de parámetros. Ahora está permitido en PHP 8, lo que significa que puede hacer lo siguiente:

public function(
    string $parameterA,
    int $parameterB,
    Foo $objectfoo,
) {
    // …
}

De cierta forma, esto se asemeja a la definición de arrays en donde sí podemos colocar una coma en el ultimo elemento definido.

Crear objetos DateTime desde interfaz

Ya puede crear un objeto DateTime a partir de un objeto DateTimeImmutable utilizando DateTime::createFromImmutable($immutableDateTime). Al agregar DateTime::createFromInterface() y DatetimeImmutable::createFromInterface() ahora hay una forma generalizada de convertir los objetos DateTime y DateTimeImmutable entre sí.

DateTime::createFromInterface(DateTimeInterface $other);

DateTimeImmutable::createFromInterface(DateTimeInterface $other);

Nueva interfaz Stringable (RFC)

La interfaz Stringable se puede usar para escribir una sugerencia de cualquier cosa que sea una cadena o implemente __toString(). Además, cada vez que una clase implementa __toString(), implementa automáticamente la interfaz detrás de escena y no hay necesidad de implementarla manualmente.

class Foo
{
    public function __toString(): string
    {
        return 'foo';
    }
}

function bar(Stringable $stringable) { /* … */ }

bar(new Foo());
bar('abc');

Mejoras de métodos abstractos en Traits (RFC)

Los Traits pueden especificar métodos abstractos que deben ser implementados por las clases que los usan. Sin embargo, hay una advertencia: antes de PHP 8, la firma de estas implementaciones de métodos no estaba validada. El siguiente ejemplo era válido:

trait Test {
    abstract public function test(int $input): int;
}

class UsesTrait
{
    use Test;

public function test($input)
    {
        return $input;
    }
}

PHP 8 realizará la validación adecuada de la firma del método cuando use un Trait e implemente sus métodos abstractos. Esto significa que deberás escribir esto en su lugar:

class UsesTrait
{
    use Test;

public function test(int $input): int
    {
        return $input;
    }
}

Implementación de Objeto token_get_all() (RFC)

La función token_get_all() devuelve un array de valores. Este RFC agrega una clase PhpToken con un método PhpToken::getAll(). Esta implementación funciona con objetos en lugar de valores simples. Consume menos memoria y es más fácil de leer.

ext-json siempre disponible (RFC)

Anteriormente era posible compilar PHP sin la extensión JSON activa, esto ya no es posible nunca más. Desde que JSON es usado tan ampliamente, es mejor que los desarrolladores puedan siempre confiar en que este siempre allí, en vez de tener que asegurarse primero de que la extensión exista.

Nuevas funciones en PHP

Función str_contains() (RFC)

Algunos podrían decir que esto era posible desde hace mucho tiempo, pero finalmente ya no tenemos que depender de strpos para saber si una cadena contiene otra cadena.

En lugar de hacer esto:

if (strpos('string with lots of words', 'words') !== false) { 
    /* … */ 
}

Podremos hacer esto:

if (str_contains('string with lots of words', 'words')) { 
    /* … */ 
}

Parece algo tonto o sencillo, pero cosas como estas hacen falta en todos los lenguajes, son como especies de helpers que simplifican y optimizan el proceso de desarrollo.

Funciones str_starts_with() y str_ends_with() (RFC)

Estas dos maravillosas funciones han sido añadidas al core de PHP. Un ejemplo vale más que 1000 palabras para describir lo que hacen:

str_starts_with('haystack', 'hay'); // true
str_ends_with('haystack', 'stack'); // true

Función fdiv()

La nueva función fdiv() realiza algo similar a las funciones fmod() y intdiv(), lo cual permite la división por 0. En lugar de errores obtendremos INF, -INF ó NAN, dependiendo del caso.

Función get_debug_type()(RFC)

get_debug_type() retorna el tipo de una variable. Suena como algo que ya gettype() hace?,get_debug_type() retorna un output para arrays, strings, clases anónimas y objetos, mas útil.

For ejemplo, invocar gettype() en la clase \\Foo\\Bar podría retornar object. Usar get_debug_type() retornará el class name.

Función get_resource_id()

Los recursos son variables especiales en PHP, que se refieren a recursos externos. Un ejemplo es una conexión MySQL, otro un identificador de archivo.

A cada uno de esos recursos se le asigna una ID, aunque anteriormente la única forma de saber esa identificación era enviar el recurso a int:

$resourceId = (int) $resource;

PHP 8 añade la función get_resource_id(), haciendo esta operación más obvia y type-safe:

$resourceId = get_resource_id($resource);

Es todo por ahora, recuerda estar pendiente de este post, ya que si aparecen nuevas características, trataré de actualizar el post con lo nuevo.

Recuerda que si tienes alguna sugerencia o pregunta, no dudes en dejar tus comentarios al final del post.

Si te gustó este post, ayúdame a que pueda servirle a muchas más personas, compartiendo mis contenidos en tus redes sociales.

Espero que este post haya sido de gran ayuda para ti, y como siempre, cualquier inquietud o duda que tengas, puedes contactarme por cualquiera de las vías disponibles, o dejando tus comentarios al final de este post. También puedes sugerir que temas o post te gustaría leer a futuro.

Did you find this article valuable?

Support Francisco Ugalde by becoming a sponsor. Any amount is appreciated!