Позднее статическое связывание в PHP — механизм, который разрешает ссылаться на класс вызова в контексте статического наследования.
Точнее, позднее статическое связывание сохраняет класс,
название которого указали в последнем «неперенаправленном вызове». При вызове статических
методов это тот класс, название которого явно указали слева от оператора
::
;
при нестатических вызовах это класс объекта. «Перенаправленным вызовом» называется
статический вызов через конструкции self::
, parent::
,
static::
или через функцию forward_static_call()
при движении вверх по иерархии классов.
Строку с названием класса вызова получают функцией get_called_class(),
а конструкция static::
вводит область действия вызываемого класса.
Природа названия «позднее статическое связывание» возникает из внутренней
логики работы языка. Связывание называется «поздним», потому что конструкция static::
разрешается не в тот класс, в котором определили метод,
а вычисляется на основе информации в ходе исполнения программы.
Связывание также назвали «статическим», поскольку
этот механизм в числе прочего умеет вызывать статические методы.
self::
Статические ссылки на текущий класс наподобие конструкции self::
или константы __CLASS__
разрешаются в класс,
которому принадлежит функция, — в котором функцию определили:
Пример #1 Пример обращения к члену класса через конструкцию self::
<?php
class A
{
public static function who()
{
echo __CLASS__;
}
public static function test()
{
self::who();
}
}
class B extends A
{
public static function who()
{
echo __CLASS__;
}
}
B::test();
?>
Результат выполнения приведённого примера:
A
Позднее статическое связывание стремится устранить это ограничение и вводит
ключевое слово для ссылки на класс, который изначально вызвали
в ходе исполнения программы. По сути, это ключевое слово, которое в предыдущем примере
разрешило бы ссылаться на класс B
из метода test()
.
Вместо введения нового ключевого слова для позднего статического связывания разработчики языка выбрали
ключевое слово static
, которое зарезервировали прежде.
Пример #2 Пример позднего статического связывания через конструкцию static::
<?php
class A
{
public static function who()
{
echo __CLASS__;
}
public static function test()
{
static::who(); // В этом месте появляется позднее статическое связывание
}
}
class B extends A
{
public static function who()
{
echo __CLASS__;
}
}
B::test();
?>
Результат выполнения приведённого примера:
B
Замечание:
В нестатическом контексте классом вызова будет класс экземпляра объекта. Обращение через конструкцию
$this->
попытается вызывать закрытые методы из той же области действия, тогда как результат конструкцииstatic::
зависит от контекста вызова. Другое отличие состоит в том, что обращение через конструкциюstatic::
умеет ссылаться только на статические свойства.
Пример #3 Пример ссылки через конструкцию static::
в нестатическом контексте
<?php
class A
{
private function foo()
{
echo "Success!\n";
}
public function test()
{
$this->foo();
static::foo();
}
}
class B extends A
{
/* Метод foo() скопируется в класс В из класса A, поэтому областью действия метода
по-прежнему будет класс А, и вызов будет успешным */
}
class C extends A
{
private function foo()
{
/* Этот метод заменил собой исходный; область действия нового метода — класс С */
}
}
$b = new B();
$b->test();
$c = new C();
try {
$c->test();
} catch (Error $e) {
echo $e->getMessage();
}
?>
Результат выполнения приведённого примера:
Success! Success! Success! Call to private method C::foo() from scope A
Замечание:
Разрешение поздних статических связок останавливается на статическом вызове по полному названию класса, без попытки перенаправить вызов в класс, в котором сделали последний неперенаправленный вызов. Между тем, статические вызовы через конструкции
parent::
илиself::
перенаправляют информацию о вызове.Пример #4 Пример перенаправленных и неперенаправленных вызовов
<?php
class A
{
public static function foo()
{
static::who();
}
public static function who()
{
echo __CLASS__ . "\n";
}
}
class B extends A
{
public static function test()
{
A::foo();
parent::foo();
self::foo();
}
public static function who()
{
echo __CLASS__ . "\n";
}
}
class C extends B
{
public static function who()
{
echo __CLASS__ . "\n";
}
}
C::test();
?>Результат выполнения приведённого примера:
A C C