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
ParentClassAunque 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
ChildClassCon 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.
Desafío
FácilConstruyamos 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 baseModelcon una propiedad estática protegida$tableNameestablecida en"models". Añade un método estáticocreate()que usenew static()para devolver una nueva instancia de cualquier clase que lo haya llamado. Añade otro método estáticogetTable()que devuelvastatic::$tableName. Por último, añade un métododescribe()que devuelva"[ClassName] from table [tableName]"usandostatic::classystatic::$tableName.User.php: Crea una claseUserque extiendaModel. Incluye el archivo padre. Sobrescribe la propiedad estática$tableNamecon"users". La clase hereda todos los métodos deModelsin ningún cambio.Product.php: Crea una claseProductque extiendaModel. Incluye el archivo padre. Sobrescribe la propiedad estática$tableNamecon"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 heredadocreate()para crear instancias tanto deUsercomo deProduct. Para cada instancia, llama adescribe()e imprime el resultado en su propia línea. Después, imprime el nombre de la tabla de cada clase usandoUser::getTable()yProduct::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()
?>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
1Fundamentos de la POO
Archivos externosIntroducción a la POOClases frente a objetosLa palabra clave $thisMétodosPropiedadesConstructor (__construct)Destructor (__destruct)Repaso: calculadora simple4Herencia
Herencia básicaLa palabra clave parent::Sobrescritura de métodosLa palabra clave finalClases abstractasRepaso - Jerarquía de empleados7Encapsulación
Público, protegido, privadoModificadores de acceso en profundidadGetters y settersOcultación de la informaciónPromoción del constructor (8.0)Repaso: sistema de registros de estudiantes10Conceptos avanzados de OOP
Composición vs. herenciaInyección de dependenciasClases anónimasEnums (PHP 8.1)Fibras (PHP 8.1)Análisis profundo de la clonación de objetosGeneradores e iteradores13Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository2Namespaces y carga automática
Introducción a los NamespacesLa palabra clave useEstándar de carga automática PSR-4Autoloader de ComposerRepaso: proyecto organizado5Interfaces y contratos
Introducción a las interfacesImplementación de interfacesImplementación de múltiples interfacesInterfaces vs. clase abstractaTipado con interfacesRepaso - Calculadora de formas8Métodos mágicos
Introducción a los métodos mágicos__toString y __debugInfo__get, __set, __isset, __unset__call y __callStatic__clone y clonación de objetos__serialize y __unserializeRepaso: colección personalizada3Propiedades de clase
Propiedades de instancia vs. estáticasConstantes en clasesMétodos y propiedades estáticosPropiedades privadas y protegidasPropiedades de solo lectura (PHP 8.1)Repaso: gestor de cuentas bancarias6Polimorfismo
Repaso de la sobrescritura de métodosPolimorfismo mediante interfacesAnotaciones de tipo y tipos uniónEnlace estático tardíoRepaso: procesador de pagos9Traits
Introducción a los TraitsUso de múltiples TraitsResolución de conflictos entre TraitsMétodos abstractos en TraitsTraits frente a la herencia12Patrones de diseño, parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón StrategyPractica por tu cuenta: Compilador de PHP online