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

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

7 ответов

Решение

Начиная с 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. Тем не менее, я постараюсь дать вам краткое резюме.

В основном это сводится к тому, что 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".

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

Я цитирую из книги: "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контекст - это тот, где вы определили метод изначально. С staticэто тот, от кого ты звонишь.

Например:

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

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