Qué hay de nuevo en PHP 8 - Parte 1

Ronaldo B.27 de julio de 202210 min de lectura

En este artículo

PHP 8 tiene previsto su lanzamiento para el 26 de noviembre de este año y traerá muchas novedades que nos ayudarán y elevarán este lenguaje a otro nivel.

A través de esta publicación estamos iniciando una serie de artículos que hablarán sobre las novedades de esta versión mediante ejemplos prácticos. Algunas de las novedades son nuevos recursos, otras son mejoras en recursos ya existentes.

En este artículo hablaremos sobre las 8 primeras novedades.

Método constructor simplificado

Cuando trabajamos con Orientación a Objetos, aprendemos que el método constructor es uno de los más importantes en una clase. Y PHP 8 lo simplificará, para que no se vuelva redundante en el proceso de definición de los atributos en el momento en que se instancia el objeto. Veamos un ejemplo de esta cuestión.

Imagina que deseamos crear una clase Person que almacenará la información de nombre, edad y altura de una persona. Actualmente, con la versión 7.4 de PHP, podríamos crear esta clase con el siguiente código:

class Person {

    public string $name;
    public int $age;
    public float $height;

    public function __construct(
        string $name,
        int $age,
        float $height
    ) {

        $this->name = $name;
        $this->age = $age;
        $this->height = $height;

    }

}

Este código funcionaría sin errores. Sin embargo, ¿te diste cuenta de cómo repetimos el nombre de los atributos? Hicimos esto tres veces: en el momento de la creación de los atributos, en los parámetros del método constructor y en la definición de ellos en el cuerpo del método constructor. Es un poco redundante, ¿no crees? En la versión 8, podremos realizar este mismo proceso con el siguiente código:

class Person {

    public function __construct(
        public string $name,
        public int $age,
        public float $height
    ) {

    }

}

¡Mucho más simple! Nota que ahora definimos la visibilidad de los atributos en el momento en que definimos los parámetros del método constructor.

Es importante recordar que los atributos que se crearán automáticamente tendrán el mismo nombre que los parámetros informados. Además, esta funcionalidad tiene algunas excepciones. Este recurso solo funcionará en métodos constructores de clases no abstractas. Es decir, no será reconocido en funciones simples, en clases abstractas o en interfaces.

Tipos de Unión

El próximo recurso que analizaremos se llama Tipos de Unión. Su objetivo es permitir que PHP soporte varios tipos de datos en su estructura, no solo un valor único. Esto puede aplicarse a parámetros de métodos y también en el retorno de métodos en nuestras clases. Para definir los varios tipos que una variable puede recibir, basta usar el signo de | (pipe). Veamos un ejemplo.

Crearemos una clase Product que almacenará el valor del precio del producto. Este valor puede ser del tipo entero o decimal (float). La creación de esta clase, ya aplicando el nuevo concepto del método constructor simplificado, será así:

class Product {

    public function __construct(
        public int|float $price
    ) { }

}

De esta forma, si informamos un valor entero, como 15, o un valor decimal, como 18.5, será aceptado por PHP.

Este recurso también puede aplicarse a los parámetros de los métodos. Por ejemplo: podríamos tener esta misma lógica aplicada en un método set_price():

public function set_price(int|float $new_price) {
	$this->price = $new_price;
}

Podemos hacer esta definición incluso en el retorno de los métodos. Veamos cómo quedaría el método get_price():

public function get_price(): int|float {
	return $this->price;
}

En este ejemplo hemos añadido solo dos tipos posibles para los métodos, pero podríamos definir una cantidad mayor si lo deseáramos.

Puede haber algunas situaciones en las que este recurso puede devolvernos un error, principalmente cuando definimos alguna configuración redundante. Por ejemplo, no se recomienda repetir un mismo tipo en el mismo contexto. Imagina definir string|STRING en los parámetros de un método. Eso devolvería un error.

Asimismo, hay tipos que son parecidos. Ejemplo: si definimos el tipo bool|false|true, también veremos un error. Lo recomendado es definir solo bool en este caso para no tener problemas.

