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

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

Ответ 1

Начиная с 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:: вводит ее область действия.

Ответ 2

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

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

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

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

Ответ 3

Существует не очень очевидное поведение:

Следующий код создает "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".

Ответ 4

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

Ответ 5

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

Ответ 6

Например:

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();

Ответ 7

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

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

Ответ 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;        
    }
}