Czym dokładnie są późne wiązania statyczne w PHP?

Odpowiedzi:

198

Zdecydowanie musisz przeczytać Late Static Bindings w podręczniku PHP. Spróbuję jednak krótko podsumować.

Zasadniczo sprowadza się to do tego, że selfsłowo kluczowe nie podlega tym samym regułom dziedziczenia. selfzawsze odnosi się do klasy, w której jest używany. Oznacza to, że jeśli utworzysz metodę w klasie nadrzędnej i wywołasz ją z klasy potomnej, selfnie będzie odnosić się do dziecka, jak można by się tego spodziewać.

Późne wiązanie statyczne wprowadza nowe użycie staticsłowa kluczowego, które rozwiązuje tę konkretną wadę. Kiedy używasz static, reprezentuje klasę, w której używasz go po raz pierwszy, tj. „wiąże się” z klasą wykonawczą.

To są dwie podstawowe koncepcje, które za tym stoją. Droga self, parenti staticdziała, gdy staticjest w grze może być subtelna, więc zamiast iść do bardziej szczegółowo, bym zdecydowanie zaleca się badać ręcznych przykłady stron. Kiedy zrozumiesz podstawy każdego słowa kluczowego, przykłady są niezbędne, aby zobaczyć, jakie wyniki uzyskasz.

zombat
źródło
Uważam, że ten artykuł jest naprawdę przydatny i opisowy, sprawdź go [link] ( techflirt.com/tutorials/oop-in-php/late-static-binding.html )
Sadegh Shaikhi
„... selfsłowo kluczowe nie jest zgodne z regułami dziedziczenia. selfzawsze jest przypisywane do klasy, w której zostało użyte.” - Co nie oznacza, że ​​nie można wywołać statycznej metody rodzica z obiektu podrzędnego za pośrednictwem self, tak jak w przypadku metod niestatycznych. Może masz na myśli właściwą rzecz, ale powinieneś to przeformułować. To wszystko ma znaczenie tylko wtedy, gdy dzieci mają identyczne nazwy członków, ponieważ możesz zdecydować, do których z nich się odwoływać, używając static::zamiast tego.
DanMan
81

Z PHP: Late Static Bindings - Manual :

Począwszy od PHP 5.3.0, PHP implementuje funkcję zwaną późnym wiązaniem statycznym, która może być używana do odwoływania się do wywoływanej klasy w kontekście dziedziczenia statycznego.

Późne wiązanie statyczne próbuje rozwiązać to ograniczenie, wprowadzając słowo kluczowe, które odwołuje się do klasy, która została początkowo wywołana w czasie wykonywania. ... Zdecydowano nie wprowadzać nowego słowa kluczowego, ale raczej użyć, staticktóre było już zastrzeżone.

Zobaczmy przykład:

<?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
?>

Późne powiązania statyczne działają poprzez przechowywanie klasy nazwanej w ostatnim „wywołaniu nieprzekierowującym”. W przypadku wywołań metod statycznych jest to klasa o jawnej nazwie (zwykle ta po lewej stronie ::operatora); w przypadku wywołań metod niestatycznych jest to klasa obiektu. A „Przekazywanie połączeń” jest statycznym, który został wprowadzony przez self::, parent::, static::lub, jeśli idzie w górę w hierarchii klasowej forward_static_call(). Funkcja get_called_class()może służyć do pobrania ciągu znaków z nazwą wywoływanej klasy i static::wprowadzenia jej zakresu.

Mrinmoy Ghoshal
źródło
1
Ten post jest w ~ 80% dosłowną kopią artykułu php.net bez znaczników cytowania.
WoodrowShigeru
22

Nie ma zbyt oczywistego zachowania:

Poniższy kod tworzy „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

Jeśli jednak usuniemy deklarację funkcji classname z klasy beta, otrzymamy jako wynik „alphaalpha”.

Jokerius
źródło
1
Bardzo dobrze. To samo jest pokazane w podręczniku PHP, ale jest to znacznie jaśniejsze. Dla odniesienia: php.net/manual/en/language.oop5.late-static-bindings.php (patrz przykład 4)
musicin3d
11

Cytuję z książki: „PHP Master pisze nowatorski kod”.

Późne wiązanie statyczne było funkcją wprowadzoną w php 5.3. Pozwala nam dziedziczyć statyczne metody z klasy nadrzędnej i odwoływać się do wywoływanej klasy potomnej.

Oznacza to, że możesz mieć klasę abstrakcyjną z metodami statycznymi i odwoływać się do konkretnych implementacji klasy potomnej za pomocą notacji static :: method () zamiast self :: method ().

Zapraszam również do zapoznania się z oficjalną dokumentacją php: http://php.net/manual/en/language.oop5.late-static-bindings.php


Najprostszym sposobem wyjaśnienia późnego wiązania statycznego jest prosty przykład. Przyjrzyj się dwóm definicjom klas poniżej i czytaj dalej.

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";
    }
}

Widzimy klasę rodzica (pojazd) i klasę dziecka (samochód). Klasa nadrzędna ma 2 metody publiczne:

  • invokeDriveByStatic
  • invokeStopBySelf

Klasa nadrzędna ma również 2 prywatne metody:

  • drive
  • stop

Klasa Child przesłania dwie metody:

  • drive
  • stop

Teraz wywołajmy metody publiczne:

  • invokeDriveByStatic
  • invokeStopBySelf

Zadaj sobie pytanie: która klasa wywołuje invokeDriveByStatic/ invokeStopBySelf? Klasa rodzica czy dziecka?

Spójrz poniżej:

// 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

Słowo statickluczowe jest używane we wzorcu projektowym Singleton. Zobacz link: https://refactoring.guru/design-patterns/singleton/php/example

Julian
źródło
7

Najprostszy przykład pokazujący różnicę.
Uwaga, self :: $ c

class A
{
    static $c = 7;

    public static function getVal()
    {
        return self::$c;
    }
}

class B extends A
{
    static $c = 8;
}

B::getVal(); // 7

Późne wiązanie statyczne, uwaga static :: $ c

class A
{
    static $c = 7;

    public static function getVal()
    {
        return static::$c;
    }
}

class B extends A
{
    static $c = 8;
}

B::getVal(); // 8
Siergiej Oniszczenko
źródło
4

Na przykład:

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();
Petah
źródło
4

Patrząc na to z punktu „po co miałbym tego używać?” z perspektywy, jest to w zasadzie sposób na zmianę kontekstu, z którego metoda statyczna jest interpretowana / uruchamiana.

W selfprzypadku kontekstem jest ten, w którym pierwotnie zdefiniowano metodę. Z static, to ten, z którego dzwonisz.

DanMan
źródło
1

Zwróć także uwagę na aktualizację zmiennych statycznych w klasach potomnych. Znalazłem ten (nieco) nieoczekiwany wynik, w którym dziecko B aktualizuje dziecko 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
// )

Możesz to naprawić, deklarując tę ​​samą zmienną w każdej klasie potomnej, na przykład:

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;        
    }
}
Frank Forte
źródło