2015-09-24 17 views
10

Con il binding statico tardivo in PHP v5.3, è possibile dichiarare utilmente i metodi static nelle interfacce; con i tratti in PHP v5.4, i metodi possono essere sia static o abstract ma non entrambi. Questo sembra essere illogico e incoerente.Perché i caratteri PHP non hanno metodi astratti statici?

In particolare, si supponga di avere un'interfaccia per la quale un tratto fornisce tutte le implementazioni, ad eccezione di un metodo statico; a meno che tale metodo non sia dichiarato nel tratto, gli analizzatori statici si bloccano su qualsiasi riferimento ad essi all'interno del carattere. Ma fornire un'implementazione concreta all'interno della caratteristica non costringe più a implementare/utilizzare le classi per fornire la propria implementazione, il che è pericoloso; abstract static sarebbe l'ideale, ma non è permesso.

Qual è la spiegazione di questa contraddizione? Come consiglieresti di risolvere questo problema?

interface MyInterface 
{ 
    public static function getSetting(); 
    public function doSomethingWithSetting(); 
} 

trait MyTrait 
{ 
    public abstract static function getSetting(); // I want this... 

    public function doSomethingWithSetting() { 
     $setting = static::getSetting(); // ...so that I can do this 
     /* ... */ 
    } 
} 

class MyClass implements MyInterface 
{ 
    use MyTrait; 
    public static function getSetting() { return /* ... */ } 
} 
+0

Ho provato quello che vuoi in php 5.6.10 (CLI) e ha funzionato .. Ti ho sbagliato o lo vuoi per php 5.4? – Mjh

+1

@Mjh: qual è la tua impostazione ['error_reporting'] (https://secure.php.net/manual/en/errorfunc.configuration.php#ini.error-reporting)? Quanto sopra dovrebbe dare luogo ad un errore 'E_STRICT'. – eggyal

+0

Ho impostato 'error_reporting (E_ALL);'. Ho dichiarato un tratto con 'public static static function test();' e ho ottenuto 'PHP Strict Standards: la funzione statica MyTest :: test() non dovrebbe essere astratta nel codice shell php sulla riga 1'. Quando creo una classe che utilizza il tratto e se non implemento il metodo astratto, ottengo l'errore 'PHP Fatal: Class MyTestClass contiene 1 metodo astratto e deve quindi essere dichiarato astratto o implementare i restanti metodi (MyTestClass :: test) nel codice della shell php sulla linea 1'. Questo aiuta affatto? – Mjh

risposta

5

TL; DR: È possibile definire abstract static su trait s, ma internals reputa cattiva pratica e può rimuovere in futuro.

Non ho avuto molta caffeina, ma mi darò un colpo.

Strettamente, abstract significa che la sottoclasse deve implementare e static significa codice solo per questa classe specifica. Presi insieme, abstract static significa "la sottoclasse deve implementare il codice solo per questa specifica classe". Concetti totalmente ortogonali.

Ma ... PHP 5.3+ supporta l'ereditarietà statica grazie a LSB. In pratica, apriamo un po 'la definizione: self prende la precedente definizione di static, mentre static diventa "codice per questa classe specifica o una delle sue sottoclassi". La nuova definizione di abstract static è "la sottoclasse deve implementare il codice per questa classe specifica o una delle sue sottoclassi". Questo può portare ad alcune persone che pensano allo static in senso stretto, alla confusione. Vedi ad esempio bug #53081.

Cosa rende trait così speciale da suscitare questo avviso? Beh, date un'occhiata al engine code che implementa l'avviso:

if (ptr->flags & ZEND_ACC_STATIC && (!scope || !(scope->ce_flags & ZEND_ACC_INTERFACE))) { 
    zend_error(error_type, "Static function %s%s%s() cannot be abstract", scope ? ZSTR_VAL(scope->name) : "", scope ? "::" : "", ptr->fname); 
} 

Questo codice dice il luogo solo un abstract static è consentito è all'interno di un interface. Non è unico per i tratti, è unico per la definizione di abstract static. Perché? Beh, notare c'è un leggero caso angolo nella nostra definizione:

sub-class must implement code for this specific class or any of its sub-classes

Con questo codice:

abstract class Foo { 
    abstract public static function get(); 
} 

Tale definizione significa che dovrei in grado di chiamare Foo::get. Dopotutto lo Foo è una classe (si veda la parola chiave "classe" lì) e nella definizione rigorosa, get è pensato per essere implementato in quella classe . Ma chiaramente questo non ha senso, perché, beh, siamo tornati all'ortogonalità della staticità severa.

Se lo provate in PHP, si ottiene l'unica risposta razionale possibile:

Cannot call abstract method Foo::get()

Quindi perché PHP aggiunto eredità statica, deve fare i conti con questi casi d'angolo. Questa è la natura delle caratteristiche. Alcune altre lingue (C#, Java, ecc.) Non presentano questo problema, perché adottano la definizione rigorosa e semplicemente non consentono abstract static. Per sbarazzarci di questo caso d'angolo e semplificare il motore, potremmo applicare questa regola "abstract static only in interface" in futuro. Quindi, E_STRICT.


userei un delegato del servizio per risolvere il problema:

I have common method I want to use in several classes. This common method relies on a static method that must be defined externally to the common code.

trait MyTrait 
{ 
    public function doSomethingWithSetting() { 
     $service = new MyService($this); 
     return $service->doSomethingWithSetting(); 
    } 
} 

class MyService 
{ 
    public function __construct(MyInterface $object) { 
     $this->object = $object; 
    } 
    public function doSomethingWithSetting() { 
     $setting = $this->object->getSetting(); 
     return $setting; 
    } 
} 

sente un po 'di Rube Goldberg però. Probabilmente guarderebbero alla motivazione per la statica e prendere in considerazione la possibilità di ridefinirli.

+0

Concordo con la tua analisi di 'abstract' vs' static' e il suo significato contraddittorio nel contesto di * ereditarietà statica *. Tuttavia, quando un * trait * dichiara che un metodo è 'astratto', esso * può * essere implementato all'interno della classe using, proprio come i metodi' static' dichiarati nelle interfacce possono essere implementati all'interno della classe di implementazione. Quindi la contraddizione che evidenzi non significa più tratti di quanto non faccia per le interfacce, laddove tale uso è consentito. Citando il codice del motore che causa l'errore, si rinforza solo il fatto che si tratta di una svista/bug, non che si tratta di una decisione deliberata di progettazione. – eggyal

Problemi correlati