Las Nuevas Características de PHP 8 - Parte 2

Ronaldo B.27 de julio de 202213 min de lectura

En este artículo

Este es el segundo artículo de nuestra serie sobre las novedades de PHP 8. En el primer artículo hablamos sobre 8 novedades de esta versión. Puedes acceder a ese artículo haciendo clic aquí.

En este artículo hablaremos sobre 8 novedades más de la versión. ¿Estás preparado? ¡Vamos a más novedades!

Nuevo Operador Nullsafe

Las últimas versiones de PHP han agregado varios operadores, que nos han ayudado en varias situaciones. En la versión 8 tendremos la implementación de un operador que también será muy útil. Hablamos del operador Nullsafe.

Un excelente hábito que hemos desarrollado cuando comenzamos a madurar como programadores es siempre ejecutar algún código después de verificar que su expresión no es nula. Generalmente hacemos esto usando un if() verificando si esa variable es diferente de NULL. Eso es genial; pero si las verificaciones son demasiado extensas, nuestro código puede volverse verboso y repetitivo. Veamos un ejemplo de esto.

Imagina que poseemos una clase de ciudad. Tendrá el siguiente contenido:

class City {

    public function __construct(
        public string $name,
        public string $state
    ) {

    }

    public function set_name($new_name) {
        $this->name = $new_name;
        return $this;
    }

    public function get_name() {
        return $this;
    }

    public function get_uppercase() {
        return strtoupper($this->name);
    }

}

Vamos a instanciar una nueva instancia de esta clase con el siguiente código:

$city = new City("Sao Bernardo do Campo", "SP");

Ahora imagina que deseamos recuperar el valor del nombre de la ciudad, definir un nuevo nombre y luego mostrar ese nuevo nombre en letras mayúsculas, pero siempre verificando que las propiedades no sean nulas. Podríamos hacer esto con el siguiente código:

if ($city !== NULL) {

    $name = $city->get_name();

    if ($name !== NULL) {

        $city->set_name("Sao Paulo");

        $new_name = $city->get_uppercase();

        if ($new_name !== NULL) {

            echo $new_name;

        }

    }

}

Este código funcionaría. Sin embargo, se volvió un poco repetitivo, ¿verdad? En esta situación, el nuevo operador Nullsafe puede ayudarnos. Nos permite verificar si el resultado de los métodos o propiedades es nulo, simplemente añadiendo un signo de interrogación (?) antes del símbolo “->”. Esto significa que podríamos reescribir el código anterior con la siguiente línea:

echo $city?->get_name()?->set_name('Sao Paulo')?->get_uppercase();

¡Vaya, mucho más simple, verdad?

Ten en cuenta que este operador se utilizará principalmente en llamadas a métodos encadenados. Así que, si deseas utilizarlo, puede que sea necesario adaptar tus clases y objetos para que funcionen de esta manera.

El operador Nullsafe también cancela la ejecución de los métodos si alguno de ellos devuelve un valor nulo. Esto puede ayudarnos con el rendimiento de nuestro servidor. Imagina que algún método en nuestro proyecto consume mucho procesamiento o memoria del servidor. Si algún otro método antes de él devuelve NULL, este método no se ejecutará, lo que ahorrará recursos del servidor. Este concepto se llama “short circuiting” (circuito corto).

Nuevo Tipo de Retorno - static

Para hablar sobre esta novedad, necesitamos recordar el concepto de dos palabras clave muy utilizadas cuando trabajamos con Orientación a Objetos en PHP: static y self. Ambas se utilizan para hacer referencia a métodos y atributos ya declarados en las clases, pero con una pequeña diferencia. Para poder visualizar esta diferencia, es necesario aplicar el concepto de herencia en la Orientación a Objetos. Veamos un ejemplo de esto.

Vamos a crear dos clases, una llamada First y la otra llamada Second. Tendrán el siguiente código:

class First {

    public static function class_name() {
        return __CLASS__;
    }

    public static function show() {
        return self::class_name();
    }

}

class Second extends First {

    public static function class_name() {
        return __CLASS__;
    }

}

