Facebook
Twitter
Google+
Kommentare
14

Die magische Leere

So nachdem wir den Freitag ja noch fast friedlich zu ende gebracht haben, widmen wir uns heute mal wieder einem technischen Thema, dass ich jetzt gerade übrigens ins OpenOffice tippe. Toll, oder? Geht so! Wisst ihr was aber wirklich toll ist? Unser Workshop für die International PHP Conference wurde angenommen. Wer als etwas zum Thema „advanced Eclipse“ wissen will, der sollte vorbei schauen. Werde einen halben Tag zusammen mit Bastian Feder und vielleicht Sven Kiera über ein paar PDT Features reden, die ihr noch nicht kennt (hoffe ich einfach mal).

Die letzte Woche bin ich mal wieder über ein mir schon bekanntes Problem mit PHP und empty gestoßen. Mir ist aber aufgefallen, dass ich es noch nie dokumentiert habe und ich dies mal nachholen sollte, bevor ich wieder alles vergessen habe und das Wissen für immer in den Tiefen des Internets verschwindet. Außerdem ist es gut, wenn man es mal gelesen hat und sich dann wieder erinnert.

Wow, schon fast der ganze Artikel rum und ich habe noch kein Wort über das eigentliche Thema geschrieben. Ist auch neuer Rekord im „drumrumreden“. Wobei geht es also heute? PHP verhält sich bei der empty Methode ein wenig anders als man es erwartet, wenn man damit auf magische Methoden losgeht. Ein kleines Beispiel:

class MagicClass
{
	public $theArray = array( '1' );

	public function __get( $name )
	{
		return array( '1' );
	}
}
$magic = new MagicClass();
var_dump( empty( $magic->theArray ) );
var_dump( empty( $magic->theMagicArray ) );

Wir wissen alle, dass die magische __get Methode immer dann aufgerufen wird, wenn man auf ein Attribut lesend zugreift, welches nicht existiert. Wenn man sich das Beispiel anschaut, so müsste beide var_dump ein false zurückliefern. Tun sie aber leider nicht. Der erste Aufruf, mit dem existierenden Attribut liefert natürlich ein false. So weit so gut. Der zweite Aufruf aber leider nicht. Komischerweise wird die magische Methode gar nicht aufgerufen und somit kommt dort etwas leeres zurück und das ist natürlich empty.

Mit ein wenig rumgooglen könnte man jetzt wahrscheinlich rausfinden was genau kaputt ist, da ich aber gerade im Zug von Freiburg nach Hamburg sitze und keinen Internetzugang habe, muss ich das euch oder einem der kommenden Artikel überlassen.

Lösung: Danke erstmal an KingCrunch und Sebastian. Die Lösung des Problems ist einfach. Die empty Funktion erwartet, dass die magische Methode __isset( ) auch mit Leben gefüllt ist, dann klappt das ganze nämlich ohne Probleme.

Über den Autor

Nils Langner

Nils Langner ist der Gründer von "the web hates me" und auch der Hauptautor. Im wahren Leben leitet er das Qualitätsmanagementteam im Gruner+Jahr-Digitalbereich und ist somit für Seiten wie stern.de, eltern.de und gala.de aus Qualitätssicht verantwortlich. Nils schreibt seit den Anfängen von phphatesme, welches er ebenfalls gegründet hat, nicht nur für diverse Blogs, sondern auch für Fachmagazine, wie das PHP Magazin, die t3n, die c't oder die iX. Nebenbei ist er noch ein gern gesehener Sprecher auf Konferenzen. Herr Langner schreibt die Texte über sich gerne in der dritten Form.
Kommentare

