Facebook
Twitter
Google+
Kommentare
9
Willkommen bei "the web hates me". Mittlerweile hat unser Team ein tolles neues Monitoringtool für Agenturen gelauncht. Unter dem Namen koality.io haben wir einen Service geschaffen, der er Agenturen ermöglicht mit sehr geringen Kosten alle Ihre Webseiten zu überwachen.

Alles static, oder was?

Sicher – so manchen PHP-Entwickler stört die eine oder andere Ungereimtheit dieser Sprache. Eine, die mich immer wieder an PHP zweifeln lässt ist die Handhabung der statischen Methodenaufrufe.

Ganz nebenbei gesagt sollte, wie ich finde, jeder PHP-Entwickler vor dieser Eigenschaft gewarnt sein.

PHP erlaubt es, Instanzmethoden – also eine nicht-statische Methode einer Klasse – statisch aufzurufen. Die Phänomene, die dann beim Zugriff auf $this erscheinen, sind abenteuerlich:

Als Grundlage soll folgende Klasse betrachtet werden:

class Foo
{
    public function ReturnClassname()
    {
        return $this ? get_class( $this ) : '**NULL**';
    }
}

Die Methode ReturnClassname gibt mithilfe von get_class den Typ der Instanz zurück, auf die $this referenziert. Für den Fall, dass $this NULL ist, wird der String „**NULL**“ zurückgegeben.

$foo2 = new Foo();
echo "Instance Call will return Classname " . $foo2->ReturnClassname() . "\n";

Korrekterweise erhalten wir bei diesem Codebeispiel den Klassennamen „Foo“:

> Instance Call will return Classname Foo

Was aber, wenn jemand unsere Klasse Foo falsch verwendet und einen static call durchführt?

echo "Static Call will return Classname " . Foo::ReturnClassname() . "\n";

Wie es quasi von PHP zu erwarten ist, wird dies vom Interpreter akzeptiert. Die Variable $this hat den Wert NULL. Der Entwickler bekommt jedoch eine Notice, auf die er während der Entwurfszeit reagieren sollte:

> Notice: Undefined variable: this in /home/timo/…/static.php on line …
> Static Call will return Classname **NULL**

Soweit so gut – bis hier könnte man dies fast noch akzeptieren. Trotzdem frage ich mich, wie ich mich dann als API-Entwickler davor schützen kann, dass die Anwender der Interfaces keine falschen Calls mache. Es liegt auf der Hand, dass man nicht vor jedem Zugriff auf $this prüfen möchte, ob der Call richtig gemacht wurde. Man denke nur an Beans, die dutzende von Settern und Getter haben, die alle schlichtweg nur auf ein Attribut zugreifen möchten. An jeder Stelle eine Sicherheitsprüfung ob $this NULL ist?

Was dem PHPHatesMe-Leser tatsächlich Freude bereiten wird ist, was passiert, wenn man die Klasse Foo in einem Klassenkontext statisch aufruft:

class Bar
{
    public function Fool()
    {
        // Call als Instanzmethode
        $foo2 = new Foo();
        echo "Instance Call will return Classname " . $foo2->ReturnClassname() . "\n";
        // Call als statische Methode
        echo "Static Call will return Classname " . Foo::ReturnClassname() . "\n";
    }

}

Wie gewohnt liefert der Call als Instanzmethode den Klassennamen Foo. Der statische Aufruf der Methode ReturnClassname hingegen, resultiert nicht in einem Warning o.ä. sondern liefert den Klassennamen Bar:

> Static Call will return Classname Bar

PHP scheint also bei statischen Methodenaufrufen die Referenz von $this nicht zu verschieben, sondern es beim ursprünglichen Wert zu belassen. Arbeitet man als API-Entwickler mit der Variable $this kann dies ernsthafte Konsequenzen haben. Eventuell ist eine Methode, die man auf der Instanz von Foo ausführen möchte, auch in Bar definiert, führt aber komplett andere Aktionen aus.

Ich denke, es liegt auf der Hand, dass dies gefährlich werden könnte.

Um PHP hier etwas aus der Patsche zu helfen könnte man sagen: „In wichtigen Fällen kann man doch prüfen, ob $this wirklich die richtige Instanz ist!“

Denkt man jedoch an Polymorphismus, fällt auf, dass selbst get_class nicht mehr weiterhelfen kann:

class Foo2 extends Foo
{
}

Führt man auf die Klasse Foo2 die Methode GetClassname aus, erhalten wir:

> Instance Call will return Classname Foo2

Ich kann aber alle Leser beruhigen: Instanceof bringt hier Hilfe. Damit könnte Foo herausfinden, ob die Klasse im richtigen Kontext aufgerufen wurde:

$correctScope = $this instanceof Foo;

Trotzdem würde ich mir wünschen, dass wie bei einer „richtigen“ Programmiersprache entweder eine RuntimeException oder ein Fatal-Error produziert wird.

That’s why PHP hates me.

Über den Autor

Timo Holzherr

