Odpowiedzi:
Zdecydowanie musisz przeczytać Late Static Bindings w podręczniku PHP. Spróbuję jednak krótko podsumować.
Zasadniczo sprowadza się to do tego, że self
słowo kluczowe nie podlega tym samym regułom dziedziczenia. self
zawsze 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, self
nie będzie odnosić się do dziecka, jak można by się tego spodziewać.
Późne wiązanie statyczne wprowadza nowe użycie static
sł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
, parent
i static
działa, gdy static
jest 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.
self
słowo kluczowe nie jest zgodne z regułami dziedziczenia. self
zawsze 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.
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ć,
static
któ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 przezself::
,parent::
,static::
lub, jeśli idzie w górę w hierarchii klasowejforward_static_call()
. Funkcjaget_called_class()
może służyć do pobrania ciągu znaków z nazwą wywoływanej klasy istatic::
wprowadzenia jej zakresu.
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”.
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 static
kluczowe jest używane we wzorcu projektowym Singleton. Zobacz link: https://refactoring.guru/design-patterns/singleton/php/example
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
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 self
przypadku kontekstem jest ten, w którym pierwotnie zdefiniowano metodę. Z static
, to ten, z którego dzwonisz.
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;
}
}