14 Comments

  1. Also erstmal wird ‚__call()‘ bei nicht-existenten Methoden aufgerufen, aber hast ja im Source auch korrekt ‚__get()‘ stehen. Und zweitens ist es bei mir nicht reproduzierbar …

    $ php -a
    Interactive mode enabled

    theArray ) );
    var_dump( empty( $magic->theMagicArray ) );
    bool(false)
    bool(true)

    Man beachte das zweite ‚true‘

    Reply
  2. Nein, es ist noch zu früh …. Natürlich is das ‚true‘ hier falsch. Allerdings scheint es ihn einfach zu verwirren, dass die anderen magischen Methoden fehlen

    public function __isset($name) {
    return true;

    Zugegeben: Damit macht es sich PHP etwas einfach, aber das ist zumindest die Lösung

    Reply
  3. Vielleicht sollte es in PHP ein MagicAccessorInterface geben, welches die Implementation von __isset(), __set() und __get() erzwingt, um solcherleit Nebeneffekte zu vermeiden.
    Ähnlich ging es mir mit StreamWrappern auch dort vergisst man viel, da kein Interface vorhanden ist, welches man befolgen muss.

    Reply
  4. @Christian: Ich glaube, dass das relativ schwer mit einem Interface abzudecken ist. Vielleicht sollte PHP da einfach ein wenig „schlauer“ intern sein. Auch bei den Streams ist es schwer es mit genau einem zu lösen, denn viele Dinge sind nun mal optional in der Implementierung. Aber vll. ist das auch der Grund, warum Streams kaum behandelt werden.

    Reply
  5. Das Problem mit nem Interface da is, dass das Konzept der magischen Methoden älter sit als Interfaces in PHP. D.h. das Interfaces einführen wird recht willkürlich.

    Bei den Streams, wie Nils sagt, hast Du recht viel optionales, d.h. Du musst am Ende nen Stapel Interfaces kombinieren, das größere Problemda is die Abwärtskomptibilität: ImAllgemeinen versuchen wir nichts zu brechen. Erzwingen wir es isses ein nerviger Bruch, mahcenwir es optional is kaum was gewonnen …. Tja,würde man PHP jetzt neu Designen wäre viel anders, so sieht man halt die Entwicklung …

    Reply
  6. Was spricht gegen ein var_dump((sizeof($magic->theArray) == 0)), also die Verwendung von sizeof()? (Abgesehen von der kürzeren Schreibweise)

    Reply
  7. @Stephan: Da spricht im Grunde nichts dagegen. Das Problem ist nur, dass du es machen musst. Ich würde intuitiv eher auf empty zurückgreifen und wenn ich das Problem kenne, dann komme ich auch so drum herum.

    Reply
  8. @Johannes Da geb ich dir prinzipiell recht. Ich würde allerdings trotzdem Interfaces als Schnittstellenbeschreibung begrüssen. Gerne optional.
    Vielleicht ist es bei den Magischen Sachen etwas übertrieben, aber bei StreamWrappern sehr sinnvoll. Ich hab mich da schon so oft durchgehangelt, weil ich immer irgendeine Methode vergessen habe oder nicht korrekt implementiert habe auf die irgendeine der File Funktionen zugreift. Ein Krampf.
    Ein Interface mit vernünftigen ausführlichen DocComments wäre toll. (Mittlerweile hab ich mir natürlich selber sowas geschrieben. )

    Reply
  9. Christin, irgendwo habe ichda was als Patch halb-fertig, ich glaube ich hatte mich damals für eine Abstrakte Klasse entschieden um die optionalen Dinge besser Abbilden zu können. Das hat aber zur Folge, dass Streams dann wirklich neu geschrieben werden müssen, d.h. das lohntnur wenn wir da größere Ändeurngen, neue Fetures oder so, mit Verinden, da wüsste ich nciht was.

    So Interfaces ohne Bedeutung, d.h. auf die nicht explizit geprüft wird oder so dazuzulegen bringt recht wenig. Ok, man könnte sagen „Wenn das nicht implementiert ist gibt es ne E_DEPRECATED“ und ab 6 muss man das Interface implementieren … könnte man überlegen. „feel free to send a patch“ wäre da das Motto 🙂

    Reply
  10. @Johannes
    Och ich seh das eher als freiwillige Sache. Ich hätte halt gerne ein Stream Interface auf php.net zum runterladen, das _kann_ ich dann implementieren und wenn ich das tue, kann ich mich drauf verlassen, alle Zugriffsarten zu bedienen.
    Wie gesagt, das hab ich mittlerweile selbst für mich geschrieben. Klappt prima. Wäre halt nur schoen gewesen, wenn diese Info so bereits auf php.net verfügbar gewesen wäre.. 🙂

    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