Software-Entwicklung ist für mich mehr als ein Beruf, mit dem ich mir die Brötchen verdiene - es ist meine Leidenschaft. Themen wie professionelle, objektorientierte Software-Entwicklung, moderne Web-Entwicklung, Agiles Projektmanagement sind für mich so spannend wie ein guter Krimi. Deshalb habe ich 2003-2006 Informationstechnik studiert und nach dem Studienabschluss meinen Job als Web-Entwickler gewählt. Seit 2006 halte ich Vorlesungen im Fach "Software-Engineering" an der DHBW (Dualen Hochschule Baden-Württemberg) in Stuttgart und bin im Prüfungsausschuss der DHBW in Horb. Obwohl ich so viel Zeit beruflich mit der Entwicklung von Software verbringe, bin ich privat trotzdem unermüdlich und habe deshalb mein zweites Hobby, die Musik, mit dem ersten Verbunden: Im Juli 2009 habe ich mein erstes großes privates Web-Projekt gestartet - GUESSASONG. (http://www.guessasong.net/) Ich bin gespannt, was ihr über meine Beiträge denkt und werde versuchen trotz vollem Terminplan regelmäßig hier Posts zu machen.
Kommentare

9 Comments

  1. Naja jetzt muss man drauf achten, dass eine Methode nicht statisch aufgerufen wird und auch nicht mit Verweisen hantiert wird (http://www.phphatesme.com/archives/532). Ich glaube ja so langsam müssten PHP Methoden schon alleine 10 Zeilen haben, nur um sicher zu stellen, dass man auch mit dem arbeitet, was man haben will.

    Reply
  2. Ja, das glaube ich auch langsam. Es nervt, dass man bei der Parameterübergabe keinen nativen Datentypen erzwingen kann. Aber das wird es bei PHP wohl auch nie geben..

    Reply
  3. Das Return-Type-Hints kommen halte ich für ein Gerücht.

    TypeHints für Skalare sind halt so eine Sache: PHP legt nirgendswo Wert auf den Typ sondenr konvertiert entsprechend, wenn ich nun aber nen TypeHint machen würde wäre das ein Fatal Error, damit muss ich da wirklcih aufpassen, meist is mir egal ob ich nen „nummerischen String“ oder einen Integer habe, d.h. bei der Verwendung müsste ich ständig casten um keinen Fatal error zu bekommen (die meisten DB Extensions leifern auch integer Werte als strings z.B., d.h. ich lese ne id aus einer Datenbank, und muss die dann erst casten bevor ich sie verwenden kann).

    Die Alternative da wäre das der TypeHint die Castet – das is aber wohl komoplett daneben, bliebe der Weg „strict“ und „lazy“ typehints zu definieren, das is aber auch nciht perfekt.

    … oh und man könnte das ganze noch durch einen „scalar“ Type-hint machen, der is dann aber auch wieder nicht aussagekräftig genug, nein, passt alles nicht wirklich. Ich habe bislang kein Proposal gesehen, dass von vorne bis hinten Sinn macht ohne das wir PHP in ne voll typisierte Sprache wandeln – aber wenn ich das will kann ich as ja machen und wechseln, dann bin ich gleich nen Stapel anderer PHP-Probleme mit los 🙂

    Reply
  4. Ich habe gerade auf der To-Do-Liste folgendes gefunden:

    – add internal flag only to force calling of the parent constructor

    Wenn das auch für den Programmierer benutzbar wäre (also nicht internal), das wäre ausgezeichnet. *hoff* Ich finde es ziemlich ungeschickt, dass man bei PHP keine Chance hat, zu erzwingen, dass der Parent-Constructor aufgerufen wird – es sei denn man macht ihn final und bietet noch soetwas wie eine Template-(Hook-)Methode für die Subklassen…

    Reply
  5. Es gibt keine Schutz vor dummen Entwicklern.

    Wenn du nicht willst, das deine Methoden als statische Methoden aufgerufen werden, dann dokumentiere das so. Verwende dazu phpDocumentor. Alle anderen Dokumentationsgeneratoren decken die Eigenheiten von PHP nur unzureichend ab.

    Wenn die dokumentieren Vorbedingungen nicht erfüllt sind, dann brauche ich die Nachbedingungen, die ich sonst garantieren würde, nicht zu erfüllen.

    Davon weiche ich nur bei sicherheitskritischen Funktionen ab (z.B. Kommunikation mit der Datenbank, dem Browser oder sonstigen externen Systemen).

    Type Hints eignen sich hervorragend für Autocompletion in der IDE, sowohl bei Parametern als auch beim Rückgabewert. Da der Compiler aber sowieso nicht feststellen kann ob eine bestimmte Variable einen bestimmten Typ hat, war’s das damit auch schon. Mir ist es eigentlich egal ob eine Funktion schon beim Aufruf oder erst während der Abarbeitung Voodoo macht, mögliche Fehler sollten vorher behoben werden.

    Reply

Leave a Comment.

Link erfolgreich vorgeschlagen.

Vielen Dank, dass du einen Link vorgeschlagen hast. Wir werden ihn sobald wie möglich prüfen. Schließen