Ya se respira sabor a calabaza, así que ha llegado la hora de una nueva versión de PHP, el lenguaje de scripting del lado del servidor que impulsa nuestro CMS favorito, WordPress. Antes del lanzamiento de la versión 8.4 el 21 de noviembre, los desarrolladores de PHP han presentado numerosas versiones preliminares de la nueva base de código, incluyendo algunas versiones preliminares desde que se congelaron las funcionalidades en agosto.

Junto con las nuevas funcionalidades, mejoras y deprecaciones, que prevemos en esta época del año, en 2024 se produjeron ajustes en el ciclo de publicación de PHP, con el fin de las publicaciones de seguridad para todas las versiones que se soportan en la actualidad, sincronizadas con el final del año en lugar de coincidir con la fecha de su lanzamiento GA.

Además, ese soporte se amplió un año, lo que significa que podrías estar utilizando PHP 8.4 de forma segura hasta 2028 (con dos años de correcciones de seguridad y errores y dos años sólo de correcciones de seguridad).

Aunque puedas pasar más tiempo con PHP 8.4, probablemente quieras enterarte de las novedades de esta versión ahora mismo. Así que, entremos en materia.

Nuevas funcionalidades y mejoras en PHP 8.4

Las nuevas funcionalidades incluidas en el lanzamiento de PHP 8.3 del año pasado parecerán poco importantes si se comparan con algunas de las novedades de 8.4:

Property hooks

Los property hooks (hooks de propiedad) aportan un enfoque totalmente nuevo al manejo de los «getters» y «setters» en la programación orientada a objetos (POO) de PHP, permitiéndote simplificar la estructura de tus archivos de clase.

Como ejemplo de lo que los hooks de propiedad pueden sustituir, la sencilla clase que aparece a continuación incluye las propiedades $size y $flavor. Tienen visibilidad private para protegerlas del acceso directo fuera del objeto resultante. Por eso, los métodos públicos getter y setter median en el acceso a las propiedades:

class Coffee
{
    private string $size;
    private string $flavor;
    public function __construct(string $size, string $flavor) {
        $this->size   = $size;
        $this->flavor = $flavor;
    }

    // "Setting" coffee size and flavor
    public function setSize(string $size): void {
        $this->size = $size;
    }
    public function setFlavor(string $flavor): void {
        $this->flavor = $flavor;
    }

    // "Getting" coffee size and flavor
    public function getSize(): string {
        return $this->size;
    }
    public function getFlavor(): string {
        return $this->flavor;
    }
} // End of class

// Make some coffee
$coffee = new Coffee('Small', 'Pumpkin Spice');
print $coffee->getSize() . ' ' . $coffee->getFlavor(); // Prints "Small Pumpkin Spice"

// Change order
$coffee->setSize('Grande');
$coffee->setFlavor('Mocha');
print $coffee->getSize() . ' ' . $coffee->getFlavor(); // Prints "Grande Mocha"

O puede que tu clase tenga muchas propiedades, y en lugar de escribir muchos métodos getter y setter, utilices los métodos mágicos _get y _set de PHP. Incluso podrías ordenar las cosas en una declaración switch algo desordenada, como el siguiente extracto.

// __set magic method example
public function __set(string $key, $value): void 
    switch ($key) {
        case 'size':
            $this->size = $value;
            break;
        case 'flavor':
            $this->flavor = $value;
            break;
        default:
            throw new InvalidArgumentException('Invalid input');
        }
}

// Later, we can change the coffee order like this:
$coffee->size = 'Grande';
$coffee->flavor = 'Mocha';

Sea cual sea el enfoque que elijas, cuantas más propiedades tengas en tu clase, más lejos estará el código que las manipula de sus definiciones, que suelen estar al inicio del archivo de la clase. Además, algunas implementaciones de los métodos mágicos _get y _set pueden, de forma inesperada, proporcionar acceso a propiedades privadas o protegidas en tu objeto que no habías previsto exponer.

La nueva funcionalidad hooks de propiedad agrupa la funcionalidad getter y setter con las propias propiedades. En el ejemplo de hooks de propiedad que aparece a continuación, verás que las propiedades $size y $flavor de la clase Café ahora son públicas. Pero también hemos añadido una validación básica a los hooks set, diferenciándolos de las asignaciones directas.

// Property definitions at the top of our Coffee class
class Coffee
{
    public string $flavor {
        set(string $value) {
            if (strlen($value) > 16) throw new InvalidArgumentException('Input is too long');
                $this->flavor = $value;
        }
    }

    public string $size {
        set(string $value) {
            if (! in_array($value, array(‘Small’, ‘Grande’))) throw new InvalidArgumentException('Not a valid size');
                $this->size = $value;
        }
    }

