Menu
Coddy logo textTech

Enlace estático tardío

Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy — lección 36 de 91.

Cuando usas self:: en una clase padre, siempre se refiere a la clase donde está escrito, no a la clase que se está utilizando realmente. Esto puede causar un comportamiento inesperado en la herencia. El enlace estático tardío (late static binding) resuelve este problema utilizando la palabra clave static::.

Considera este ejemplo donde self:: no funciona como se espera:

<?php
class ParentClass {
    public static function create() {
        return new self();
    }
    
    public static function getClassName() {
        return self::class;
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();
echo get_class($obj) . "\n";
echo ChildClass::getClassName();

Salida:

ParentClass
ParentClass

Aunque llamamos a ChildClass::create(), obtuvimos un objeto ParentClass porque self:: se resuelve en tiempo de compilación. Ahora usemos static:: en su lugar:

<?php
class ParentClass {
    public static function create() {
        return new static();
    }
    
    public static function getClassName() {
        return static::class;
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();
echo get_class($obj) . "\n";
echo ChildClass::getClassName();

Salida:

ChildClass
ChildClass

Con static::, PHP espera hasta el tiempo de ejecución para determinar qué clase usar. Esto se llama vinculación "tardía" porque la decisión se retrasa. Es esencial para los métodos de fábrica y cualquier método estático que deba respetar la herencia.

Punto clave: Utiliza static:: en lugar de self:: cuando quieras que las clases hijas hereden y personalicen correctamente el comportamiento estático.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de registro de modelos que demuestre el poder del enlace estático tardío (late static binding). Crearás una clase base Model con un método de fábrica que las clases hijas puedan heredar — y gracias a static::, cada clase hija creará correctamente instancias de sí misma en lugar del padre.

Organizarás tu código en cuatro archivos:

  • Model.php — Crea una clase base Model con una propiedad estática protegida $tableName establecida en "models". Añade un método estático create() que use new static() para devolver una nueva instancia de cualquier clase que lo haya llamado. Añade otro método estático getTable() que devuelva static::$tableName. Finalmente, añade un método describe() que devuelva "[ClassName] from table [tableName]" usando static::class y static::$tableName.
  • User.php — Crea una clase User que extienda de Model. Incluye el archivo padre. Sobrescribe la propiedad estática $tableName a "users". La clase hereda todos los métodos de Model sin ningún cambio.
  • Product.php — Crea una clase Product que extienda de Model. Incluye el archivo padre. Sobrescribe la propiedad estática $tableName a "products". De nuevo, la clase hereda todos los métodos del padre.
  • main.php — Incluye tanto el archivo de User como el de Product. Usa el método de fábrica heredado create() para crear instancias tanto de User como de Product. Para cada instancia, llama a describe() e imprime el resultado en su propia línea. Luego imprime el nombre de la tabla para cada clase usando User::getTable() y Product::getTable(), cada uno en su propia línea.

La magia aquí es que los métodos create() y getTable() se definen solo en la clase padre Model, pero funcionan correctamente con las clases hijas porque static:: retrasa el enlace hasta el tiempo de ejecución. Cuando llamas a User::create(), PHP crea una instancia de User — no una instancia de Model — porque static:: se refiere a la clase que fue realmente llamada.

Tu salida debería mostrar cuatro líneas: la descripción para la instancia de User, la descripción para la instancia de Product, el nombre de la tabla de User y el nombre de la tabla de Product.

Hoja de referencia

La palabra clave self:: en una clase padre siempre se refiere a la clase donde está escrita, resolviéndose en tiempo de compilación. El enlace estático tardío (Late static binding) con static:: resuelve la referencia en tiempo de ejecución, permitiendo que las clases hijas hereden y personalicen correctamente el comportamiento estático.

Uso de self:: (enlace en tiempo de compilación)

<?php
class ParentClass {
    public static function create() {
        return new self();  // Siempre crea ParentClass
    }
    
    public static function getClassName() {
        return self::class;  // Siempre devuelve "ParentClass"
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();  // Devuelve una instancia de ParentClass
echo ChildClass::getClassName();  // Salida: ParentClass

Uso de static:: (enlace en tiempo de ejecución)

<?php
class ParentClass {
    public static function create() {
        return new static();  // Crea una instancia de la clase que realiza la llamada
    }
    
    public static function getClassName() {
        return static::class;  // Devuelve el nombre de la clase que realiza la llamada
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();  // Devuelve una instancia de ChildClass
echo ChildClass::getClassName();  // Salida: ChildClass

Punto clave: Utilice static:: en lugar de self:: cuando desee que las clases hijas hereden y personalicen correctamente el comportamiento estático. Esto es esencial para los métodos de fábrica y cualquier método estático que deba respetar la herencia.

Pruébalo tú mismo

<?php

require_once 'User.php';
require_once 'Product.php';

// TODO: Usa el método de fábrica heredado create() para crear una instancia de User

// TODO: Usa el método de fábrica heredado create() para crear una instancia de Product

// TODO: Llama a describe() en la instancia de User e imprime el resultado

// TODO: Llama a describe() en la instancia de Product e imprime el resultado

// TODO: Imprime User::getTable()

// TODO: Imprime Product::getTable()

?>
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos