Constructor promotion y readonly en PHP 8: menos código para lo mismo

PHP 8 trajo dos características que reducen drásticamente el código repetitivo en clases: el constructor promotion, que convierte los parámetros del constructor en propiedades automáticamente, y las propiedades readonly, que garantizan inmutabilidad tras la inicialización. PHP 8.2 añade además clases readonly completas.

Constructor promotion: antes y después

<?php
// Sin constructor promotion: 12 líneas para 3 propiedades
class PuntoAntiguo
{
    public float $x;
    public float $y;
    public float $z;

    public function __construct(float $x, float $y, float $z)
    {
        $this->x = $x;
        $this->y = $y;
        $this->z = $z;
    }
}

// Con constructor promotion (PHP 8.0): 5 líneas
class Punto
{
    public function __construct(
        public float $x,
        public float $y,
        public float $z = 0.0,
    ) {}
}

$p = new Punto(1.0, 2.0, 3.0);
echo $p->x; // 1.0
?>

Mezclar parámetros normales y promovidos

<?php
class Producto
{
    public readonly string $id;

    public function __construct(
        public readonly string $nombre,
        public readonly float  $precio,
        string $id = '',  // parámetro normal, no se convierte en propiedad
    ) {
        // Lógica adicional en el cuerpo del constructor
        $this->id = $id !== '' ? $id : uniqid('prod_');
    }
}

$p = new Producto('Teclado', 89.99);
echo $p->nombre; // Teclado
echo $p->id;     // prod_6640f1a2c1234 (aleatorio)
?>

Propiedades readonly: inmutabilidad controlada

<?php
class DireccionPostal
{
    public function __construct(
        public readonly string $calle,
        public readonly string $ciudad,
        public readonly string $codigoPostal,
        public readonly string $pais = 'ES',
    ) {}

    // Para "modificar", crea una nueva instancia con los datos cambiados
    public function conCiudad(string $nuevaCiudad): static
    {
        return new static(
            $this->calle,
            $nuevaCiudad,
            $this->codigoPostal,
            $this->pais,
        );
    }
}

$dir = new DireccionPostal('Calle Mayor 1', 'Madrid', '28001');
echo $dir->ciudad; // Madrid

// $dir->ciudad = 'Barcelona'; // Fatal error: Cannot modify readonly property

$dir2 = $dir->conCiudad('Barcelona');
echo $dir2->ciudad; // Barcelona
echo $dir->ciudad;  // Madrid (la original no cambia)
?>

Clases readonly en PHP 8.2

En PHP 8.2, una clase marcada como readonly convierte automáticamente todas sus propiedades en readonly sin tener que declararlo una a una:

<?php
// PHP 8.2: readonly class
readonly class Coordenada
{
    public function __construct(
        public float $latitud,
        public float $longitud,
        public float $altitud = 0.0,
    ) {}

    public function distanciaA(Coordenada $otra): float
    {
        // Fórmula de Haversine simplificada
        $dlat = deg2rad($otra->latitud  - $this->latitud);
        $dlon = deg2rad($otra->longitud - $this->longitud);
        $a = sin($dlat/2)**2
           + cos(deg2rad($this->latitud))
           * cos(deg2rad($otra->latitud))
           * sin($dlon/2)**2;
        return 6371 * 2 * atan2(sqrt($a), sqrt(1 - $a)); // km
    }
}

$madrid   = new Coordenada(40.4168, -3.7038);
$barcelona = new Coordenada(41.3851, 2.1734);

echo round($madrid->distanciaA($barcelona)) . ' km'; // 505 km
?>

Combinar constructor promotion con validación

<?php
class RangoPrecio
{
    public function __construct(
        public readonly float $min,
        public readonly float $max,
    ) {
        if ($this->min < 0) {
            throw new InvalidArgumentException('El mínimo no puede ser negativo');
        }
        if ($this->max < $this->min) {
            throw new InvalidArgumentException('El máximo debe ser mayor que el mínimo');
        }
    }

    public function contiene(float $precio): bool
    {
        return $precio >= $this->min && $precio <= $this->max;
    }
}

$rango = new RangoPrecio(10.0, 100.0);
var_dump($rango->contiene(50.0)); // true
var_dump($rango->contiene(5.0));  // false
?>

La documentación oficial de constructor promotion y la de propiedades readonly detallan las restricciones de uso en herencia y con tipos de unión.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP