2015-09-24 6 views
10

Mit späte statische Bindung in PHP v5.3, kann man static Methoden in Schnittstellen sinnvoll deklarieren; mit Merkmalen in PHP v5.4, Methoden können entweder static oder abstract aber nicht beides sein. Dies scheint unlogisch und widersprüchlich zu sein.Warum können PHP-Merkmale keine statischen abstrakten Methoden haben?

Angenommen, man hat eine Schnittstelle, für die eine Eigenschaft alle Implementierungen bereitstellt, außer für eine statische Methode; Wenn diese Methode nicht im Merkmal deklariert wird, stoßen statische Analysatoren bei allen Referenzen darauf innerhalb des Merkmals zurück. Aber die Bereitstellung einer konkreten Implementierung innerhalb des Merkmals erzwingt nicht mehr das Implementieren/Verwenden von Klassen, um ihre eigene Implementierung bereitzustellen - was gefährlich ist; abstract static wäre ideal, ist aber nicht erlaubt.

Was ist die Erklärung für diesen Widerspruch? Wie empfehlen Sie, dieses Problem zu lösen?

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

Ich habe versucht, was Sie wollen in PHP 5.6.10 (CLI) und es hat funktioniert .. Habe ich dich falsch verstanden oder willst du das für PHP 5.4? – Mjh

+1

@Mjh: Was ist Ihre Einstellung ['error_reporting'] (https://secure.php.net/manual/en/errorfunc.configuration.php#ini.error-reporting)? Das obige sollte zu einem "E_STRICT" -Fehler führen. – eggyal

+0

Ich habe die 'error_reporting (E_ALL);'. Ich erklärte ein Merkmal mit 'öffentlichem abstraktem statischem Funktionstest();' und ich erhielt 'PHP strenge Standards: Statische Funktion MyTest :: test() sollte nicht abstrakt im PHP-Shell-Code auf Leitung 1 sein'. Wenn ich eine Klasse erzeuge, die das Merkmal verwendet, und wenn ich die abstrakte Methode nicht implementiere, bekomme ich 'PHP Fatal error: Die Klasse MyTestClass enthält 1 abstrakte Methode und muss daher als abstrakt deklariert werden oder die restlichen Methoden implementieren (MyTestClass :: test) in PHP-Shell-Code in Zeile 1'. Hilft das überhaupt? – Mjh

Antwort

5

TL; DR: Sie können definieren abstract static auf trait s, aber Interna hält es schlechte Praxis und es in Zukunft entfernen.

Ich hatte nicht viel Koffein, aber ich werde einen Riss geben.

Ausschließlich abstract bedeutet, dass die Unterklasse implementiert werden muss, und static bedeutet nur Code für diese spezifische Klasse. Zusammengenommen bedeutet abstract static "Unterklasse muss Code nur für diese spezifische Klasse implementieren". Total orthogonale Konzepte.

Aber ... PHP 5.3+ unterstützt statische Vererbung dank LSB. Also öffnen wir diese Definition tatsächlich ein wenig: self nimmt die frühere Definition von statisch, während static wird "Code für diese bestimmte Klasse oder einer seiner Unterklassen". Die neue Definition von abstract static lautet "Unterklasse muss Code für diese bestimmte Klasse oder eine ihrer Unterklassen implementieren". Dies kann dazu führen, dass einige Leute, die an static im engeren Sinne denken, zu Verwirrung führen. Siehe zum Beispiel bug #53081.

Was macht trait so besonders, diese Warnung auszulösen? Nun, werfen Sie einen Blick auf die engine code, die die Bekanntmachung implementiert:

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

Dieser Code sagt der nur Ort ein abstract static darf innerhalb eines interface ist. Es ist nicht einzigartig für Merkmale, es ist einzigartig in der Definition von abstract static. Warum? Nun, bemerken es gibt eine leichte Ecke Fall in unserer Definition:

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

Mit diesem Code:

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

Diese Definition bedeutet, dass ich sollte Lage sein Foo::get zu nennen. Nach allem Foo ist eine Klasse (siehe das Schlüsselwort "Klasse" dort) und in der strengen Definition, get soll in dieser Klasse Foo implementiert werden. Aber klar, das ergibt keinen Sinn, denn nun sind wir wieder bei der Orthogonalität der strikten Statik.

Wenn Sie es in PHP versuchen, erhalten Sie die einzige rationale Antwort möglich:

Cannot call abstract method Foo::get()

So weil PHP statische Vererbung hinzugefügt, es hat mit dieser Ecke Fälle zu behandeln. Dies ist die Natur von Merkmalen. Einige andere Sprachen (C#, Java, usw.) haben dieses Problem nicht, weil sie die strenge Definition übernehmen und abstract static einfach nicht erlauben. Um diesen Eckfall loszuwerden und die Engine zu vereinfachen, können wir diese "abstrakte statische nur in der Schnittstelle" Regel in der Zukunft erzwingen. Daher E_STRICT.


würde ich einen Dienst delegieren, um das Problem zu lösen: wenn

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

fühlt sich ein bisschen Rube Goldberg. Wahrscheinlich würde man sich die Motivation für die Statik anschauen und überlegen, sie zu refaktorisieren.

+0

Ich stimme Ihrer Analyse von "abstrakt" gegen "statisch" und ihrer widersprüchlichen Bedeutung im Kontext der * statischen Vererbung * zu. Wenn jedoch ein * Merkmal * eine Methode als "abstrakt" deklariert, kann sie * in der using-Klasse * implementiert werden - so wie 'statische' in Schnittstellen deklarierte Methoden innerhalb der implementierenden Klasse implementiert werden können. Der Widerspruch, den Sie hervorheben, steht also nicht mehr für Merkmale, als für Schnittstellen, wo solche Verwendung erlaubt ist. Das Zitieren des Motorcodes, der den Fehler verursacht, bestätigt nur, dass dies ein Versehen/Fehler ist, nicht, dass es sich um eine bewusste Designentscheidung handelt. – eggyal