Explicando: la segunda clase extiende de la primera. Ambas tienen el mismo método llamado class_name(). Este método devuelve el nombre de la clase en la que fue creado. También tenemos el método show(), que es el responsable de invocar el método class_name(). En este método es donde vamos a concentrar nuestra atención. Ten en cuenta que estamos usando la palabra self para referirnos al método class_name().

Realizaremos una prueba. Haremos la llamada al método show() en la clase Second, de esta manera:

echo Second::show();

Este método fue heredado de la clase First. Ahora tenemos la duda: “¿Cuál es el nombre de la clase que se mostrará en el navegador?” Tal vez pensemos: “De la segunda clase. Después de todo, llamamos al método show() desde ella”. Cuando accedemos al navegador, vemos el siguiente resultado:

Resultado de la llamada show() en el navegador

¡Observa que la clase mostrada fue la primera! Esto ocurre debido a self. Hace referencia a los métodos de la clase “original”, es decir, la clase donde el método fue creado inicialmente. Si deseamos mostrar el nombre de la segunda clase, tendríamos que cambiar self por static en el método show():

public static function show() {
    return static::class_name();
}

El static hace referencia al método de la clase que fue llamada. Realiza esta referencia en tiempo de ejecución. Así, al acceder al navegador nuevamente, veremos el siguiente resultado:

Resultado del static en el método show()

Este concepto se llama “Late Static Bindings” (LSB) o “Vinculaciones Estáticas Tardías”.

Está bien, pero ¿por qué hablamos de todo este concepto? Hicimos esto porque la novedad de la que estamos hablando ahora involucra la aplicación del concepto de LSB, pero ahora orientado al retorno de los métodos. De esta forma, si tenemos algún método que debe retornar la instancia de la clase, podemos informar el static, no solo el self. Vamos a adaptar el último ejemplo para ver la aplicación de esta novedad en la práctica. Nuestras clases ahora quedarán así:

class First {
    public function show($class_name): self {
        return $class_name;
    }
}

class Second extends First {
    public function show($class_name): self {
        return $class_name;
    }
}

class Third extends Second {}

Observa que creamos una tercera clase, llamada Third, que extenderá de Second. Además, ten en cuenta que el método show() en las clases First y Second retornará el nombre de la clase que reciba como parámetro, pero este retorno debe ser la palabra self, es decir, la clase donde se creó el método.

Vamos a instanciar la clase Third e informar como parámetro la clase Second para el método show():

$third = new Third();

$third->show(new Second);

Este código funcionará, ya que usamos self en el método show(). Ahora, vamos a agregar la palabra static al método en la clase Second, de esta forma:

class Second extends First {
    public function show($class_name): static {
        return $class_name;
    }
}

Ahora, al ejecutar el mismo código, veremos el siguiente resultado:

Error retornado en nuestro navegador

Vemos que se retornó un error en el navegador, indicando que el retorno debería ser una instancia de la clase Third. Por lo tanto, para que el código vuelva a funcionar, necesitamos dejarlo así:

$third->show(new Third);

Tal vez nos preguntemos: “¿Cuál es la ventaja de esta novedad? ¿Por qué se implementó?” Garantiza que el retorno de los métodos creados en nuestras clases sea del tipo que deseamos, lo que nos da un mayor control sobre nuestro código.

Este es un concepto complejo, que puede confundirnos cuando lo escuchamos por primera vez, pero a medida que lo vemos en la práctica se vuelve más natural para nosotros. ¿

Mixed Type

En el último artículo hablamos sobre la novedad “Union Types” que se implementará en PHP 8 y permitirá que definamos más de un tipo para una variable o retorno de métodos en nuestros proyectos.

Usando el concepto de este nuevo recurso, también se añadirá a PHP 8 el recurso de Mixed Type, o Tipos Mixtos. Usando la palabra reservada mixed podremos referirnos a una serie de tipos. Esto ya existía en varias IDEs o también en PHPDoc, pero no existía de forma nativa en PHP.

Mixed engloba los tipos array, bool, callable, int, float, null, object, resource y string, todos resumidos en una única palabra. Veamos un ejemplo de este recurso.

Vamos a crear una nueva clase, que tendrá la siguiente estructura:

