Warum verbietet PHP 5.2+ abstrakte statische Klassenmethoden?

121

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

Strenge Standards : Statische Funktion Programm :: getSelectSQL () sollte nicht abstrakt sein in Program.class.inc

Die betreffende Funktion gehört zu einem abstrakten übergeordneten Klassenprogramm und wird als abstrakt statisch deklariert, da sie in ihren untergeordneten Klassen wie TVProgram implementiert werden sollte.

Hinweise zu dieser Änderung habe ich hier gefunden :

Abgelegte abstrakte statische Klassenfunktionen wurden gelöscht. Aufgrund eines Versehens erlaubten PHP 5.0.x und 5.1.x abstrakte statische Funktionen in Klassen. Ab PHP 5.2.x können nur Schnittstellen über diese verfügen.

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

Artem Russakovskii
quelle
12
Neue Leser sollten beachten, dass diese irrationale Einschränkung in PHP 7 entfernt wurde.
Mark Amery

Antworten:

76

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

Gleiches gilt für die Erweiterung einer Klasse mit statischen Methoden. Wenn Sie diese Klasse erweitern und eine statische Methode mit derselben Signatur erstellen, überschreiben Sie die statische Methode der Oberklasse nicht

EDIT (16. September 2009)
Update dazu. Unter PHP 5.3 sehe ich, dass abstrakte statische Daten zurück sind, egal ob gut oder schlecht. ( Weitere Informationen finden Sie unter http://php.net/lsb. )

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

Jonathan Fingland
quelle
3
OK, was ist, wenn ich die Notwendigkeit der Funktion getSelectSQL () in allen untergeordneten Elementen erzwingen möchte, die meine abstrakte Klasse erweitern? getSelectSQL () in der übergeordneten Klasse hat keinen gültigen Grund zu existieren. Was ist der beste Aktionsplan? Der Grund, warum ich mich für abstrakte statische Daten entschieden habe, ist, dass der Code erst kompiliert wird, wenn ich getSelectSQL () in allen untergeordneten Elementen implementiert habe.
Artem Russakovskii
1
Höchstwahrscheinlich sollten Sie die Dinge neu gestalten, damit getSelectSQL () eine Zusammenfassung / Instanz / Methode ist. Auf diese Weise haben / Instanzen / jedes Kindes eine solche Methode.
Matthew Flaschen
7
Abstrakte statische Aufladung ist in PHP 5.3 weiterhin nicht zulässig. Späte statische Bindungen haben nichts damit zu tun. Siehe auch stackoverflow.com/questions/2859633
Artefacto
40
Meiner Meinung nach ist diese strenge Warnung einfach nur dumm, da PHP eine "späte statische Bindung" hat, die natürlich die Idee vermittelt, statische Methoden so zu verwenden, als ob die Klasse selbst ein Objekt gewesen wäre (wie zum Beispiel in Ruby). abstract staticDies führt zu einer Überlastung der statischen Methode und kann in diesem Fall hilfreich sein.
dmitry
4
Diese Antwort ist vage falsch. "immer noch nicht erlaubt" bedeutet nur, dass Sie eine Warnung auf E_STRICT-Ebene erhalten. Zumindest in Version 5.3+ können Sie gerne abstrakte statische Funktionen erstellen, diese in erweiterten Klassen implementieren und dann über das Schlüsselwort static :: auf sie verweisen. Offensichtlich ist die statische Version der übergeordneten Klasse noch vorhanden und kann nicht direkt (über self :: oder static :: innerhalb dieser Klasse) aufgerufen werden, da sie abstrakt ist und schwerwiegende Fehler verursacht, als ob Sie eine reguläre nicht statische abstrakte Funktion aufgerufen hätten. Funktionell ist dies nützlich, ich stimme den diesbezüglichen Ansichten von @dmitry zu.
Ahoffner
79

Es ist eine lange, traurige Geschichte.

Als PHP 5.2 diese Warnung zum ersten Mal 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 wie erwartet funktioniert:

<?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 Warnung vor dem strengen Modus funktioniert der obige Code nicht. Der self::bar()Aufruf in foo()bezieht sich explizit auf die bar()Methode von ParentClass, auch wenn foo()er als Methode von aufgerufen wird ChildClass. Wenn Sie versuchen, diesen Code mit deaktiviertem Strict-Modus auszuführen, wird " Schwerwiegender PHP-Fehler: 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 besteht 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 Klassen bereitstellen können. Da PHP 5.2 keine saubere Möglichkeit bietet, eine Methode einer übergeordneten Klasse zu schreiben, die eine statische Methode der untergeordneten Klasse aufruft, für die sie aufgerufen wird, ist diese Verwendung abstrakter statischer Methoden nicht möglich. Daher ist jede Verwendung abstract staticin PHP 5.2 ein schlechter Code, wahrscheinlich inspiriert durch ein Missverständnis der Funktionsweise des selfSchlüsselworts. Es war völlig vernünftig, eine Warnung darüber zu werfen.

Dann wurde PHP 5.3 hinzugefügt, um auf die Klasse zu verweisen, für die eine Methode über das staticSchlüsselwort aufgerufen wurde (im Gegensatz zum selfSchlüsselwort, das sich immer auf die Klasse bezieht, in der die Methode definiert wurde ). Wenn Sie ändern , self::bar()um static::bar()in meinem Beispiel oben, es funktioniert gut in PHP 5.3 und höher. Sie können mehr über selfvs staticbei New self vs. new static lesen .

Mit dem hinzugefügten statischen Schlüsselwort war das eindeutige Argument für das abstract staticAuslösen einer Warnung verschwunden. Der Hauptzweck von späten statischen Bindungen bestand darin, in einer übergeordneten Klasse definierten Methoden das Aufrufen statischer Methoden zu ermöglichen, die in untergeordneten Klassen definiert werden würden. Das Zulassen abstrakter statischer Methoden erscheint angesichts der späten statischen Bindungen vernünftig und konsistent.

Sie könnten immer noch dafür eintreten, die Warnung beizubehalten. Zum Beispiel könnte man argumentieren , dass seit PHP können Sie statische Methoden der abstrakten Klassen nennen, in meinem Beispiel oben (auch nachdem sie durch den Austausch Fixierung selfmit static) Sie aussetzt eine öffentliche Methode , ParentClass::foo()die ist gebrochen und dass Sie nicht wirklich wollen entlarven. Die Verwendung einer nicht statischen Klasse - das heißt, alle Methoden zu Instanzmethoden zu machen und die untergeordneten Methoden ParentClasszu Singletons oder Ähnlichem zu machen - würde dieses Problem lösen, da ParentClasssie abstrakt sind und daher nicht instanziiert werden können heißen. Ich denke, dieses Argument ist schwach (weil ich denke, es zu entlarvenParentClass::foo() ist keine große Sache und die Verwendung von Singletons anstelle von statischen Klassen ist oft unnötig ausführlich und hässlich), aber Sie könnten einigermaßen anderer Meinung sein - es ist ein etwas subjektiver Aufruf.

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

Äh, nicht genau .

Der oben verlinkte PHP-Fehlerbericht 53081 forderte das Löschen der Warnung, da das Hinzufügen des static::foo()Konstrukts abstrakte statische Methoden sinnvoll und nützlich gemacht hatte. Rasmus Lerdorf (Schöpfer von PHP) kennzeichnet die Anfrage zunächst als falsch und durchläuft eine lange Kette von schlechten Argumenten, um zu versuchen, die Warnung zu rechtfertigen. Dann findet endlich dieser Austausch 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

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. Sie können self :: B () natürlich nicht aufrufen, 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 strenge Moduswarnung ausgegeben. Ich denke, er hat ohne eingeschalteten strengen Modus getestet. Unabhängig davon ließ ein verwirrter Rasmus die Anfrage fälschlicherweise als "Schwindel" geschlossen.

Und deshalb ist die Warnung immer noch in der Sprache. Dies ist möglicherweise keine ganz zufriedenstellende Erklärung - Sie sind wahrscheinlich hierher gekommen, in der Hoffnung, dass die Warnung rational begründet ist. Leider werden in der realen Welt Entscheidungen manchmal eher aus weltlichen Fehlern und schlechten Argumenten als aus rationalen Entscheidungen getroffen. Dies ist einfach eine dieser Zeiten.

Glücklicherweise hat der geschätzte Nikita Popov die Warnung aus der Sprache in PHP 7 als Teil von PHP RFC entfernt: E_STRICT-Benachrichtigungen neu klassifizieren . Letztendlich hat sich die Vernunft durchgesetzt, und sobald PHP 7 veröffentlicht ist, können wir alle glücklich nutzen, abstract staticohne diese dumme Warnung zu erhalten.

Mark Amery
quelle
70

Es gibt eine sehr einfache Lösung für dieses Problem, die aus gestalterischer Sicht tatsächlich sinnvoll ist. Wie Jonathan schrieb:

Gleiches gilt für die Erweiterung einer Klasse mit statischen Methoden. Wenn Sie diese Klasse erweitern und eine statische Methode mit derselben Signatur erstellen, überschreiben Sie die statische Methode der Oberklasse nicht

Als Workaround könnten Sie also Folgendes 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 Klasse, die MyFoo unterordnet, eine statische Methode getInstance und eine öffentliche Methode getSomeData implementiert. Wenn Sie MyFoo nicht unterordnen, können Sie iMyFoo dennoch implementieren, um eine Klasse mit ähnlichen Funktionen zu erstellen.

Wouter Van Vliet
quelle
2
Ist es mit diesem Muster möglich, die Funktion zu schützen ? Wenn ich das mache, bedeutet dies, dass die Klassen, die MyFoo erweitern, Warnungen auslösen, dass getInstance öffentlich sein muss. Und Sie können nicht geschützt in eine Schnittstellendefinition setzen.
Artfulrobot
3
Einige Male static::können nützlich sein.
Seyed
3
Funktioniert nicht mit Eigenschaften. Wenn nur Traits abstract staticMethoden haben könnten , ohne PHP zu meckern ...
Rudie
1
Die Verwendung einer Schnittstelle ist hier wahrscheinlich die beste Lösung. +1.
Juan Carlos Coto
Dies ist eigentlich sehr elegant wegen seiner Einfachheit. +1
G. Stewart
12

Ich weiß, das ist alt, aber ...

Warum nicht einfach eine Ausnahme für die statische Methode dieser übergeordneten Klasse auslösen? Wenn Sie sie nicht überschreiben, wird die Ausnahme verursacht.

Petah
quelle
1
Das hilft nicht, die Ausnahme würde beim Aufruf der statischen Methode auftreten - gleichzeitig würde ein Fehler "Methode existiert nicht" auftreten, wenn Sie ihn nicht überschreiben.
BT
3
@BT Ich meinte, deklarieren Sie die Methode nicht abstrakt, implementieren Sie sie, sondern lösen Sie nur eine Ausnahme aus, wenn sie aufgerufen wird. Dies bedeutet, dass sie nicht ausgelöst wird, wenn sie überschrieben wurde.
Petah
Dies scheint die eleganteste Lösung zu sein.
Alex S
Es ist besser, so etwas zur Kompilierungszeit zu sehen, als zur Laufzeit. Es ist einfacher, Probleme zu finden, bevor sie in Produktion sind, da Sie nur die Datei laden und keinen Code ausführen müssen, um herauszufinden, ob sie fehlerhaft oder fehlerhaft ist
Rahly,
4

Ich würde argumentieren, dass eine abstrakte Klasse / Schnittstelle als Vertrag zwischen Programmierern angesehen werden könnte. Es geht mehr darum, wie Dinge aussehen / sich verhalten sollen und nicht um tatsächliche Funktionen. Wie in PHP5.0 und 5.1.x zu sehen ist, ist es kein Naturgesetz, das die PHP-Entwickler daran hindert, sondern der Drang, anderen OO-Entwurfsmustern in anderen Sprachen zu folgen. Grundsätzlich versuchen diese Ideen, unerwartetes Verhalten zu verhindern, wenn man bereits mit anderen Sprachen vertraut ist.

Merkuro
quelle
Obwohl hier nicht mit PHP verwandt, gibt es eine weitere gute Erklärung: stackoverflow.com/questions/3284/…
merkuro
3
Mein Gott! Die beiden Leute, die dich herabgestimmt haben, sind völlig verrückt! Dies ist die aufschlussreichste Antwort auf diesen Thread.
Theodore R. Smith
5
@ TheodoreR.Smith Aufschlussreich? Es enthält Fehler und ist kaum kohärent. Die Behauptung, dass abstrakte Klassen "keine tatsächliche Funktionalität implementieren", ist nicht unbedingt wahr, und das ist der ganze Punkt, an dem sie zusätzlich zu Schnittstellen existieren. In der Behauptung, dass "es kein Naturgesetz ist, das die PHP-Entwickler daran hindert" , habe ich keine Ahnung, was "es" ist. Und in der Behauptung, dass "diese Ideen im Grunde versuchen, unerwartetes Verhalten zu verhindern" , habe ich keine Ahnung, was entweder "diese Ideen" oder das potenzielle "unerwartete Verhalten" sind. Was auch immer Sie daraus gewonnen haben, es geht mir verloren.
Mark Amery
2

Ich sehe keinen Grund, statische abstrakte Funktionen 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 die in PHP 5.2 existierten und sie in Java existieren. Also können wir es tun. SOLLTEN wir es tun? - Machen sie Sinn? Ja. Es ist sinnvoll, einen Teil einer Klasse zu implementieren und einen anderen Teil einer Klasse dem Benutzer zu überlassen. Bei nicht statischen Funktionen ist dies sinnvoll. Warum sollte es bei statischen Funktionen keinen Sinn ergeben? Eine Verwendung statischer Funktionen sind Klassen, in denen nicht mehr als eine Instanz (Singletons) vorhanden sein darf. Zum Beispiel eine Verschlüsselungsmaschine. Es muss nicht in mehreren Fällen vorhanden sein, und es gibt Gründe, dies zu verhindern. Beispielsweise müssen Sie nur einen Teil des Speichers vor Eindringlingen schützen. Daher ist es absolut sinnvoll, einen Teil der Engine zu implementieren und den Verschlüsselungsalgorithmus dem Benutzer zu überlassen. Dies ist nur ein Beispiel. Wenn Sie es gewohnt sind, statische Funktionen zu verwenden, finden Sie noch viel mehr.

user2964021
quelle
3
Ihr Standpunkt zu abstrakten statischen Methoden in 5.2 ist stark irreführend. Zunächst wurde in 5.2 die strenge Moduswarnung eingeführt, die sie verbietet. Sie lassen es so klingen, als ob sie in 5.2 erlaubt waren. Zweitens konnten sie in 5.2 nicht einfach verwendet werden, um "einen Teil einer Klasse zu implementieren und einen anderen Teil einer Klasse dem Benutzer zu überlassen", da noch keine späten statischen Bindungen vorhanden waren.
Mark Amery
0

In PHP 5.4+ verwenden Sie das Merkmal:

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

und in Ihrer Klasse am Anfang setzen:

use StaticExample;
Kamil
quelle
1
Ich konnte abstract public static function get_table_name();ein Merkmal einfügen und dieses Merkmal in meiner abstrakten Klasse ohne weitere E_STRICT-Warnungen verwenden! Dies erzwang immer noch die Definition der statischen Methode bei den Kindern, wie ich gehofft hatte. Fantastisch!
Programster
-1

Informieren Sie sich über die Probleme mit der späten statischen Bindung von PHP. Wenn Sie statische Methoden für abstrakte Klassen verwenden, werden Sie wahrscheinlich eher früher als später darauf stoßen. Es ist sinnvoll, dass die strengen Warnungen Sie auffordern, die Verwendung fehlerhafter Sprachfunktionen zu vermeiden.

Sean McSomething
quelle
4
Ich denke, er meint die "Strict Standards" -Warnungen.
Jacob Hume
2
Welche "Probleme" behaupten Sie, dass späte statische Bindungen haben? Sie behaupten, dass sie "kaputt" sind, was eine kühne Behauptung ist, die hier ohne Beweise oder Erklärung aufgestellt wurde. Das Feature hat für mich immer gut funktioniert, und ich denke, dieser Beitrag ist Unsinn.
Mark Amery