PHP 8.3 se publicó según lo previsto el 23 de noviembre y contiene muchas nuevas funciones y mejoras desde el lanzamiento de PHP 8.2. Aunque oficialmente se considera una versión menor, algunos de los cambios de la 8.3 podrían afectar directamente a tu trabajo con PHP, quizás ayudándote a programar más rápido y con menos errores.

Echemos un vistazo a los grandes — y a veces no tan grandes — cambios de esta última versión.

Nuevas Funciones y Mejoras en PHP 8.3

Empecemos explorando las características de PHP 8.3 que acaparan la mayoría de los titulares.

Constantes de Clase Tipadas

La posibilidad de declarar tipos para las propiedades de las clases está disponible desde PHP 7.4. Sin embargo, a pesar de los numerosos ajustes en la tipificación de PHP a lo largo de los años, no se ha extendido a las constantes — hasta ahora.

Las constantes de clase — y eso incluye también las constantes de interfaz, traits y enum —   se pueden tipar en PHP 8.3, lo que hace menos probable que los desarrolladores se desvíen de la intención que hay detrás de la declaración inicial de una constante.

Aquí hay un ejemplo básico utilizando una interfaz:

// Legal:
interface ConstTest {
    // Declared type and value are both strings
    const string VERSION = "PHP 8.3";
}

// Illegal:
interface ConstTest {
    // Type and value mismatch in this initial declaration
    const float VERSION = "PHP 8.3";
}

El valor real de esas constantes de clase tipadas se revela cuando se trabaja en clases derivadas de las declaraciones base. Aunque una clase derivada puede asignar con frecuencia un nuevo valor a una constante, PHP 8.3 puede ayudar a evitar que se cambie accidentalmente su tipo de modo que resulte incompatible con la declaración inicial:

class ConstTest {
    const string VERSION = "PHP 8.2";
}

class MyConstTest extends ConstTest {

    // Legal:
    // It's OK to change the value of VERSION here
    const string VERSION = "PHP 8.3";

    // Illegal:
    // Type must be declared if it was specified in the base class
    const VERSION = "PHP 8.3";

    // Illegal:
    // In this case, we can't change the type declared in the 
    // base class, even if the new type and its value are compatible.
    const float VERSION = 8.3;
}

Ten en cuenta que el tipo asignado a una constante de clase puede variar cuando se «estrechan» varios tipos o se utiliza un tipo por lo demás compatible:

class ConstTest {
    const string|float VERSION = "PHP 8.2";
}

class MyConstTest extends ConstTest {

    // Legal:
    // Here, it's OK to narrow the type declaration to string or float
    const string VERSION = "PHP 8.3";
    const float VERSION = 8.3;

    // Legal:
    // Value could be an int, but it's compatible with float 
    const float VERSION = 8;

    // Illegal:
    // We can't widen the type options here to include int
    const string|float|int VERSION = 8;
}

Dos tipos admitidos para otras propiedades al validar valores de retorno – void y never – no se admiten como tipos de constante de clase.

Una Nueva Función json_validate()

Cuando se trabaja con datos codificados en JSON, es bueno saber si la carga útil es sintácticamente válida antes de intentar hacer algo con ella.

En versiones anteriores de PHP, los desarrolladores han utilizado la función json_decode() y han comprobado si había errores mientras esa función intentaba convertir los datos JSON en arrays asociativas u objetos. La nueva función json_validate() realiza la comprobación de errores sin utilizar toda la memoria necesaria para construir esas estructuras de arrays u objetos.

Así que, en el pasado, en el pasado, podrías haber validado una carga útil JSON similar a ésta:

$obj = json_decode($maybeJSON);

if (json_last_error() === JSON_ERROR_NONE) {
    // Probably do something with $obj   
}

Si no vas a hacer algo inmediatamente con $obj en el ejemplo anterior, eso son muchos recursos utilizados sólo para confirmar la validez de la carga útil JSON original. En PHP 8.3, puedes hacer algo como esto y ahorrar algo de memoria:

if (json_validate($maybeJSON)) {
    // Do something with $maybeJSON   
}

Nota: De todos modos, no tiene mucho sentido utilizar json_validate() y luego pasar inmediatamente los datos por json_decode(), utilizando los recursos de memoria de la descodificación. Es más probable que utilices la nueva función para validar el JSON antes de almacenarlo en algún sitio o entregarlo como respuesta a una solicitud.

