Які саме пізні статичні прив’язки в PHP?


Відповіді:


198

Вам обов'язково потрібно прочитати пізні статичні прив’язки в посібнику PHP. Однак я спробую дати вам короткий підсумок.

В основному, це зводиться до того, що selfключове слово не відповідає тим самим правилам успадкування. selfзавжди відповідає класу, в якому він використовується. Це означає, що якщо ви робите метод у батьківському класі та називаєте його з дочірнього класу, selfвін не посилатиметься на дитину, як ви могли очікувати.

Пізня статична прив'язка вводить нове використання для staticключового слова, яке вирішує цей конкретний недолік. Коли ви використовуєте static, він представляє клас, де ви вперше його використовуєте, тобто. він 'прив'язується' до класу виконання.

Це два основних поняття. Шлях self, parentі staticпрацювати , коли staticв грі може бути тонкими, так що замість того , щоб піти , щоб більш детально, я настійно рекомендую вам вивчити керівництво прикладів сторінок. Коли ви зрозумієте основи кожного ключового слова, приклади цілком необхідні, щоб побачити, які результати ви збираєтеся отримати.


Я вважав цю статтю дійсно корисною та описовою, ознайомтесь із нею [посилання] ( techflirt.com/tutorials/oop-in-php/late-static-binding.html )
Sadegh Shaikhi

"... 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 без посилань на посилання.
WoodrowShigeru

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'.


1
Дуже хороша. Те саме показано в керівництві PHP, але це набагато зрозуміліше. Довідково: php.net/manual/en/language.oop5.late-static-bindings.php (див. Приклад 4)
musicin3d

11

Я цитую з книги: "PHP Master написати передовий код".

Пізнє статичне зв'язування було особливістю, введеною в php 5.3. Це дозволяє нам успадковувати статичні методи від батьківського класу та посилатись на дочірній клас, який викликається.

Це означає, що ви можете мати абстрактний клас зі статичними методами та посилатися на конкретні реалізації дочірнього класу, використовуючи позначення static :: method () замість методу self :: ().

Не соромтеся також ознайомитися з офіційною документацією на 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

Найпростіший приклад показати різницю.
Зауважте, самості: $ 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, це той, з якого ви його називаєте.


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;        
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.