¿Qué son exactamente los enlaces estáticos tardíos en PHP?

Respuestas:

198

Definitivamente necesita leer Enlaces estáticos tardíos en el manual de PHP. Sin embargo, intentaré darle un resumen rápido.

Básicamente, se reduce al hecho de que la selfpalabra clave no sigue las mismas reglas de herencia. selfsiempre se resuelve en la clase en la que se usa. Esto significa que si crea un método en una clase primaria y lo llama desde una clase secundaria, selfno hará referencia al secundario como podría esperar.

El enlace estático tardío introduce un nuevo uso para la staticpalabra clave, que aborda esta deficiencia en particular. Cuando lo usa static, representa la clase donde lo usa por primera vez, es decir. se 'une' a la clase de tiempo de ejecución.

Esos son los dos conceptos básicos detrás de esto. La forma self, parenty staticfunciona cuando staticestá en juego puede ser sutil, por lo que en lugar de ir a más detalle, lo recomiendo encarecidamente que estudie los ejemplos de páginas del manual. Una vez que comprenda los conceptos básicos de cada palabra clave, los ejemplos son bastante necesarios para ver qué tipo de resultados obtendrá.

zombat
fuente
este artículo me pareció realmente útil y descriptivo, échale un vistazo [link] ( techflirt.com/tutorials/oop-in-php/late-static-binding.html )
Sadegh Shaikhi
"... la selfpalabra clave no sigue las reglas de herencia. selfsiempre se resuelve en la clase en la que se usa". - Lo que no significa que no pueda llamar al método estático de un padre desde un objeto hijo a través de self, al igual que con los métodos no estáticos. Quizás quieras decir lo correcto, pero deberías reformular eso. Todo realmente importa una vez que los niños hayan nombrado miembros idénticos, ya que puede decidir a qué referirse utilizando en su static::lugar.
DanMan
81

Desde PHP: Enlaces estáticos tardíos - Manual :

A partir de PHP 5.3.0, PHP implementa una característica llamada enlace estático tardío que puede usarse para hacer referencia a la clase llamada en el contexto de herencia estática.

El enlace estático tardío intenta resolver esa limitación mediante la introducción de una palabra clave que hace referencia a la clase que inicialmente se llamó en tiempo de ejecución. ... Se decidió no introducir una nueva palabra clave, sino utilizarla staticque ya estaba reservada.

Veamos un ejemplo:

<?php
    class Car
    {
        public static function run()
        {
            return static::getName();
        }

        private static function getName()
        {
            return 'Car';
        }
    }

    class Toyota extends Car
    {
        public static function getName()
        {
            return 'Toyota';
        }
    }

    echo Car::run(); // Output: Car
    echo Toyota::run(); // Output: Toyota
?>

Los enlaces estáticos tardíos funcionan almacenando la clase nombrada en la última "llamada no reenviada". En el caso de llamadas a métodos estáticos, esta es la clase explícitamente nombrada (generalmente la que está a la izquierda del ::operador); en caso de llamadas a métodos no estáticos, es la clase del objeto. Un "Desvío de llamadas" es una estática que se introduce por self::, parent::, static::, o, si se va para arriba en la jerarquía de clases, forward_static_call(). La función get_called_class()se puede utilizar para recuperar una cadena con el nombre de la clase llamada e static::introduce su alcance.

Mrinmoy Ghoshal
fuente
1
Esta publicación es para ~ 80% una copia literal del artículo de php.net sin marcadores de citas.
WoodrowShigeru
22

No hay un comportamiento muy obvio:

El siguiente código produce 'alphabeta'.

class alpha {

    function classname(){
        return __CLASS__;
    }

    function selfname(){
        return self::classname();
    }

    function staticname(){
        return static::classname();
    }
}

class beta extends alpha {

    function classname(){
        return __CLASS__;
    }
}

$beta = new beta();
echo $beta->selfname(); // Output: alpha
echo $beta->staticname(); // Output: beta

Sin embargo, si eliminamos la declaración de la función classname de la clase beta, obtenemos 'alphaalpha' como resultado.

Jokerius
fuente
1
Muy agradable. Lo mismo se muestra en el manual de PHP, pero esto es mucho más claro. Para referencia: php.net/manual/en/language.oop5.late-static-bindings.php (ver ej. 4)
musicin3d
11

Cito del libro: "PHP Master escribe código de vanguardia".

El enlace estático tardío fue una característica introducida con php 5.3. Nos permite heredar métodos estáticos de una clase primaria y hacer referencia a la clase secundaria que se llama.

