Ответы:
Вам обязательно нужно прочитать Поздние статические привязки в руководстве по PHP. Тем не менее, я постараюсь дать вам краткое резюме.
По сути, это сводится к тому, что self
ключевое слово не следует тем же правилам наследования. self
всегда разрешается в классе, в котором он используется. Это означает, что если вы создадите метод в родительском классе и вызовете его из дочернего класса, self
он не будет ссылаться на дочерний класс, как вы могли бы ожидать.
Позднее статическое связывание вводит новое использование static
ключевого слова, которое устраняет этот конкретный недостаток. Когда вы используете static
, он представляет класс, где вы впервые используете его, т.е. он «привязывается» к классу времени выполнения.
Это две основные концепции, стоящие за этим. Путь self
, parent
и static
работать , когда static
в игре может быть тонкими, так что вместо того , чтобы пойти , чтобы более подробно, я настоятельно рекомендую вам изучить руководство примеров страниц. Как только вы поймете основы каждого ключевого слова, примеры станут необходимыми, чтобы увидеть, какие результаты вы собираетесь получить.
self
ключевое слово не соответствует правилам наследования. self
всегда разрешает класс, в котором оно используется». - Это не означает, что вы не можете вызывать статический метод родителя через дочерний объект self
, как с нестатическими методами. Возможно, вы имеете в виду правильные вещи, но вы должны перефразировать это. Все это действительно имеет значение, только если у детей есть члены с одинаковыми именами, так как вы можете решить, на кого ссылаться, используя static::
вместо этого.
Из 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
?>
Поздние статические привязки работают путем сохранения класса, названного в последнем «вызове без пересылки». В случае статических вызовов методов это класс с явным именем (обычно слева от
::
оператора); в случае нестатических вызовов методов это класс объекта. А «переадресация вызова» является статичным , который вводитсяself::
,parent::
,static::
или, если идти вверх в иерархии классов,forward_static_call()
. Функцияget_called_class()
может использоваться для извлечения строки с именем вызываемого класса и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
Самый простой способ объяснить позднюю статическую привязку - это простой пример. Взгляните на два определения классов ниже и читайте дальше.
class Vehicle {
public static function invokeDriveByStatic() {
return static::drive(); // Late Static Binding
}
public static function invokeStopBySelf() {
return self::stop(); // NOT Late Static Binding
}
private static function drive(){
return "I'm driving a VEHICLE";
}
private static function stop(){
return "I'm stopping a VEHICLE";
}
}
class Car extends Vehicle {
protected static function drive(){
return "I'm driving a CAR";
}
private static function stop(){
return "I'm stopping a CAR";
}
}
Мы видим Родительский класс (Автомобиль) и Детский класс (Автомобиль). Родительский класс имеет 2 открытых метода:
invokeDriveByStatic
invokeStopBySelf
Родительский класс также имеет 2 приватных метода:
drive
stop
Дочерний класс переопределяет 2 метода:
drive
stop
Теперь давайте вызовем публичные методы:
invokeDriveByStatic
invokeStopBySelf
Спросите себя: какой класс вызывает invokeDriveByStatic
/ invokeStopBySelf
? Родительский или Детский класс?
Посмотрите ниже:
// This is NOT Late Static Binding
// Parent class invokes from Parent. In this case Vehicle.
echo Vehicle::invokeDriveByStatic(); // I'm driving a VEHICLE
echo Vehicle::invokeStopBySelf(); // I'm stopping a VEHICLE
// !!! This is Late Static Binding !!!!
// Child class invokes an inherited method from Parent.
// Child class = Car, Inherited method = invokeDriveByStatic().
// The inherited method invokes a method that is overridden by the Child class.
// Overridden method = drive()
echo Car::invokeDriveByStatic(); // I'm driving a CAR
// This is NOT Late Static Binding
// Child class invokes an inherited method from Parent.
// The inherited method invokes a method inside the Vehicle context.
echo Car::invokeStopBySelf(); // I'm stopping a VEHICLE
static
Ключевое слово используется в шаблоне проектирования Singleton. Смотрите ссылку: https://refactoring.guru/design-patterns/singleton/php/example
Самый простой пример, чтобы показать разницу.
Обратите внимание, self :: $ c
class A
{
static $c = 7;
public static function getVal()
{
return self::$c;
}
}
class B extends A
{
static $c = 8;
}
B::getVal(); // 7
Позднее статическое связывание, обратите внимание на статическое :: $ c
class A
{
static $c = 7;
public static function getVal()
{
return static::$c;
}
}
class B extends A
{
static $c = 8;
}
B::getVal(); // 8
Также смотрите, обновляете ли вы статические переменные в дочерних классах. Я нашел этот (несколько) неожиданный результат, когда дочерний B обновляет дочерний C:
class A{
protected static $things;
}
class B extends A {
public static function things(){
static::$things[1] = 'Thing B';
return static::$things;
}
}
class C extends A{
public static function things(){
static::$things[2] = 'Thing C';
return static::$things;
}
}
print_r(C::things());
// Array (
// [2] => Thing C
// )
B::things();
print_r(C::things());
// Array (
// [2] => Thing C
// [1] => Thing B
// )
Вы можете исправить это, объявив одну и ту же переменную в каждом дочернем классе, например:
class C extends A{
protected static $things; // add this and B will not interfere!
public static function things(){
static::$things[2] = 'Thing C';
return static::$things;
}
}