• Alles static, oder was?

    von am 9. Oktober 2008

    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.

    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, ...

    Zum Profil von Timo Holzherr

    9 Kommentare »


    • Nils Langner
      am 9. Oktober 2008 um 09:30 Uhr

      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.


    • Lukas
      am 9. Oktober 2008 um 11:42 Uhr

      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..


    • Timo
      am 9. Oktober 2008 um 11:53 Uhr

      Typehints für native Datentypen wären wunderschön! Genauso wie den Typ von Instanzattributen festzulegen… *träum*

      Aber PHP hat ja nicht mal die bestehenden Type Hints für Objekte “richtig” implementiert (s. http://www.phphatesme.com/archives/505).

      Was soll man da erwarten?


    • Nils
      am 9. Oktober 2008 um 12:53 Uhr

      Aber wenigstens sollen ja bald Typehints für Rückgabewerte möglich sein. Ist doch auch schon mal was.


    • Johannes
      am 9. Oktober 2008 um 16:02 Uhr

      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 :-)


    • admin
      am 9. Oktober 2008 um 17:09 Uhr

      Das mit den Return Type Hints hatte ich aus der Todo Liste von PHP 6.0 “add support for type-hinted return values” (http://wiki.php.net/todo/php60). Zumindest war das meine Interpretation.


    • Timo
      am 9. Oktober 2008 um 18:28 Uhr

      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…


    • Oswald
      am 10. Oktober 2008 um 05:00 Uhr

      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.


    • Statische Methoden imitieren | PHP hates me - Der PHP Blog
      am 14. Oktober 2008 um 08:05 Uhr

      [...] ohne dies gelöst. Was ja auch legitim ist für diese Zeit. Jetzt mit PHP5 und einer besseren aber nicht perfekten Unterstützung des static Schlüsselwortes möchte man aber vielleicht trotzdem der Klasse eine statische Methode [...]

    RSS Feed für Kommentare zu diesem Artikel. TrackBack URL

    Hinterlasse einen Kommentar

    Werbung
    PHP Magazin
    Ausgabe 02/2010

    Dieses Mal mit Artikeln zu den Themen OpenSocial und Apache Shindig, Graphentheorie, Smarty3

    t3n
    Ausgabe 19

    Social Media (R)evolution. Weitere Themen sind noSQL, Crowdsourcing ...

    PHP Journal
    Ausgabe 2/2010

    PHP & Windows optimal nutzen, die besten PHP-CMS im Überblick, Google-API mit Zend Framework nutzen.

    Wir wurden schon öfters gefragt, ob man uns nicht irgendwie unterstützen kann. Die Antwort war immer einfach: Klar! Am einfachsten ist es eure nächsten Einkäufe bei Amazon über unsere Link abzuwickeln. Damit würdet ihr uns schon sehr helfen. Über Co-Autoren freuen wir uns aber noch mehr.