Warum verbietet PHP 5.2+ abstrakte statische classnmethoden?

Nachdem ich strenge Warnungen in PHP 5.2 aktiviert hatte, sah ich eine Menge strenger Standardwarnungen von einem Projekt, das ursprünglich ohne strenge Warnungen geschrieben wurde:

Strikte Standards : Die statische function Program :: getSelectSQL () sollte in Program.class.inc nicht abstrakt sein

Die fragliche function gehört zu einem abstrakten Elternklasse-Programm und wird als abstrakt-statisch deklariert, da sie in ihren untergeordneten classn wie TVProgram implementiert werden sollte.

Ich fand Hinweise auf diese Änderung hier :

Die abstrakten statischen classnfunktionen wurden gelöscht. Aufgrund einer Übersicht erlaubten PHP 5.0.x und 5.1.x abstrakte statische functionen in classn. Ab PHP 5.2.x können nur Interfaces diese haben.

Meine Frage ist: Kann jemand klar erklären, warum es in PHP keine abstrakte statische function geben sollte?

    statische Methoden gehören zu der class, die sie deklariert hat. Wenn Sie die class erweitern, können Sie eine statische Methode mit demselben Namen erstellen, aber Sie implementieren tatsächlich keine statische abstrakte Methode.

    Dasselbe gilt für das Erweitern jeder class mit statischen Methoden. Wenn Sie diese class erweitern und eine statische Methode derselben Signatur erstellen, überschreiben Sie die statische Methode der Oberklasse nicht

    BEARBEITEN (16. September 2009)
    Update zu diesem Thema. Wenn ich PHP 5.3 benutze, sehe ich, dass die abstrakte Statistik zurück ist, für gut oder schlecht. (siehe http://php.net/lsb für weitere Informationen)

    KORREKTUR (von philfreo)
    abstract static ist in PHP 5.3 immer noch nicht erlaubt, LSB ist verwandt, aber anders.

    Es gibt eine sehr einfache Arbeit für dieses Problem, die unter Designgesichtspunkten sinnvoll ist. Wie Jonathan schrieb:

    Dasselbe gilt für das Erweitern jeder class mit statischen Methoden. Wenn Sie diese class erweitern und eine statische Methode derselben Signatur erstellen, überschreiben Sie die statische Methode der Oberklasse nicht

    Also, als eine Arbeit könnte man das tun:

     < ?php abstract class MyFoo implements iMyFoo { public static final function factory($type, $someData) { // don't forget checking and do whatever else you would // like to do inside a factory method $class = get_called_class()."_".$type; $inst = $class::getInstance($someData); return $inst; } } interface iMyFoo { static function factory($type, $someData); static function getInstance(); function getSomeData(); } ?> 

    Und jetzt erzwingen Sie, dass jede classnunterklasse von MyFoo eine statische Methode GetInstance und eine öffentliche getSomeData-Methode implementiert. Und wenn Sie MyFoo nicht ableiten, können Sie iMyFoo weiterhin implementieren, um eine class mit ähnlicher functionalität zu erstellen.

    Es ist eine lange, traurige Geschichte.

    Als PHP 5.2 zum ersten Mal diese Warnung einführte, waren späte statische Bindungen noch nicht in der Sprache. Falls Sie mit späten statischen Bindungen nicht vertraut sind, beachten Sie, dass Code wie dieser nicht so funktioniert, wie Sie es erwarten würden:

     < ?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(); 

    Abgesehen von der strikten Moduswarnung funktioniert der obige Code nicht. Der Aufruf von self::bar() in foo() verweist explizit auf die Methode bar() von ParentClass , auch wenn foo() als Methode von ChildClass . Wenn Sie versuchen, diesen Code mit deaktiviertem strict-Modus auszuführen, wird " PHP Fatal error: Die abstrakte Methode ParentClass :: bar () kann nicht aufgerufen werden" angezeigt .

    Vor diesem Hintergrund waren abstrakte statische Methoden in PHP 5.2 nutzlos. Der Sinn einer abstrakten Methode liegt darin, dass Sie Code schreiben können, der die Methode aufruft, ohne zu wissen, welche Implementierung aufgerufen werden soll - und dann verschiedene Implementierungen für verschiedene untergeordnete classn bereitzustellen. Da PHP 5.2 jedoch keine saubere Methode zum Schreiben einer Methode einer Elternklasse bietet, die eine statische Methode der Kindklasse aufruft, für die sie aufgerufen wurde, ist diese Verwendung von statischen Methoden nicht möglich. Daher ist jede Verwendung von abstract static in PHP 5.2 ein schlechter Code, der wahrscheinlich von einem Missverständnis der functionsweise des self Schlüsselwortes inspiriert ist. Es war durchaus vernünftig, darüber eine Warnung zu geben.

    Aber dann kam PHP 5.3 in der Fähigkeit hinzu, auf die class zu verweisen, auf der eine Methode über das static Schlüsselwort aufgerufen wurde (im Gegensatz zum self Schlüsselwort, das sich immer auf die class bezieht, in der die Methode definiert wurde ). Wenn Sie in meinem obigen Beispiel self::bar() in static::bar() ändern, funktioniert es in PHP 5.3 und höher gut. Sie können mehr über das self gegen das static am Neuen Selbst gegen das Neue statisch lesen.

    Mit dem hinzugefügten static-Schlüsselwort ist das klare Argument für die abstract static Warnung weggefallen. Der Hauptzweck der späten statischen Bindungen bestand darin, Methoden zu erlauben, die in einer Elternklasse definiert sind, statische Methoden aufzurufen, die in untergeordneten classn definiert werden würden; Das Ermöglichen abstrakter statischer Methoden erscheint vernünftig und konsistent angesichts der Existenz späte statischer Bindungen.

    Du könntest immer noch, denke ich, dafür plädieren, die Warnung zu behalten. Zum Beispiel könnten Sie argumentieren, dass, da PHP Ihnen erlaubt, statische Methoden von abstrakten classn aufzurufen, in meinem obigen Beispiel (selbst nachdem Sie es durch static ) Sie eine öffentliche Methode ParentClass::foo() die kaputt ist und das du nicht wirklich aussetzen willst. Die Verwendung einer nicht statischen class - ParentClass alle Methodeninstanzmethoden zu machen und die ParentClass von ParentClass alle zu Singletons oder etwas zu machen - würde dieses Problem lösen, da ParentClass , das abstrakt ist, nicht instanziiert werden kann und daher seine Instanzmethoden können Ich werde nicht gerufen. Ich denke, dass dieses Argument schwach ist (weil ich denke, dass ParentClass::foo() keine große Sache ist und die Verwendung von Singletons anstelle von statischen classn oft unnötig wortreich und hässlich ist), aber das könnte ein wenig subjektiv sein.

    Basierend auf diesem Argument behielten die PHP-Entwickler die Warnung in der Sprache, oder?

    Äh, nicht genau .

    Der PHP-Fehlerbericht 53081, oben verlinkt, verlangte, dass die Warnung gelöscht wurde, da das Hinzufügen des static::foo() Konstrukts abstrakte statische Methoden vernünftig und nützlich gemacht hatte. Rasmus Lerdorf (Schöpfer von PHP) beginnt mit der Kennzeichnung der Anfrage als falsch und durchläuft eine lange Kette von schlechten Überlegungen, um zu versuchen, die Warnung zu rechtfertigen. Dann findet dieser Austausch schließlich statt:

    Giorgio

    ich weiss aber:

     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

    Richtig, genau so sollte es funktionieren.

    Giorgio

    aber es ist nicht erlaubt 🙁

    Rasmus

    Was ist nicht erlaubt?

     abstract class cA { static function A(){static::B();} abstract static function B(); } class cB extends cA { static function B(){echo "ok";} } cB::A(); 

    Das funktioniert gut. Du kannst offensichtlich nicht self :: B () nennen, aber static :: B () ist in Ordnung.

    Die Behauptung von Rasmus, dass der Code in seinem Beispiel "gut funktioniert", ist falsch; Wie Sie wissen, wird eine strikte Moduswarnung ausgegeben. Ich nehme an, er testete ohne eingeschalteten Modus. Trotzdem ließ ein verwirrter Rasmus die Anfrage fälschlicherweise als "falsch" geschlossen.

    Und deshalb ist die Warnung immer noch in der Sprache. Das mag keine befriedigende Erklärung sein - Sie sind wahrscheinlich hierher gekommen, in der Hoffnung, dass die Warnung vernünftig begründet wurde. Leider werden in der realen Welt manchmal Entscheidungen eher aus banalen Fehlern und schlechten Schlussfolgerungen als aus rationalen Entscheidungen getroffen. Das ist einfach eine dieser Zeiten.

    Glücklicherweise hat der schätzbare Nikita Popov die Warnung aus der Sprache in PHP 7 als Teil von PHP RFC: Reclassify E_STRICT notices entfernt . Letztendlich hat sich die Vernunft durchgesetzt, und sobald PHP 7 veröffentlicht ist, können wir alle glücklich abstract static ohne diese alberne Warnung zu erhalten.

    Ich weiß, das ist alt, aber ….

    Warum casting Sie nicht einfach eine Ausnahme der statischen Methode dieser Elternklasse auf diese Weise, wenn Sie sie nicht außer Kraft setzen, wird die Ausnahme verursacht.

    Ich würde argumentieren, dass eine abstrakte class / Schnittstelle als ein Vertrag zwischen Programmierern gesehen werden könnte. Es befasst sich mehr damit, wie Dinge aussehen / sich verhalten sollten und nicht die tatsächliche functionalität implementieren. Wie in php5.0 und 5.1.x zu sehen ist, ist es kein Naturgesetz, das die php-Entwickler davon abhält, es zu tun, sondern den Drang, mit anderen OO-Entwurfsmustern in anderen Sprachen zu gehen. Grundsätzlich versuchen diese Ideen, unerwartetes Verhalten zu verhindern, wenn man mit anderen Sprachen bereits vertraut ist.

    Ich sehe keinen Grund, statische abstrakte functionen zu verbieten. Das beste Argument, dass es keinen Grund gibt, sie zu verbieten, ist, dass sie in Java erlaubt sind. Die Fragen sind: – Sind die technisch machbar? – Ja, da das in PHP 5.2 existiert und sie in Java existieren. Also, wenn es geht. Sollen wir es tun? – Machen sie Sinn? Ja. Es ist sinnvoll, einen Teil einer class zu implementieren und einen anderen Teil einer class dem Benutzer zu überlassen. Es macht Sinn in nicht statischen functionen, warum sollte es nicht für statische functionen sinnvoll sein? Eine Verwendung von statischen functionen sind classn, in denen es nicht mehr als eine Instanz (Singletons) geben darf. Zum Beispiel eine Verschlüsselungs-Engine. Es muss nicht in mehreren Instanzen existieren und es gibt Gründe, dies zu verhindern – zum Beispiel müssen Sie nur einen Teil des Speichers vor Eindringlingen schützen. Es macht also durchaus Sinn, einen Teil der Engine zu implementieren und den Verschlüsselungsalgorithmus dem Benutzer zu überlassen. Dies ist nur ein Beispiel. Wenn Sie daran gewöhnt sind, statische functionen zu verwenden, werden Sie viel mehr finden.

    In PHP 5.4+ verwenden Sie Eigenschaft:

     trait StaticExample { public static function instance () { return new self; } } 

    und in deiner class am Anfang:

     use StaticExample; 

    Sehen Sie sich die ‘Late Static Binding’-Probleme von PHP an. Wenn Sie statische Methoden auf abstrakte classn anwenden, werden Sie wahrscheinlich eher früher als später darauf stoßen. Es macht Sinn, dass die strikten Warnungen Ihnen sagen, dass Sie keine kaputten Sprachfunktionen verwenden sollten.