Clonación Profunda de Propiedades readonly

La capacidad de declarar propiedades de clase individuales como readonly apareció en PHP 8.1. PHP 8.2 introdujo la posibilidad de asignar ese atributo a toda una clase. Sin embargo, muchos desarrolladores consideraban que las restricciones impuestas al trabajar con clases que contenían dichas propiedades obstaculizaban una programación útil.

Una RFC para modificar el comportamiento de readonly hizo dos propuestas:

  1. Permitir que las clases que no son readonly extiendan a las clases que sí lo son
  2. Permitir que las propiedades de readonly se reinicien al clonar

Es la segunda propuesta la que ha llegado a PHP 8.3. El nuevo enfoque permite que las instancias de una clase con propiedades readonly se reinicien dentro del método mágico __clone (incluso a través de funciones invocadas desde dentro de __clone).

Este ejemplo de código de la RFC muestra cómo funciona:

class Foo {
    public function __construct(
        public readonly DateTime $bar,
        public readonly DateTime $baz
    ) {}
 
    public function __clone() {
        // $bar will get a new DateTime when clone is invoked
        $this->bar = clone $this->bar; 

        // And this function will be called
        $this->cloneBaz();
    }
 
    private function cloneBaz() {
       // This is legal when called from within __clone
        unset($this->baz); 
    }
}
 
$foo = new Foo(new DateTime(), new DateTime());
$foo2 = clone $foo;

Nuevo Atributo #[\Override]

Al implementar interfaces en PHP, los programadores proporcionan funcionalidad detallada para los métodos nombrados en esas interfaces. Al crear una instancia de una clase, los programadores pueden anular un método padre creando una versión alternativa con el mismo nombre y una firma compatible en el hijo.

Uno de los problemas es que los programadores pueden pensar que están implementando un método de la interfaz o anulando un método padre, cuando no es así. Podrían estar creando una bestia completamente distinta debido a un error tipográfico en el nombre del método de la clase hija o porque se han eliminado o renombrado métodos en el código padre.

PHP 8.3 introduce el atributo #[\Override] para ayudar a los programadores a dejar claro que un método debe tener algún linaje dentro del código.

Aquí hay un ejemplo básico:

class A {
    protected function ovrTest(): void {}
}

// This will work because ovrTest() 
// can be found in the parent class
class B extends A {
    #[\Override]
    public function ovrTest(): void {}
}

// This will fail because ovrBest() 
// (probably a typo) is not in the parent
class C extends A {
    #[\Override]
    public function ovrBest(): void {}
}

Obtención Dinámica de Constantes de Clase y Miembros de Enum

A diferencia de lo que ocurre con otras propiedades en el código PHP, la obtención de constantes de clase y miembros de Enum con nombres de variables ha sido un poco enrevesada. Antes de PHP 8.3, puede que lo hicieras utilizando la función constant() de esta forma:

class MyClass {
    public const THE_CONST = 9;
}

enum MyEnum int {
    case FirstMember = 9;
    case SecondMember = 9;
}

$constantName = 'THE_CONST';
$memberName = 'FirstMember';

echo constant('MyClass::' . $constantName);
echo constant('MyEnum::' . $memberName)->value;

Ahora, utilizando las mismas definiciones de clase y Enum anteriores, puedes conseguir el mismo resultado con la obtención dinámica de constantes de PHP 8.3 de esta forma:

$constantName = 'THE_CONST';
$memberName = 'FirstMember';

echo MyClass::{$constantName};
echo MyEnum::{$memberName}->value;

Nuevo Método getBytesFromString()

¿Alguna vez has querido generar cadenas aleatorias utilizando una colección preaprobada de caracteres? Ahora puedes hacerlo fácilmente con el método getBytesFromString() que se ha añadido a la extensión Random en PHP 8.3.

Este nuevo método es sencillo: le pasas una cadena de caracteres como material de origen y especificas cuántos de ellos quieres utilizar. A continuación, el método seleccionará bytes de la cadena de forma aleatoria hasta que alcance esa longitud especificada.

Aquí tienes un ejemplo sencillo:

$rando = new Random\Randomizer();
$alpha = 'ABCDEFGHJKMNPQRSTVWXYZ';

$rando->getBytesFromString($alpha, 6); //  "MBXGWL"
$rando->getBytesFromString($alpha, 6); //  "LESPMG"
$rando->getBytesFromString($alpha, 6); //  "NVHWXC"

