To długa, smutna historia.
Kiedy PHP 5.2 po raz pierwszy wprowadziło to ostrzeżenie, późne statyczne wiązania nie były jeszcze w języku. Jeśli nie jesteś zaznajomiony z późnymi wiązaniami statycznymi, zwróć uwagę, że taki kod nie działa tak, jak można się spodziewać:
<?php
abstract class ParentClass {
static function foo() {
echo "I'm gonna do bar()";
self::bar();
}
abstract static function bar();
}
class ChildClass extends ParentClass {
static function bar() {
echo "Hello, World!";
}
}
ChildClass::foo();
Pomijając ostrzeżenie o trybie ścisłym, powyższy kod nie działa. self::bar()
Rozmowy foo()
wyraźnie odnosi się do bar()
metody ParentClass
, nawet jeśli foo()
jest nazywany jako metody ChildClass
. Jeśli spróbujesz uruchomić ten kod przy wyłączonym trybie ścisłym, zobaczysz komunikat „ Błąd krytyczny PHP: nie można wywołać metody abstrakcyjnej ParentClass :: bar () ”.
Biorąc to pod uwagę, abstrakcyjne metody statyczne w PHP 5.2 były bezużyteczne. Cały punkt z zastosowaniem metody abstrakcyjne, że można napisać kod, który wywołuje metodę, nie wiedząc, co to realizacja będzie powołanie - a następnie dostarczyć różne implementacje na różnych klasach potomnych. Ale ponieważ PHP 5.2 nie oferuje prostego sposobu na napisanie metody klasy nadrzędnej, która wywołuje statyczną metodę klasy potomnej, na której jest wywoływana, takie użycie abstrakcyjnych metod statycznych nie jest możliwe. Stąd jakiekolwiek użycie abstract static
w PHP 5.2 jest złym kodem, prawdopodobnie zainspirowanym niezrozumieniem działania self
słowa kluczowego. Było całkowicie rozsądne, aby ostrzec w tej sprawie.
Ale potem pojawił się PHP 5.3 z możliwością odwoływania się do klasy, dla której metoda została wywołana za pomocą static
słowa kluczowego (w przeciwieństwie do self
słowa kluczowego, które zawsze odnosi się do klasy, w której metoda została zdefiniowana ). Jeśli zmienisz self::bar()
na static::bar()
w moim przykładzie powyżej, działa dobrze w PHP 5.3 i nowszych. Możesz przeczytać więcej o self
vs static
at Nowa jaźń kontra nowa statystyka .
Po dodaniu słowa kluczowego static wyraźny argument za abstract static
wysłaniem ostrzeżenia zniknął. Głównym celem późnych wiązań statycznych było umożliwienie metodom zdefiniowanym w klasie nadrzędnej wywoływanie metod statycznych, które zostałyby zdefiniowane w klasach potomnych; dopuszczenie abstrakcyjnych metod statycznych wydaje się rozsądne i spójne, biorąc pod uwagę istnienie późnych wiązań statycznych.
Wydaje mi się, że nadal możesz argumentować za utrzymaniem ostrzeżenia. Na przykład, można argumentować, że skoro PHP umożliwia wywoływanie metod statycznych klas abstrakcyjnych, w moim przykładzie powyżej (nawet po zamontowaniu go zastępując self
z static
) jesteś narażając sposób publiczny ParentClass::foo()
, który jest uszkodzony i że tak naprawdę nie chcą expose. Użycie klasy niestatycznej - to znaczy uczynienie wszystkich metod instancjami metodami i uczynienie ich elementów potomnych ParentClass
jako singletonów lub coś w tym rodzaju - rozwiązałoby ten problem, ponieważ ParentClass
będąc abstrakcyjnymi, nie można utworzyć instancji, więc metody instancji nie mogą Zostać wezwanym. Myślę, że ten argument jest słaby (ponieważ myślę, że ujawniamParentClass::foo()
to nic wielkiego i używanie singletonów zamiast klas statycznych jest często niepotrzebnie rozwlekłe i brzydkie), ale możesz się z tym nie zgodzić - jest to nieco subiektywne wezwanie.
Bazując na tym argumencie, twórcy PHP zachowali ostrzeżenie w języku, prawda?
Uh, nie dokładnie .
Raport o błędzie PHP 53081, do którego łącze powyżej, wezwał do usunięcia ostrzeżenia, ponieważ dodanie static::foo()
konstrukcji sprawiło, że abstrakcyjne metody statyczne stały się rozsądne i użyteczne. Rasmus Lerdorf (twórca PHP) zaczyna od oznaczenia żądania jako fałszywego i przechodzi przez długi łańcuch złych argumentów, próbując uzasadnić ostrzeżenie. W końcu dochodzi do wymiany:
Giorgio
wiem, ale:
abstract class cA
{
//static function A(){self::B();} error, undefined method
static function A(){static::B();} // good
abstract static function B();
}
class cB extends cA
{
static function B(){echo "ok";}
}
cB::A();
Rasmus
Dokładnie tak to powinno działać.
Giorgio
ale nie jest dozwolone :(
Rasmus
Co nie jest dozwolone?
abstract class cA {
static function A(){static::B();}
abstract static function B();
}
class cB extends cA {
static function B(){echo "ok";}
}
cB::A();
To działa dobrze. Oczywiście nie możesz wywołać self :: B (), ale static :: B () jest w porządku.
Twierdzenie Rasmusa, że kod w jego przykładzie „działa dobrze” jest fałszywe; jak wiesz, generuje ostrzeżenie o trybie ścisłym. Wydaje mi się, że testował bez włączonego trybu ścisłego. Mimo wszystko zdezorientowany Rasmus pozostawił prośbę błędnie zamkniętą jako „fałszywą”.
I dlatego ostrzeżenie jest nadal w języku. Może to nie być w pełni satysfakcjonujące wyjaśnienie - prawdopodobnie przyszedłeś tutaj z nadzieją, że ostrzeżenie zostało racjonalnie uzasadnione. Niestety, w prawdziwym świecie czasami wybory rodzą się z przyziemnych błędów i złego rozumowania, a nie z racjonalnego podejmowania decyzji. To po prostu jeden z tych czasów.
Na szczęście szacowny Nikita Popov usunął ostrzeżenie z języka w PHP 7 jako część notatek PHP RFC: Reclassify E_STRICT . Ostatecznie zwyciężył rozsądek i po wydaniu PHP 7 wszyscy możemy z radością korzystać abstract static
bez otrzymywania tego głupiego ostrzeżenia.