Что такое поздние статические привязки в PHP?

Ответы:

198

Вам обязательно нужно прочитать Поздние статические привязки в руководстве по PHP. Тем не менее, я постараюсь дать вам краткое резюме.

По сути, это сводится к тому, что selfключевое слово не следует тем же правилам наследования. selfвсегда разрешается в классе, в котором он используется. Это означает, что если вы создадите метод в родительском классе и вызовете его из дочернего класса, selfон не будет ссылаться на дочерний класс, как вы могли бы ожидать.

Позднее статическое связывание вводит новое использование staticключевого слова, которое устраняет этот конкретный недостаток. Когда вы используете static, он представляет класс, где вы впервые используете его, т.е. он «привязывается» к классу времени выполнения.

Это две основные концепции, стоящие за этим. Путь self, parentи staticработать , когда staticв игре может быть тонкими, так что вместо того , чтобы пойти , чтобы более подробно, я настоятельно рекомендую вам изучить руководство примеров страниц. Как только вы поймете основы каждого ключевого слова, примеры станут необходимыми, чтобы увидеть, какие результаты вы собираетесь получить.

zombat
источник
я нашел эту статью действительно полезной и описательной, зацените ее [ссылка] ( techflirt.com/tutorials/oop-in-php/late-static-binding.html )
Садег Шейхи,
«... selfключевое слово не соответствует правилам наследования. selfвсегда разрешает класс, в котором оно используется». - Это не означает, что вы не можете вызывать статический метод родителя через дочерний объект self, как с нестатическими методами. Возможно, вы имеете в виду правильные вещи, но вы должны перефразировать это. Все это действительно имеет значение, только если у детей есть члены с одинаковыми именами, так как вы можете решить, на кого ссылаться, используя static::вместо этого.
DanMan
81

Из 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::представления ее области действия.

Мринмой Гошал
источник
1
Этот пост составляет ~ 80% дословной копии статьи php.net без маркеров цитирования.
ВудроШигеру
22

Там не очень очевидное поведение:

Следующий код производит '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».

Jokerius
источник
1
Очень хорошо. То же самое показано в руководстве по PHP, но это гораздо понятнее. Для справки: php.net/manual/en/language.oop5.late-static-bindings.php (см.
Пример
11

Я цитирую из книги: «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

Джулиан
источник
7

Самый простой пример, чтобы показать разницу.
Обратите внимание, 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
Сергей Онищенко
источник
4

Например:

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();
Петах
источник
4

Глядя на это с "почему я бы использовал это?" в перспективе это в основном способ изменить контекст, из которого интерпретируется / запускается статический метод.

С selfконтекстом является тот, где вы определили метод первоначально. С staticтого, с кого ты звонишь.

DanMan
источник
1

Также смотрите, обновляете ли вы статические переменные в дочерних классах. Я нашел этот (несколько) неожиданный результат, когда дочерний 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;        
    }
}
Фрэнк Форте
источник