Что такое поздние статические привязки в PHP?
Начиная с PHP 5.3.0, PHP реализует функцию, называемую поздней статической привязкой, которая может использоваться для ссылки на вызываемый класс в контексте статического наследования.
Позднее статическое связывание пытается решить это ограничение, введя ключевое слово, которое ссылается на класс, который изначально вызывался во время выполнения. Было решено не вводить новое ключевое слово, а использовать static
которая уже была зарезервирована.
Давайте посмотрим пример:
<?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 ?>
late static bindings
работают, сохраняя класс, названный в последнем «непереадресованном вызове». В случае вызовов статических методов это явно названный класс (обычно тот, который находится слева от оператора ::); в случае нестатических вызовов методов это класс объекта.
«Переадресационный вызов» является статическим, который вводится self::
, parent::
, static::
или, если он идет вверх по иерархии классов, forward_static_call()
.
Функция get_called_class()
может использоваться для извлечения строки с именем вызываемого класса, а static::
вводит ее область видимости.
В руководстве PHP вам обязательно нужно прочитать Late Static Bindings . Однако я постараюсь дать вам краткое резюме.
В основном, это сводится к тому, что ключевое слово self
не соответствует правилам наследования. self
всегда разрешает класс, в котором он используется. Это означает, что если вы создадите метод в родительском классе и вызовите его из дочернего класса, то self
не буду ссылаться на ребенка, как вы могли бы ожидать.
Позднее статическое связывание вводит новое использование для ключевого слова static
, которое учитывает этот конкретный недостаток. Когда вы используете static
, он представляет класс, в котором вы его сначала используете, т.е. он «привязывается» к классу среды выполнения.
Это две основные концепции. Способ, как self
, parent
и static
работает, когда static
находится в игре, может быть тонкой, поэтому вместо того, чтобы подробно останавливаться, я настоятельно рекомендую изучить примеры страниц руководства. Когда вы понимаете основы каждого ключевого слова, примеры совершенно необходимы, чтобы увидеть, какие результаты вы собираетесь получить.
Существует не очень очевидное поведение:
Следующий код создает «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
Однако, если мы удалим объявление функции classname из бета-класса, в качестве результата получим «alphaalpha».
Я цитирую из книги: «PHP Master пишет передовой код».
Поздней статической привязкой была функция, введенная с php 5.3. Это позволяет нам наследовать статические методы из родительского класса и ссылаться на вызываемый дочерний класс.
Это означает, что вы можете иметь абстрактный класс со статическими методами и ссылаться на конкретные реализации дочернего класса, используя нотацию static :: method () вместо self :: method ().
Не стесняйтесь также взглянуть на официальную документацию php: http://php.net/manual/en/language.oop5.late-static-bindings.php
Пример:
<?php class Animal { public static function StaticCall() { // Parent object invokes its own getAnimalName() // Child object invokes its own getAnimalName() instead of parent's getAnimalName() return static::getAnimalName(); } public static function SelfCall() { return self::getWeight(); } private static function getAnimalName(){ return 'Animal <br />'; } private static function getWeight(){ return '10 kg <br />'; } } class Bird extends Animal { public static function getAnimalName(){ return 'Bird <br />'; } private static function getWeight(){ return '2 kg <br />'; } } echo Animal::StaticCall(); // Animal echo Animal::SelfCall(); // 10 kg echo Bird::StaticCall(); // Bird invokes method from own object echo Bird::SelfCall(); // 10 kg invokes method from parent
В приведенном выше коде вы можете увидеть два класса Animal
который является родительским классом, и Bird
который является дочерним классом. У Animal
и Bird
есть getAnimalName()
и getWeight()
. Суперкласс Animal
имеет два метода: StaticCall()
и SelfCall()
.
Метод StaticCall()
вызывает getAnimalName()
с помощью static
ключевого слова.
Метод SelfCall()
вызывает getWeight()
, используя ключевое слово self
.
Теперь у нас есть вопрос: в каком контексте выполняется getAnimalName()
?
Ответ: static::getAnimalName()
идентифицирует контекст и вызывает метод в этом контексте.
Если вы вызываете Bird::StaticCall()
код будет выполнять StaticCall()
который находится в Animal
. Тогда static::getAnimalName()
вызовет и выполнит из Bird
метод getAnimalName()
.
Это отличается от self::
, потому что self::
всегда вызывает метод внутри объекта self
. В этом случае self::getWeight()
определяется в объекте Animal
в методе SelfCall()
и Bird::SelfCall()
будет Затем self::getWeight()
вызывает getWeight()
в контексте объекта Animal
.
Самый простой пример, чтобы показать разницу.
Примечание: self :: $ c
class A { static $c = 7; public static function getVal() { return self::$c; } } class B extends A { static $c = 8; } B::getVal(); // 7
Поздняя статическая привязка, обратите внимание на static :: $ c
class A { static $c = 7; public static function getVal() { return static::$c; } } class B extends A { static $c = 8; } B::getVal(); // 8
Например:
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();
Глядя на это из «почему я должен использовать это?» перспектива, это в основном способ изменить контекст, из которого интерпретируется / запускается статический метод.
С self
собой контекст – это тот, где вы определили метод изначально. Со static
это тот, с которого вы его вызываете.