È una storia lunga e triste.
Quando PHP 5.2 ha introdotto per la prima volta questo avviso, i collegamenti statici recenti non erano ancora presenti nel linguaggio. Nel caso in cui non si abbia familiarità con i binding statici tardivi, si noti che il codice come questo non funziona nel modo in cui ci si potrebbe aspettare:
<?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();
Lasciando da parte l'avviso della modalità rigorosa, il codice sopra non funziona. La self::bar()
chiamata in si foo()
riferisce esplicitamente al bar()
metodo di ParentClass
, anche quando foo()
viene chiamata come metodo di ChildClass
. Se provi a eseguire questo codice con la modalità rigorosa disattivata, vedrai " Errore irreversibile PHP: impossibile chiamare il metodo astratto ParentClass :: bar () ".
Detto questo, i metodi statici astratti in PHP 5.2 erano inutili. Il punto centrale dell'utilizzo di un metodo astratto è che puoi scrivere codice che chiama il metodo senza sapere quale implementazione verrà chiamata e quindi fornire implementazioni diverse su classi figlie diverse. Ma poiché PHP 5.2 non offre un modo pulito per scrivere un metodo di una classe genitore che chiama un metodo statico della classe figlia su cui è chiamato, questo utilizzo di metodi statici astratti non è possibile. Quindi qualsiasi utilizzo di abstract static
in PHP 5.2 è un codice errato, probabilmente ispirato da un malinteso sul funzionamento della self
parola chiave. Era del tutto ragionevole lanciare un avvertimento su questo.
Ma poi è arrivato PHP 5.3 aggiunto nella possibilità di fare riferimento alla classe su cui un metodo è stato chiamato tramite la static
parola chiave (a differenza della self
parola chiave, che si riferisce sempre alla classe in cui il metodo è stato definito ). Se cambi self::bar()
in static::bar()
nel mio esempio sopra, funziona bene in PHP 5.3 e versioni successive. Puoi leggere di più su self
vs static
in New self vs. new static .
Con la parola chiave statica aggiunta, l'argomento chiaro per aver abstract static
lanciato un avviso era sparito. Lo scopo principale dei binding statici tardivi era di consentire ai metodi definiti in una classe genitore di chiamare metodi statici che sarebbero stati definiti nelle classi figlie; consentire metodi statici astratti sembra ragionevole e coerente data l'esistenza di binding statici tardivi.
Potresti ancora, immagino, giustificare il mantenimento dell'avvertimento. Ad esempio, potresti sostenere che poiché PHP ti consente di chiamare metodi statici di classi astratte, nel mio esempio sopra (anche dopo averlo risolto sostituendolo self
con static
) stai esponendo un metodo pubblico ParentClass::foo()
che è rotto e che non vuoi davvero esporre. L'utilizzo di una classe non statica, ovvero, rendendo tutti i metodi metodi di istanza e facendo in modo che i figli di ParentClass
tutti siano singleton o qualcosa del genere, risolverebbe questo problema, poiché ParentClass
, essendo astratto, non può essere istanziato e quindi i suoi metodi di istanza non possono essere chiamato. Penso che questo argomento sia debole (perché penso che esporreParentClass::foo()
non è un grosso problema e l'uso di singleton invece di classi statiche è spesso inutilmente prolisso e brutto), ma potresti ragionevolmente non essere d'accordo - è una chiamata in qualche modo soggettiva.
Quindi, sulla base di questo argomento, gli sviluppatori PHP hanno mantenuto l'avviso nella lingua, giusto?
Uh, non esattamente .
Il bug report di PHP 53081, collegato sopra, richiedeva di eliminare l'avvertimento poiché l'aggiunta del static::foo()
costrutto aveva reso ragionevoli e utili metodi statici astratti. Rasmus Lerdorf (creatore di PHP) inizia etichettando la richiesta come fasulla e passa attraverso una lunga catena di ragionamenti sbagliati per cercare di giustificare l'avvertimento. Quindi, finalmente, avviene questo scambio:
Giorgio
lo so ma:
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
Bene, è esattamente come dovrebbe funzionare.
Giorgio
ma non è consentito :(
Rasmus
Cosa non è permesso?
abstract class cA {
static function A(){static::B();}
abstract static function B();
}
class cB extends cA {
static function B(){echo "ok";}
}
cB::A();
Funziona bene. Ovviamente non puoi chiamare self :: B (), ma static :: B () va bene.
L'affermazione di Rasmus che il codice nel suo esempio "funziona bene" è falsa; come sai, genera un avviso di modalità rigorosa. Immagino che stesse testando senza la modalità rigorosa attivata. Indipendentemente da ciò, un Rasmus confuso ha lasciato la richiesta erroneamente chiusa come "fasulla".
Ed è per questo che l'avvertimento è ancora nella lingua. Questa potrebbe non essere una spiegazione del tutto soddisfacente: probabilmente sei venuto qui sperando che ci fosse una giustificazione razionale dell'avvertimento. Sfortunatamente, nel mondo reale, a volte le scelte nascono da errori banali e da un cattivo ragionamento piuttosto che da un processo decisionale razionale. Questa è semplicemente una di quelle volte.
Fortunatamente, la stimabile Nikita Popov ha rimosso l'avviso dal linguaggio in PHP 7 come parte degli avvisi PHP RFC: Reclassify E_STRICT . Alla fine, la sanità mentale ha prevalso e una volta rilasciato PHP 7 possiamo usarlo felicemente abstract static
senza ricevere questo stupido avvertimento.