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 principal, siempre se refiere a la clase donde está escrito, no a la clase que realmente se está utilizando. Esto puede causar un comportamiento inesperado en la herencia. El enlace estático tardío resuelve este problema mediante la palabra clave static::.

Considera este ejemplo en el que 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 de 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 denomina enlace «tardío» 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: Usa static:: en lugar de self:: cuando quieras que las clases secundarias hereden y personalicen correctamente el comportamiento estático.

challenge icon

Desafío

Fácil

Construyamos un sistema de registro de modelos que demuestre el poder del enlace estático tardío. Crearás una clase base Model con un método de fábrica que las clases hijas pueden heredar y, gracias a static::, cada clase hija creará correctamente instancias de sí misma en lugar de la clase 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. Por último, 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 Model. Incluye el archivo padre. Sobrescribe la propiedad estática $tableName con "users". La clase hereda todos los métodos de Model sin ningún cambio.
  • Product.php: Crea una clase Product que extienda Model. Incluye el archivo padre. Sobrescribe la propiedad estática $tableName con "products". Nuevamente, la clase hereda todos los métodos de la clase padre.
  • main.php: Incluye los archivos User y 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. Después, imprime el nombre de la tabla de cada clase usando User::getTable() y Product::getTable(), cada uno en su propia línea.

La magia está en que los métodos create() y getTable() están definidos únicamente en la clase padre Model y, aun así, 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:: hace referencia a la clase a la que realmente se llamó.

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

Pruébalo tú mismo

<?php

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

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

// TODO: Usa el método de fábrica create() heredado 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

Practica por tu cuenta: Compilador de PHP online