Es posible que la longitud solicitada de la salida aleatoria tenga más bytes que la cadena de entrada:

$rando = new Random\Randomizer();
$nums = '123456';

$rando->getBytesFromString($nums, 10); //  "2526341615"

Con una cadena de entrada de caracteres únicos, cada uno tiene la misma probabilidad de ser seleccionado para el resultado aleatorio. Sin embargo, los caracteres se pueden ponderar haciendo que aparezcan más a menudo que otros en la entrada. Por ejemplo:

$rando = new Random\Randomizer();
$weighted = 'AAAAA12345';

$rando->getBytesFromString($weighted, 5); //  "1AA53"
$rando->getBytesFromString($weighted, 10); //  "42A5A1AA3A"

Nuevos Métodos getFloat() y nextFloat()

Ampliando también la extensión Random, PHP 8.3 introduce dos nuevos métodos para generar valores flotantes aleatorios: getFloat() y nextFloat().

Aquí hay un ejemplo:

$rando = new Random\Randomizer();

// Generate a float value between a minimum 
//  value of 0 and a maximum value of 5
$rando->getFloat(0,5); // 2.3937446906217

El método getFloat() también acepta un tercer parámetro después de los valores mínimo y máximo. Utilizando un Enum Random\IntervalBoundary puedes determinar si los propios valores mínimo y máximo pueden ser devueltos por la función.

Éstas son las reglas:

  • IntervalBoundary::ClosedOpen: puede devolverse el mínimo, pero no el máximo
  • IntervalBoundary::ClosedClosed: se pueden devolver tanto el mín. como el máx
  • IntervalBoundary::OpenClosed: no puede devolverse el mínimo, pero sí el máximo
  • IntervalBoundary::OpenOpen: no puede devolverse ni el mínimo ni el máximo

Cuando se utiliza getFloat() sin especificar el Enum como tercer parámetro, el valor por defecto es IntervalBoundary::ClosedOpen.

Un ejemplo útil proporcionado por la documentación de la nueva función genera coordenadas aleatorias de longitud y latitud, donde las latitudes pueden incluir -90 y 90, pero la longitud no puede incluir -180 y 180 (ya que son iguales):

$rando = new Random\Randomizer();

printf(
    "Lat: %+.6f Long: %+.6f",
    $rando->getFloat(-90, 90, \Random\IntervalBoundary::ClosedClosed),

    // -180 will not be used 
    $rando->getFloat(-180, 180, \Random\IntervalBoundary::OpenClosed),
);

El nuevo método nextFloat() es esencialmente lo mismo que utilizar getFloat() para solicitar un valor aleatorio que vaya de 0 a menos de 1:

$rando = new Random\Randomizer();

$rando->nextFloat(); // 0.3767414902847

Otros Cambios Menores en PHP 8.3

PHP 8.3 también incluye otras funciones nuevas y cambios menores. Los mencionaremos a continuación con enlaces a recursos adicionales (cuando estén disponibles):

Depreciaciones en PHP 8.3

Con cada nueva versión de PHP, algunas funciones y configuraciones se marcan para su eventual eliminación. Una vez desaprobadas, estas funciones no se recomiendan para un uso continuado y generarán avisos en muchos registros cuando aparezcan en código en ejecución.

Aquí tienes una lista de desaprobaciones en PHP 8.3, con enlaces a información adicional:

Resumen

Hemos visto los cambios significativos incluidos en PHP 8.3. Para obtener una lista detallada de todas las actualizaciones de esta versión, puedes consultar el registro oficial de cambios de la versión. Si planeas trasladar tu código a una plataforma que ejecute la última versión de PHP, la Guía de Migración de 8.2 a 8.3 puede ayudarte a no tener problemas.

Si te corresponde instalar PHP en tus servidores de desarrollo o producción, la versión 8.3 ya está lista para su descarga.

Si eres cliente de Kinsta, puedes confiar en nosotros para que esta versión esté disponible en los servidores de tus proyectos de Alojamiento Administrado de WordPress o Alojamiento de Aplicaciones.

Steve Bonisteel Kinsta

Steve Bonisteel es un Editor Técnico de Kinsta que comenzó su carrera de redactor como periodista de prensa escrita, persiguiendo ambulancias y camiones de bomberos. Lleva tratando temas relacionados con la tecnología de Internet desde finales de la década de 1990.