Что такое поздние статические привязки в PHP? - PullRequest
123 голосов
/ 16 декабря 2009

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

Ответы [ 8 ]

185 голосов
/ 16 декабря 2009

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

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

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

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

78 голосов
/ 22 декабря 2012

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

22 голосов
/ 19 ноября 2013

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

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

10 голосов
/ 18 июня 2014

Я цитирую из книги: «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 голосов
/ 23 февраля 2016

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

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

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

4 голосов
/ 09 сентября 2010

Например:

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();
1 голос
/ 03 мая 2019

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