Tema 3 · PHP 8 Moderno

Constructor (__construct), Promoción de Propiedades y Readonly en PHP 8

Aprende a inicializar objetos de forma segura con __construct(), aprovecha la sintaxis concisa de Constructor Property Promotion y protege la inmutabilidad con readonly.

El Constructor: Garantizando Objetos Válidos

En el tema anterior tuvimos que llamar a un método $objeto->inicializar(...) manualmente. Si un programador olvidara llamarlo, el objeto nacería incompleto o roto.

Para evitar esto, PHP ofrece el constructor: un método mágico especial llamado __construct() (con dos guiones bajos) que se ejecuta automáticamente de forma inmediata cada vez que se usa el operador new.

La Revolución de PHP 8: Constructor Property Promotion

Antes de PHP 8, declarar e inicializar propiedades requería escribir el nombre de cada variable hasta cuatro veces (código repetitivo conocido como Boilerplate):

❌ PHP 7 (Antiguo y Repetitivo)
class Usuario {
  private string $nombre;
  private string $email;

  public function __construct(
    string $nombre, 
    string $email
  ) {
    $this->nombre = $nombre;
    $this->email = $email;
  }
}
✔ PHP 8+ (Constructor Promotion)
class Usuario {
  // ¡Todo en una sola línea limpia!
  public function __construct(
    private string $nombre,
    private string $email
  ) {}
}

Al anteponer public, private o protected al argumento del constructor, PHP crea la propiedad y le asigna el valor recibido automáticamente.

Inmutabilidad con `readonly` (PHP 8.1 / 8.2)

La palabra clave readonly asegura que una propiedad solo pueda asignarse una única vez (dentro del constructor). Cualquier intento posterior de alterarla provocará un error fatal:

public function __construct(
    public readonly int $id, // El ID jamás podrá ser cambiado después de crearse
    public string $nombre
) {}

⚡ Producto E-Commerce con Constructor Promotion y Propiedades Readonly

Ejecuta el código en el Sandbox y observa cómo se inicializan las propiedades inmutables:

Producto.php
Solo lectura
<?php
declare(strict_types=1);

final class Producto
{
    // Constructor Property Promotion + Readonly en PHP 8.2
    public function __construct(
        public readonly int $id,
        public readonly string $codigoSku,
        private string $nombre,
        private float $precio,
        private int $stock = 0
    ) {
        if ($precio < 0) {
            throw new InvalidArgumentException("El precio no puede ser negativo.");
        }
    }

    public function getNombre(): string
    {
        return $this->nombre;
    }

    public function getPrecioConIva(float $iva = 0.21): float
    {
        return round($this->precio * (1 + $iva), 2);
    }

    public function agregarStock(int $cantidad): void
    {
        if ($cantidad > 0) {
            $this->stock += $cantidad;
        }
    }

    public function renderTarjeta(): void
    {
        echo "<div style='font-family:system-ui; border:1px solid #e2e8f0; border-radius:10px; padding:18px; max-width:420px; background:#ffffff; box-shadow:0 4px 6px rgba(0,0,0,0.05); margin-bottom:15px;'>";
        echo "<span style='background:#e0e7ff; color:#3730a3; padding:3px 8px; border-radius:4px; font-size:12px; font-weight:bold;'>SKU: {$this->codigoSku} (ID #{$this->id})</span>";
        echo "<h3 style='margin:10px 0 5px 0; color:#0f172a;'>{$this->nombre}</h3>";
        echo "<p style='margin:0; font-size:18px; font-weight:bold; color:#059669;'>$" . number_format($this->precio, 2) . " <span style='font-size:12px; color:#64748b; font-weight:normal;'>+ IVA: $" . number_format($this->getPrecioConIva(), 2) . "</span></p>";
        echo "<p style='margin:8px 0 0 0; color:#64748b; font-size:13px;'>Disponibles en almacén: <strong>{$this->stock}</strong> unidades</p>";
        echo "</div>";
    }
}

// Instanciación directa y segura con el constructor
$prod1 = new Producto(101, "KB-MAC-01", "MacBook Pro M3 Max", 2499.00, 15);
$prod2 = new Producto(102, "KB-LOGI-MX", "Mouse Logitech MX Master 3S", 99.90, 42);

echo "<h2>Catálogo de Productos (PHP 8 Constructor Promotion):</h2>";
$prod1->renderTarjeta();
$prod2->renderTarjeta();

// Demostración de protección readonly:
// $prod1->id = 999; // ❌ Descomentar esto lanzaría: Cannot modify readonly property
Pruébalo tú mismo » Se abre en una nueva pestaña con ejecución en vivo
🧠
Mini-Quiz de Retención
Comprueba tu comprensión antes de pasar a la siguiente lección
1 Pregunta
Pregunta 1
¿Qué característica de PHP 8 permite declarar la visibilidad, el tipo y asignar el valor de una propiedad directamente dentro de los parámetros de __construct() sin escribir código extra?
🦉
¿Tienes dudas con este tema?
Sócrates AI puede darte analogías y explicarte este código paso a paso.

🚀 ¿Quieres dominar Manual de Programación Orientada a Objetos en PHP (POO) y crear proyectos reales?

Aprende arquitectura limpia, desarrollo web moderno, buenas prácticas y despliegue en producción con nuestro Bootcamp Fullstack Developer con mentorías en vivo.

🏛️ Autoría y Propiedad Intelectual: Obra educativa original de David Amador para Kilopolis Academia (Solucionáticos), estructurada sobre las especificaciones y estándares abiertos del W3C, el estándar vivo de WHATWG y la documentación oficial de código abierto. © Todos los derechos reservados.