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.
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
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, utilizarstatic::
podría dar resultados diferentes. Tenga en cuenta también questatic::
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::
oself::
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