    // Rest of the Coffee class
}

// Define a coffee
$coffee = new Coffee();
$coffee->size = 'Grande';
$coffee->flavor = 'Pumpkin spice';

Del mismo modo, como puedes ver a continuación, un hook get puede empaquetar funcionalidad en lo que parece ser una referencia ordinaria a una propiedad de un objeto.

// Simplified Coffee class
class Coffee
{
    public string $flavor {
        get { 
            return $this->flavor . ' Spice';
       }
    }
}

// Create a flavor 
$coffee = new Coffee();
$coffee->flavor = 'Pumpkin'; // Stores the value "Pumpkin"
print $coffee->flavor;       // Prints "Pumpkin Spice"

A diferencia de los métodos mágicos de PHP, los hooks de propiedad pueden utilizarse en interfaces y clases abstractas. Un ejemplo de interfaz:

interface Coffee
{
    public string $size { get; set; }
    public string $flavor { get; set; }
}

Visibilidad asimétrica

Los métodos getter y setter visibles públicamente que hemos visto antes representan el enfoque tradicional para acceder a las propiedades privadas y protegidas dentro de sus clases.

Una característica ingeniosa de PHP 8.4 es la posibilidad de que una propiedad tenga distintos niveles de visibilidad dependiendo del contexto en el que se acceda a ella. Así, una propiedad puede ser pública cuando se lee, pero privada o protegida cuando se establece.

Mira esto:

class Coffee
{
    public private(set) string $flavor = 'Pumpkin Spice';
}

$coffee = new Coffee();
print $coffee->flavor;     // Prints "Pumpkin Spice"
$coffee->flavor = 'Mocha';  // Error (visibility)

Arriba, la propiedad $flavor de la clase es pública excepto en un contexto de configuración. Ya es bastante sencillo, pero la visibilidad asimétrica tiene incluso un pequeño atajo:

class Coffee
{
    // public is assumed when the context is not setting
    private(set) string $flavor = 'Pumpkin Spice';
}

Puedes utilizar hooks de propiedad y visibilidad asimétrica en combinación para obtener una enorme flexibilidad a la hora de trabajar con propiedades de objetos de distintas visibilidades.

Encadenar new sin paréntesis

Hablando de atajos, para llamar a new y encadenar métodos solía ser necesario colocar su invocación entre paréntesis, así:

$coffee = (new Coffee())->getFlavor()->getSize();

PHP 8.4 permite lo siguiente:

$coffee = new Coffee()->getFlavor()->getSize();

Puede parecer un cambio menor, pero eliminar sólo dos paréntesis hace que sea mucho más fácil de leer y depurar.

Nuevas funcionalidades para encontrar elementos de arrays

En el apartado «¿Quieres decir que no podíamos hacer esto ya?», PHP 8.4 introduce la función array_find(), que puede buscar elementos de un array que cumplan las condiciones expresadas en una función callback. La función devuelve el valor del primer elemento que coincida con la prueba callback.

La nueva versión incluye otras tres funciones relacionadas:

  • array_find_key(): Como array_find(), pero el valor devuelto es la clave del elemento coincidente en lugar del valor de los propios elementos.
  • array_all(): Devuelve true si todos los elementos del array que se está comprobando coinciden con la prueba de la llamada de retorno.
  • array_any(): Devuelve true si al menos uno de los elementos de la matriz coincide con la prueba callback.

Ten en cuenta que las dos últimas funciones devuelven indicadores booleanos en lugar de claves o contenido del array.

Aquí tienes algunos ejemplos rápidos:

$array = [
    'a' => 'Mocha',
    'b' => 'Caramel',
    'c' => 'Maple',
    'd' => 'Pumpkin'
   ];

// Find the first flavor name that is 5 characters long
var_dump(array_find($array, function (string $value) {
    return strlen($value) == 5;
})); // Returns “Mocha,” even though “Maple” is the same length 

// Find the array key for the first flavor with a name longer than 5 characters.
var_dump(array_find_key($array, function (string $value) {
    return strlen($value) > 5;
})); // Returns “b”

// Check to see if any flavor name is less than 5 characters long
var_dump(array_any($array, function (string $value) {
    return strlen($value) < 5;
})); // Returns false

// Check to see if all flavor names are shorter than 8 characters
var_dump(array_all($array, function (string $value) {
    return strlen($value) < 8;
})); // Returns true

HTML5 parsing

HTM5 es el estándar predeterminado para la estructura de las páginas web modernas, pero la tecnología de análisis del Modelo de Objetos del Documento (DOM) de PHP se había estancado en HTML 4.01.

