Leccion 39 de 75 8 min de lectura

Propiedades Readonly

PHP 8.1 introduce readonly, un modificador que permite crear propiedades que solo pueden asignarse una vez, ideal para objetos inmutables.

Propiedades readonly

Una propiedad readonly solo puede asignarse una vez, normalmente en el constructor. Cualquier intento posterior de modificarla lanza un error:

PHP
<?php

declare(strict_types=1);

class Usuario
{
    public readonly string $id;
    public readonly string $email;

    public function __construct(string $email)
    {
        $this->id = uniqid('user_');
        $this->email = $email;
    }
}

$usuario = new Usuario('ana@example.com');
echo $usuario->id;    // user_6574a3f2...
echo $usuario->email; // ana@example.com

// Error: Cannot modify readonly property
// $usuario->email = 'otro@example.com';

Readonly con constructor promotion

readonly se combina perfectamente con constructor property promotion para crear clases muy concisas:

PHP
<?php

declare(strict_types=1);

class Producto
{
    public function __construct(
        public readonly string $sku,
        public readonly string $nombre,
        public readonly float $precio
    ) {}
}

$producto = new Producto('SKU-001', 'Laptop', 999.99);
echo $producto->nombre; // Laptop

// Las propiedades son publicas pero inmutables
// $producto->precio = 899.99; // Error!

Clases readonly (PHP 8.2)

PHP 8.2 permite marcar toda la clase como readonly. Todas sus propiedades seran automaticamente readonly:

PHP PHP 8.2+
<?php

declare(strict_types=1);

readonly class Coordenadas
{
    public function __construct(
        public float $latitud,
        public float $longitud
    ) {}

    public function distanciaA(Coordenadas $otra): float
    {
        // Calculo simplificado
        $dLat = $otra->latitud - $this->latitud;
        $dLon = $otra->longitud - $this->longitud;
        return sqrt($dLat ** 2 + $dLon ** 2);
    }
}

$madrid = new Coordenadas(40.4168, -3.7038);
$barcelona = new Coordenadas(41.3851, 2.1734);
echo $madrid->distanciaA($barcelona);

Caso de uso: Value Objects

Los Value Objects son objetos que representan un valor y deben ser inmutables. readonly es perfecto para ellos:

PHP
<?php

declare(strict_types=1);

readonly class Dinero
{
    public function __construct(
        public float $cantidad,
        public string $moneda = 'EUR'
    ) {
        if ($cantidad < 0) {
            throw new \InvalidArgumentException('Cantidad negativa');
        }
    }

    public function sumar(Dinero $otro): Dinero
    {
        if ($this->moneda !== $otro->moneda) {
            throw new \InvalidArgumentException('Monedas diferentes');
        }
        // Retorna un NUEVO objeto (inmutabilidad)
        return new Dinero($this->cantidad + $otro->cantidad, $this->moneda);
    }

    public function formatear(): string
    {
        return number_format($this->cantidad, 2) . ' ' . $this->moneda;
    }
}

$precio = new Dinero(100.00);
$iva = new Dinero(21.00);
$total = $precio->sumar($iva);

echo $total->formatear(); // 121.00 EUR
Inmutabilidad

Cuando necesites "modificar" un objeto readonly, crea uno nuevo con los valores actualizados. Esto garantiza que el objeto original nunca cambie.

Ejercicios

Ejercicio 1: Clase Email readonly

Crea una clase Email readonly que valide el formato del email en el constructor y tenga un metodo getDominio().

Ver solucion
PHP
<?php

declare(strict_types=1);

readonly class Email
{
    public function __construct(
        public string $direccion
    ) {
        if (!filter_var($direccion, FILTER_VALIDATE_EMAIL)) {
            throw new \InvalidArgumentException('Email invalido');
        }
    }

    public function getDominio(): string
    {
        return explode('@', $this->direccion)[1];
    }
}

$email = new Email('ana@example.com');
echo $email->getDominio(); // example.com

Ejercicio 2: Clase Punto con método mover

Crea una clase Punto readonly con coordenadas x, y. Incluye un metodo mover() que retorne un nuevo Punto con las coordenadas desplazadas.

Ver solucion
PHP
<?php

declare(strict_types=1);

readonly class Punto
{
    public function __construct(
        public float $x,
        public float $y
    ) {}

    public function mover(float $dx, float $dy): Punto
    {
        return new Punto($this->x + $dx, $this->y + $dy);
    }
}

$punto = new Punto(10, 20);
$nuevo = $punto->mover(5, -3);
echo "{$nuevo->x}, {$nuevo->y}"; // 15, 17

¿Te está gustando el curso?

Cursos premium con proyectos reales y soporte.

Descubrir cursos premium