Clases y Objetos
PHP Manual

Enlace estático en tiempo de ejecución

Desde PHP 5.3.0, PHP incorpora una nueva funcionalidad llamada enlace estático en tiempo de ejecución que permite hacer referencias a la clase en uso dentro de un contexto de herencia estática.

De forma más precisa, un enlace estático en tiempo de ejecución para funcionar almacena el nombre de clase de la última llamada que no tenga "propagación". En el caso de las llamadas a métodos estáticos, se trata de la clase a la que se llamó explícitamente (normalmente, la que precede al operador ::); en los casos de llamadas a métodos que no son estáticos, se resolvería a la clase del objeto. Una "llamada con propagación" es una llamada estática que está precedida por self::, parent::, static::, o, si seguimos la jerarquía de clases, forward_static_call(). La función get_called_class() puede utilizarse para obtener un string con el nombre de la clase que realiza la llamada, y static:: revela cuál es su alcance.

Se le ha llamado "enlace estático en tiempo de ejecución" teniendo en cuenta un punto de vista interno. "Enlace en tiempo de ejecución" viene del hecho de que static:: ya resuelve a la clase en la que se definió el método, sino que en su lugar se resolverá utilizando información en tiempo de ejecución debido a que se puede utilizar (entre otras cosas) para las llamadas de métodos estáticos, se le llamó también "enlace estático".

Limitaciones de self::

Las referencias estáticas que hay dentro de la clase en uso, como self:: o __CLASS__, se resuelven empleando el nombre de la clase a la que pertenece la función:

Ejemplo #1 Uso de self::

<?php
class {
    public static function 
who() {
        echo 
__CLASS__;
    }
    public static function 
test() {
        
self::who();
    }
}

class 
extends {
    public static function 
who() {
        echo 
__CLASS__;
    }
}

B::test();
?>

El resultado del ejemplo sería:

A

Uso de Enlace Estático en Tiempo de ejecución

Los enlaces estáticos en tiempo de ejecución tratan de resolver estas limitaciones empleando una palabra clave que haga referencia a la clase que realizó la llamada en tiempo de ejecución. Es decir, una palabra clave que en el ejemplo anterior permita hacer referencia desde test() a B. Se decidió no crear una nueva palabra clave, por lo que en su lugar se ha utilizado la palabra reservada static.

Ejemplo #2 Uso básico de static::

<?php
class {
    public static function 
who() {
        echo 
__CLASS__;
    }
    public static function 
test() {
        static::
who(); // He aquí el enlace estático en tiempo de ejecución
    
}
}

class 
extends {
    public static function 
who() {
        echo 
__CLASS__;
    }
}

B::test();
?>

El resultado del ejemplo sería:

B

Nota:

En contextos no estáticos, la clase que realiza la llamada será la clase del objeto instanciado. Dado que $this-> tratará de invocar métodos privados en su mismo ámbito, el uso de static:: puede provocar diferents resultados. Otra diferencia es que static:: sólo puede hacer referencia a propiedades estáticas.

Ejemplo #3 Uso de static:: en un contexto no estático

<?php
class {
    private function 
foo() {
        echo 
"exito!\n";
    }
    public function 
test() {
        
$this->foo();
        static::
foo();
    }
}

class 
extends {
   
/* foo() se copiará en B, por lo tanto su ámbito seguirá siendo A
    * y la llamada tendrá éxito */
}

class 
extends {
    private function 
foo() {
        
/* se reemplaza el método original; el ámbito del nuevo es ahora C */
    
}
}

$b = new B();
$b->test();
$c = new C();
$c->test();   //falla
?>

El resultado del ejemplo sería:

exito!
exito!
exito!


Fatal error:  Call to private method C::foo() from context 'A' in /tmp/test.php on line 9

Nota:

En una llamada que se resuelva como estática, la resolución de enlaces estáticos en tiempo de ejecución se dentendrá sin propagarse. Por otra parte, las llamadas estáticas que utilicen palabras clave como parent:: o self:: sí propagarán la información.

Ejemplo #4 Llamadas que propagan y que no propagan

<?php
class {
    public static function 
foo() {
        static::
who();
    }

    public static function 
who() {
        echo 
__CLASS__."\n";
    }
}

class 
extends {
    public static function 
test() {
        
A::foo();
        
parent::foo();
        
self::foo();
    }

    public static function 
who() {
        echo 
__CLASS__."\n";
    }
}
class 
extends {
    public static function 
who() {
        echo 
__CLASS__."\n";
    }
}

C::test();
?>

El resultado del ejemplo sería:

A
C
C


Clases y Objetos
PHP Manual