Esto significa que puede tener una clase abstracta con métodos estáticos y hacer referencia a las implementaciones concretas de la clase secundaria utilizando la notación static :: method () en lugar del self :: method ().

Siéntase libre de echar un vistazo a la documentación oficial de php también: http://php.net/manual/en/language.oop5.late-static-bindings.php


La forma más clara de explicar el enlace estático tardío es con un ejemplo simple. Eche un vistazo a las dos definiciones de clase a continuación y siga leyendo.

class Vehicle {
    public static function invokeDriveByStatic() {
        return static::drive(); // Late Static Binding
    }
    public static function invokeStopBySelf() {
        return self::stop(); // NOT Late Static Binding
    }
    private static function drive(){
        return "I'm driving a VEHICLE";
    }
    private static function stop(){
        return "I'm stopping a VEHICLE";
    }
}

class Car extends Vehicle  {
    protected static function drive(){
        return "I'm driving a CAR";
    }
    private static function stop(){
        return "I'm stopping a CAR";
    }
}

Vemos una clase para padres (vehículo) y una clase para niños (automóvil). La clase para padres tiene 2 métodos públicos:

  • invokeDriveByStatic
  • invokeStopBySelf

La clase para padres también tiene 2 métodos privados:

  • drive
  • stop

La clase infantil anula 2 métodos:

  • drive
  • stop

Ahora invoquemos los métodos públicos:

  • invokeDriveByStatic
  • invokeStopBySelf

Pregúntese: ¿Qué clase invoca invokeDriveByStatic/ invokeStopBySelf? ¿La clase padre o hijo?

Echa un vistazo a continuación:

// This is NOT Late Static Binding
// Parent class invokes from Parent. In this case Vehicle.
echo Vehicle::invokeDriveByStatic(); // I'm driving a VEHICLE
echo Vehicle::invokeStopBySelf(); // I'm stopping a VEHICLE

// !!! This is Late Static Binding !!!!
// Child class invokes an inherited method from Parent.
// Child class = Car, Inherited method = invokeDriveByStatic().
// The inherited method invokes a method that is overridden by the Child class.
// Overridden method = drive()
echo Car::invokeDriveByStatic(); // I'm driving a CAR

// This is NOT Late Static Binding
// Child class invokes an inherited method from Parent.
// The inherited method invokes a method inside the Vehicle context.
echo Car::invokeStopBySelf(); // I'm stopping a VEHICLE

La staticpalabra clave se utiliza en un patrón de diseño Singleton. Ver enlace: https://refactoring.guru/design-patterns/singleton/php/example

Julián
fuente
7

El ejemplo más simple para mostrar la diferencia.
Nota, self :: $ c

class A
{
    static $c = 7;

    public static function getVal()
    {
        return self::$c;
    }
}

class B extends A
{
    static $c = 8;
}

B::getVal(); // 7

Enlace estático tardío, nota estática :: $ c

class A
{
    static $c = 7;

    public static function getVal()
    {
        return static::$c;
    }
}

class B extends A
{
    static $c = 8;
}

B::getVal(); // 8
Sergey Onishchenko
fuente
4

Por ejemplo:

abstract class Builder {
    public static function build() {
        return new static;
    }
}

class Member extends Builder {
    public function who_am_i() {
         echo 'Member';
    }
}

Member::build()->who_am_i();
Petah
fuente
4

Mirándolo desde un "¿por qué usaría esto?" perspectiva, es básicamente una forma de cambiar el contexto desde el cual se está interpretando / ejecutando el método estático.

Con self, el contexto es el que definió el método originalmente. Con static, es desde el que lo estás llamando.

DanMan
fuente
1

Además, observe si actualiza las variables estáticas en las clases secundarias. Encontré este resultado (algo) inesperado donde el niño B actualiza al niño C:

class A{
    protected static $things;
}

class B extends A {
    public static function things(){
        static::$things[1] = 'Thing B';
        return static::$things; 
    }
}

class C extends A{
    public static function things(){
        static::$things[2] = 'Thing C';
        return static::$things;        
    }
}

print_r(C::things());
// Array (
//   [2] => Thing C
// )

B::things();

print_r(C::things()); 
// Array (
//    [2] => Thing C
//    [1] => Thing B
// )

Puede solucionarlo declarando la misma variable en cada clase secundaria, por ejemplo:

class C extends A{
    protected static $things; // add this and B will not interfere!

    public static function things(){
        static::$things[2] = 'Thing C';
        return static::$things;        
    }
}
Frank Forte
fuente