class Person {

    public mixed $info;

    public function set_info(mixed $info) {

        $this->info = $info;

    }

}

Con el código anterior podemos informar cualquier tipo de dato al método set_info(). Por ejemplo:

$glaucio = new Person();

$glaucio->set_info('Glaucio Daniel');

En este código informamos una cadena, definiendo el nombre de la persona. Pero si deseamos informar su edad, también es posible:

$glaucio->set_info(25);

Puse la edad de 25 para que Glaucio esté feliz ?.

Este es un recurso muy interesante, pero es necesario mencionar algunos puntos importantes sobre él.

Por ejemplo, el Mixed Type solo puede usarse solo. Después de todo, engloba todos los tipos. Esto significa que el código a continuación no funcionaría:

function hcode(mixed | string $name) {}

No puede usarse en conjunto con otro tipo.

El Mixed Type también engloba el tipo null. De esta forma, no podemos usarlo de manera anulable:

function hcode(?mixed $name) {}

Este código tampoco funcionaría.

Podemos usar mixed también como retorno de métodos. Sin embargo, al informarlo como retorno, estaremos obligados a retornar alguna información. Veamos, por ejemplo, el siguiente código:

function sum($num1, $num2): mixed {

    echo $num1 + $num2;

}

Ten en cuenta que la función sum() mostrará el resultado de la operación en el navegador. Sin embargo, no está retornando ese valor. Por lo tanto, al llamar a esta función, veremos el siguiente resultado:

Error retornado en el navegador

Se devuelve un error fatal. Lo correcto es que la función esté así:

function sum($num1, $num2): mixed {

    return $num1 + $num2;

}

Ahora, al llamar al método nuevamente, ¡veremos el resultado correcto!

Algo interesante sobre el Mixed Type es que los métodos lo adoptarán por defecto en los parámetros para los que no se informe ningún tipo específico. La gran ventaja de este nuevo recurso es dejar claro lo que estamos esperando de retorno en los métodos o lo que esperamos de los argumentos que vamos a informar a los métodos. Una vez más vemos un avance en la madurez de PHP con este nuevo recurso.

Weak Maps

La próxima novedad es una nueva clase, llamada WeakMap. Esta clase permitirá que creemos una referencia para un objeto, definiendo que cuando este objeto sea eliminado, su referencia también será eliminada. ¡Veamos esto en la práctica!

Vamos a crear un array llamado $years y añadir un valor en él, informando su clave a través de una variable. El código quedará así:

$years = [];

$key = "year";

$years[$key] = 1968;

var_dump($years);

Al acceder al navegador, veremos el siguiente resultado:

Array retornado en nuestro navegador

¡Muy bien! El array fue correctamente retornado.

La clave “year” está definida a través de la variable $key. ¿Qué sucede si la eliminamos? Vamos a hacer esta prueba añadiendo el siguiente código:

echo "<br/>";

unset($key);

var_dump($years);

Al acceder al navegador nuevamente, vemos lo siguiente:

Arrays siendo retornados en el navegador

El array fue mostrado una vez más. Observa que la variable fue eliminada, pero su referencia, o mapeo, sigue igual. Ahora aplicaremos la misma lógica usando la nueva clase WeakMap. El código quedará así:

$years = new WeakMap;

$key = (object) "year";

$years[$key] = 1968;

var_dump($years);

echo "<br/>";

unset($key);

var_dump($years);

Ten en cuenta que la variable $years ahora almacena una nueva instancia de la clase WeakMap. Además, fue necesario convertir la cadena en la variable $key a un objeto, ya que solo este tipo de dato es válido para la clase WeakMap. Ahora, al probar el mismo código en el navegador, veremos este resultado:

Nuevo resultado de nuestro código en el navegador

Ten en cuenta que ahora el objeto fue retornado solo en el primer var_dump(). En el momento en que eliminamos la variable $key, su referencia también fue borrada. Este recurso nos ayudará a ahorrar memoria en nuestro servidor, lo que mejorará el rendimiento de nuestras aplicaciones.

Usando ::class en Objetos