Cambios en la herencia de métodos privados

El encapsulamiento y la visibilidad también son conceptos muy importantes en la Orientación a Objetos. Sabemos que existen tres niveles de visibilidad en un método o atributo: público, protegido y privado. Los tres tipos pasan por la misma verificación cuando realizamos la herencia de métodos de una clase a otra. Sin embargo, los métodos privados no son heredados por las clases hijas. Por lo tanto, no parece tener mucho sentido realizar la misma verificación en los métodos de este tipo, ¿no es cierto? Pensando en eso, a partir de PHP 8 dicha verificación ya no ocurrirá más en esos métodos.

Mejora en la llamada de Exceptions

Hablemos ahora de algo a lo que los programadores están muy acostumbrados: errores ?. Generar Exceptions es un proceso que repetimos con cierta frecuencia, y a partir de la versión 8 veremos un pequeño cambio que hará que nuestro código sea un poco más simple. Para ver esto en la práctica, hagamos otro ejemplo.

Crearemos una función que multiplicará dos números. Si el resultado es menor que 10, se devolverá una Exception. La función será así:

function multiply_numbers($number1, $number2) {

    $result = ($number1 * $number2);

    if ($result < 10) {
        throw new Exception();
    }

}

Nótese que no definimos ningún mensaje en la llamada de la clase Exception. Hicimos esto porque deseamos retornar el error de forma estática, a través de un bloque try/catch. Si estuviéramos usando PHP 7.4, este código sería así:

try {
    multiply_numbers(2, 3);
} catch (Exception $error) {
    echo "Error en la multiplicación";
}

Realizamos un echo de un mensaje de error. Sin embargo, fue necesario definir una variable llamada $error, incluso si no la usamos en ningún lugar. Eso no parece tener mucho sentido, pero si no hiciéramos esta definición, el código no funcionaría. Por otro lado, usando PHP 8 podríamos crear el mismo código de la siguiente forma:

try {
    multiply_numbers(2, 3);
} catch (Exception) {
    echo "Error en la multiplicación";
}

Un poco más simple y menos confuso, ¿no crees?

Throw Expression

Aún hablando sobre errores, en la versión 8 veremos una evolución en la declaración de Exceptions. Como sabemos, podemos definir un throw new Exception en funciones y métodos. Sin embargo, no es posible realizar lo mismo en operadores ternarios o en funciones de flecha en PHP 7.

La buena noticia es que a partir de PHP 8 esto será posible. Veamos algunos ejemplos.

Imagina que deseamos crear una función de flecha que muestre la información de un error. En PHP 8 podremos crearla de la siguiente forma:

$show_error = fn($error) => throw new Exception($error);

$show_error("¡Error en PHP!!!");

Otra situación: imagina que poseemos una función que realiza una solicitud a alguna API externa. Si se devuelven datos de la API, los mostraremos. Si esto no ocurre, devolvemos una Exception. Podríamos usar un if ternario o el operador null coalescing para eso, de la siguiente forma:

$result = call_api();

return $result ?? throw new Exception("Error en la llamada a la API");

Nuestro código quedó bastante simple y fácil de entender, ¿no crees?

Error Fatal en la sobrescritura de métodos

En la Orientación a Objetos, un error bastante común que puede ocurrir es cuando sobrescribimos un método ya creado. Esto puede suceder en dos contextos: cuando implementamos una interfaz o cuando extendemos una clase de otra. Veamos un ejemplo de esta cuestión. Crearemos una interfaz llamada Automobile y una clase Car, que implementará la interfaz:

interface Automobile {

    public function show_details(array $details);

}

class Car implements Automobile {

    public function show_details(string $details) { }

}

new Car();

Nótese que tanto la interfaz como la clase tienen el método show_details(). Sin embargo, el tipo del parámetro esperado está diferente en ellos. Si estamos probando este código en PHP 7.4, veremos el siguiente resultado en el navegador:

Error en la interfaz Automobile y la variable Car

Nótese que se devolvió un error fatal debido a la sobrescritura del método. Veamos ahora qué ocurre cuando realizamos la misma sobrescritura mediante dos clases. El código será así:

