Late Static Bindings (Résolution statique a la volée)

PHP implementa una funcionalidad llamada late static binding, en español la resolución estática a la volée, que puede ser utilizada para referenciar la clase llamada en un contexto de herencia estática.

Más precisamente, las resoluciones estáticas a la volée funcionan registrando el nombre de la clase en el último "llamado no transmitido". En el caso de los llamados de métodos estáticos, se trata de la clase explícitamente nombrada (generalmente, la que está a la izquierda del operador ::) ; en el caso de métodos no estáticos, se trata de la clase del objeto. Un "llamado transmitido" es un llamado estático desencadenado por self::, parent::, static::, o, en la parte superior de la jerarquía de clases, forward_static_call(). La función get_called_class() puede ser utilizada para recuperar una cadena que contiene el nombre de la clase llamada, y static:: introduce su espacio.

Esta funcionalidad ha sido bautizada como "late static bindings", con un punto de vista interno. "Late binding", literalmente resolución tardía, proviene del hecho de que los elementos static:: no serán resueltos utilizando la clase donde el método ha sido definido, sino la que está activa durante la ejecución. El adjetivo estático ha sido añadido porque este problema se aplica (sin estar limitado a) a los métodos estáticos.

Limitaciones de self::

Las referencias estáticas a la clase actual, con self:: o __CLASS__, son resueltas utilizando la clase a la que pertenecen las funciones, es decir, donde fueron definidas:

Ejemplo #1 Uso de self::

<?php

class A
{
public static function
qui()
{
echo
__CLASS__;
}
public static function
test()
{
self::qui();
}
}

class
B extends A
{
public static function
qui()
{
echo
__CLASS__;
}
}

B::test();

?>

El resultado del ejemplo sería:

A

Uso de la resolución estática a la volée

La resolución estática a la volée intenta superar esta limitación introduciendo una palabra clave que hace referencia a la clase que ha sido llamada durante la ejecución. Para simplificar, esta palabra clave permite la referencia a B desde test(), en el ejemplo anterior. Se decidió no introducir una nueva palabra clave, sino más bien utilizar la palabra static que ya estaba reservada.

Ejemplo #2 Uso simple de static::

<?php

class A
{
public static function
qui()
{
echo
__CLASS__;
}
public static function
test()
{
static::
qui(); // Aquí, resolución a la volée
}
}

class
B extends A
{
public static function
qui()
{
echo
__CLASS__;
}
}

B::test();

?>

El resultado del ejemplo sería:

B

Nota:

En contextos no estáticos, la clase llamada será la del objeto. Como $this-> intentará llamar a métodos privados desde el mismo contexto, utilizar static:: podría dar resultados diferentes. Tenga en cuenta también que static:: solo puede hacer referencia a atributos/métodos estáticos.

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

<?php

class A
{
private function
foo()
{
echo
"success!\n";
}
public function
test()
{
$this->foo();
static::
foo();
}
}

class
B extends A
{
/* foo() será copiada en B, por lo tanto su contexto será siempre A
* y la llamada se realizará sin problemas */
}

class
C extends A
{
private function
foo()
{
/* El método original es reemplazado; el contexto es el de C */
}
}

$b = new B();
$b->test();

$c = new C();
try {
$c->test();
} catch (
Error $e) {
echo
$e->getMessage();
}

?>

El resultado del ejemplo sería:

Success!
Success!
Success!
Call to private method C::foo() from scope A

Nota:

La resolución de estáticos a la volée se detendrá en un llamado estático completamente resuelto. Por otro lado, los llamados estáticos utilizando una palabra clave como parent:: o self:: transmitirán la información de llamada.

Ejemplo #4 Llamada con o sin transmisión

<?php

class A
{
public static function
foo()
{
static::
qui();
}

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

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

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

C::test();

?>

El resultado del ejemplo sería:

A
C
C