La palabra reservada ::class puede ser utilizada dentro de las clases, para referirse a su nombre. Sin embargo, muchos programadores esperaban poder hacer uso de ella también en los objetos que instancian las clases. Esta espera ha terminado. A partir de la versión 8 de PHP, podremos usar esta palabra también fuera de las clases. ¡Vamos a crear un ejemplo!

Vamos a crear una clase Company, con el siguiente código:

class Company {

    public function index() {

        return 'Hcode Treinamentos';

    }

}

$hcode = new Company();

Hasta PHP 7, si deseábamos ver el nombre de la clase, necesitaríamos usar el siguiente código:

var_dump(get_class($hcode));

Ahora, a partir de la versión 8, podremos hacer lo mismo con el siguiente código:

var_dump($hcode::class);

De esta manera es mucho más simple. Esta es una excelente novedad, ¿no crees?

Mejoras en los Métodos Abstractos de Traits

Trait es un recurso que se añadió en PHP en la versión 5.4 y nos permite aislar partes de nuestro código y reutilizarlas en tantas clases como deseemos. Es un recurso muy útil, pero se ha identificado un pequeño “problema” hace algún tiempo, relacionado con los métodos abstractos creados dentro de un Trait. Vamos a crear un ejemplo para ver este problema.

Crearemos un Trait llamado Computer y una clase llamada Notebook, que hará uso de él. Nuestro código quedará así:

trait Computer {

    abstract public function set_processor($processor): int;

}

class Notebook {

    use Computer;

    public function set_processor($processor): string {

    }

}

¿Notas algo extraño? Observa que el método set_processor() fue redefinido en la clase Notebook, pero informando un tipo de retorno diferente. Si estuvieras trabajando con una interfaz, este código no funcionaría, pero con el Trait sí. Pensando en esta cuestión, a partir de PHP 8 la firma de un Trait está un poco más específica y ahora la validación de este recurso también involucra el retorno de los métodos. Por lo tanto, si probamos el código anterior con PHP 8, veremos el siguiente resultado:

Nuevo mensaje de error al trabajar con Traits

Coma en la Última Posición en la Pasada de Parámetros

Para hablar sobre la próxima novedad, necesito hacerte una pregunta: “¿Agregas una coma después del último parámetro informado para una función o método?” Muchos programadores hacen esto. PHP ya permite que informemos esta coma final cuando estamos definiendo los argumentos para alguna clase ya creada. Por ejemplo:

new Address(
    "Avenida Paulista",
    260,
    "São Paulo",
    "SP",
    "Brasil",
);

Observa que después del argumento “Brasil” hay una coma. Este código funciona sin problemas. Sin embargo, imagina que queremos hacer lo mismo al crear la clase, de esta forma:

class Address {

    public function __construct(
        public string $street,
        public string $number,
        public string $city,
        public string $state,
        public string $country,
    ) {

    }

}

Ten en cuenta que también añadimos una coma después del parámetro $country. Sin embargo, actualmente, este código no funcionaría, incluso el Editor de Texto devolvería un error. ¿No es un poco extraño? Es probable que ya te hayas encontrado con algún problema en este contexto.

La buena noticia es que, visando la uniformidad de PHP, a partir de la versión 8 esta sintaxis funcionará también. Por lo tanto, tendremos un dolor de cabeza menos a partir de ahora ?.

Nueva Interface - Stringable

Esta nueva interfaz usará el nuevo recurso de Union Types para permitir el uso de una cadena o un objeto Stringable en el método mágico __toString() en nuestras clases. Según Nicolas Grekas, autor de este nuevo recurso e ingeniero en el framework Symfony, uno de los objetivos es permitir una actualización directa de PHP 7 a 8.

Conclusión

En este artículo revisamos 8 novedades más de PHP 8. Es posible notar cómo PHP está evolucionando y madurando cada vez más.

Mientras aguardamos el lanzamiento de esta versión, que probablemente ocurrirá el 26 de noviembre, no dejes de aprender el núcleo de este increíble lenguaje en nuestro Curso Completo de PHP 7.

Hasta el próximo artículo :)

Sigue aprendiendo con Hcode

Regístrate en la plataforma de cursos y sigue las próximas guías del blog.