class Location {

    public function show_details(array $details) {}

}

class City extends Location {

    public function show_details(string $details) {}

}

new City();

Al probar el código, veremos el siguiente resultado:

Advertencia sobre las clases City y Location

Observa que ahora se devolvió una Advertencia. Sin embargo, ¿no crees que el concepto de ambas situaciones es el mismo? Entonces, ¿por qué devolver dos mensajes diferentes para ellas? Pensando en eso, a partir de PHP 8 ambas situaciones devolverán un error fatal, estandarizando esta cuestión.

Arrays con índices negativos

¿Alguna vez pensaste en usar arrays con índices negativos? Bastante interesante, ¿no es cierto? A partir de la versión 8, PHP permitirá que iniciemos arrays usando índices negativos. Piensa, por ejemplo, en la función array_fill(). Crea un array para nosotros y espera tres parámetros: 1) el índice inicial del array, 2) la cantidad de posiciones que tendrá el array y 3) el valor que se añadirá repetidamente en cada posición del array. ¿Qué ocurriría si informamos la primera opción con un índice negativo? Hagamos esta prueba con el siguiente código:

$cars = array_fill(-5, 7, "Ferrari");

print_r($cars);

Si estamos usando PHP 7.4, veremos el siguiente resultado en el navegador:

Resultado del código PHP 7.4 en el navegador

Nótese que la primera posición del array fue -5, el índice inicial que informamos para la función. Sin embargo, los otros índices se definieron a partir de 0. ¿No tendría más sentido que continuaran a partir del -5? Veamos ahora el resultado que obtenemos cuando probamos el mismo código con PHP 8:

Resultado del código PHP 8 en el navegador

¡Excelente, ocurrió justamente lo que deseábamos!

Ajustes en la concatenación de strings y números

Hablaremos de uno de los primeros conceptos que aprendemos cuando estudiamos PHP y también uno de los conceptos que puede causarnos mucho dolor de cabeza: la concatenación. Sabemos que PHP es un lenguaje débilmente tipado y trata de convertir números en strings cuando usamos el operador . (punto) en nuestro código. Sin embargo, esto puede causarnos algunos problemas. Veamos un ejemplo.

Crearemos dos variables numéricas, realizaremos la suma de ellas y luego mostraremos el resultado en el navegador. Podemos hacer esto mediante el siguiente código:

$number1 = 10;
$number2 = 15;

echo "Resultado: " . $number1 + $number2;

Cuando probamos este código, vemos el siguiente resultado:

Resultado de nuestro código PHP en el navegador

El resultado es incorrecto y se devolvió un Warning. Esto ocurre porque PHP trató de concatenar la string “Resultado” con el número 10, lo cual no salió bien. El responsable de hacer que la cuenta se desestimara y la concatenación ocurriera es lo que llamamos precedencia de operadores. Define la prioridad de los operadores en el código. Quizás recuerdes este concepto en las clases de expresiones matemáticas en la escuela.

Actualmente, el operador de concatenación (.) tiene una precedencia superior en relación al operador de suma (+). Para resolver el problema anterior, tendríamos que aislar la cuenta en la expresión usando paréntesis, de la siguiente forma:

echo "Resultado: " . ($number1 + $number2);

Sin embargo, puede ser que se nos olvide esto y sigamos teniendo el mismo error. Pensando en resolver esta piedra en el zapato, se propuso que en la versión 8 de PHP el operador de concatenación tenga una precedencia inferior a los operadores aritméticos, y afortunadamente esta propuesta fue aceptada. Así, si ejecutamos el código inicial usando PHP 8, veremos el siguiente resultado:

Resultado de nuestro código en el navegador

¡Perfecto! La operación se realizó y se mostró la string “Resultado”.

Conclusión

En este artículo vimos algunas de las increíbles novedades que PHP 8 está trayendo. Sin embargo, aún hablaremos sobre muchas otras en los próximos artículos de esta serie. No olvides compartir este contenido con otros, eso ayuda mucho a nuestro trabajo, muchas gracias :)

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.