En lugar de actualizar la clase DOMDocument existente que funciona con los estándares HTML más antiguos, PHP 8.4 viene con una nueva clase DomHTMLDocument que está preparada para HTM5.

Puedes importar el contenido de una página HTML5 así:

$document = DomHTMLDocument::createFromString($html)

Además del constructor createFromString($html) anterior, la clase también admite createFromFile($path) y createEmpty()

El nuevo analizador sintáctico reconoce etiquetas semánticas de HTML5 como main, article y section que ahora nos resultan familiares a la mayoría de nosotros.

Multibyte trim functions

Otra novedad en PHP 8.4 que parece haber tardado en llegar es el soporte multibyte en las funciones trim:

  • mb_trim()
  • mb_ltrim()
  • mb_rtrim()

Al igual que la antigua función PHP trim(), mb_trim elimina los espacios en blanco y algunos caracteres especiales, como los saltos de línea, de ambos extremos de una cadena que puede contener caracteres multibyte. Las demás funciones recortan los extremos izquierdo o derecho de una cadena.

Deprecaciones en PHP 8.4

Cada versión de PHP trae consigo una lista de características y funciones (algunas bastante oscuras) que se marcan para su posible eliminación de la plataforma. Una de las deprecaciones más destacadas de PHP 8.4 es el seguimiento de sesiones sin cookies.

Eliminación de las sesiones GET/POST

Aunque las cookies suelen ser el método preferido para el seguimiento de las sesiones de usuario, PHP ha soportado la fijación de datos de ID de sesión en parámetros GET/POST. Para habilitar el seguimiento de sesiones mediante parámetros en las URL, se desactiva la configuración de PHP session.use_only_cookies, y se puede habilitar la configuración session.use_trans_sid.

Con PHP 8.4, cualquiera de esos estados de la configuración activará una advertencia de deprecado que puede aparecer en los registros de tu sitio web. Cuando se publique PHP 9, estas opciones dejarán de estar disponibles.

Otras deprecaciones (y eliminaciones) en PHP 8.4

A continuación hay una lista de funcionalidades que el equipo que está detrás de PHP 8.4 ha decidido deprecar. (Algunas incluyen enlaces a más información sobre las funcionalidades),

  • Se deprecan formalmente las propiedades soft-deprecated DOMDocument y DOMEntity.
  • Eliminado DOMImplementation::getFeature($feature, $version).
  • Se depreca la constante DOM_PHP_ERR.
  • Deprecada la etiqueta «S» en unserialize().
  • Deprecadas session.sid_length y session.sid_bits_per_character.
  • Deprecado SplFixedArray::__wakeup().
  • Deprecadoxml_set_object() y xml_set_*_handler() con nombres de métodos de cadena.
  • Deprecado pasar null y false a dba_key_split().
  • Se depreca el paso de tipos de datos incorrectos para las opciones en las funciones de la extensión ext/hash.
  • Deprecadas las constantes SUNFUNCS_RET_STRING, SUNFUNCS_RET_DOUBLE, SUNFUNCS_RET_TIMESTAMP.
  • Deprecado mecanismo propietario de escape CSV
  • Deprecada la constante E_STRICT.
  • Deprecado strtok().
  • Deprecado retornar valores no-string desde un manejador de salida de usuario
  • Deprecada la producción de output en un manejador de salida de usuario
  • Deprecadofile_put_contents() con $data como array.
  • Deprecados mysqli_ping() y mysqli::ping()
  • Deprecadomysqli_refresh().
  • Deprecado mysqli_kill().
  • Deprecado el segundo parámetro de mysqli_store_result().
  • Deprecado lcg_value().
  • Deprecado uniqid().
  • Deprecados md5(), sha1(), md5_file() y sha1_file().
  • Deprecado pasar E_USER_ERROR a trigger_error().
  • Deprecado usar guion bajo («_») como nombre de clase.
  • Deprecada constante SOAP_FUNCTIONS_ALL y su uso en SoapServer::addFunction().

Resumen

PHP 8.4 viene con algunos cambios interesantes. Estamos impacientes por tener pronto esta versión en nuestros servidores para nuestra evaluación de rendimiento de PHP anual — es decir, nuestras pruebas con varios sistemas de gestión de contenidos basados en PHP.

También estamos interesados en ver cuándo empiezan los desarrolladores a incorporar algunas de las nuevas funcionalidades de PHP 8.4 en sus proyectos, sobre todo los hooks de propiedad.

¿Qué funciones de PHP 8.4 son tus favoritas? ¡Comparte tus opiniones con nuestra comunidad en los comentarios!

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.