• Refactor by Reference

    von am 14. September 2008

    Fast eine Stunde Fehlersuche hat mir am Freitag eine schöne Eigenart von PHP gekostet, mit der ich nicht gerechnet hatte und deswegen möchte ich sie mit euch teilen, denn ich denke, dass jeder in diese Lage kommen kann.

    Wie der Titel schon beschreibt wollte ich eine Funktion verändern, also Refactoring betreiben. Eigentlich ging es nur darum eine Funktion aus einer Klasse in eine andere zu verschieben. Um sicherzustellen, dass danach alles noch funktioniert, wie es sollte, habe ich die alte Funktion weiterhin aufrufbar gelassen, habe sie aber einfach auf die neue umgeleitet. Im Großen und Ganzen sah die Ursprungsklasse wie folgt aus:

    <?php
      class helperClass
      {
        public static function trimArray( $array )
        {
          // lösche alle leeren Elemente aus dem Array
          return $array;
        }
      }
    ?>

    Also kein Hexenwerk diese einfache Funktion zu verschieben. Gesagt getan habe ich dann eine neue Klasse implementiert und die alte Funktion umgeleitet, was so aussah:

    <?php
      class HelperClass
      {
        public static function trimArray( $array )
        {
          return ArrayHelperClass::trim( $array );
        }
      }
    
      class ArrayHelperClass
      {
        public static function trim( $array )
        {
          // lösche alle leeren Elemente aus dem Array
          return $array;
        }
      }
    ?>

    Ich glaube nicht, dass jemand dieses Refactoring anders angegangen hätte. Über den Sinn und Unsinn von Helperklassen können wir ein anderes mal diskutieren. Bitte schaut euch meinen Umbau genau an, wer von das Problem auf Anhieb sieht, der bekommt von mir einen kompletten Eintrag darüber, wie toll er ist. Ich muss aber noch dazu sagen, dass wir diese Funktion bereits mit Unittests abgedeckt hatten, uns aber dieser eine Spezialfall nicht eingefallen ist.

    Nachdem diese umgeschriebene Funktion auf unseren Produktiv-Server gewechselt ist ging erstmal gar nichts mehr. Irgendwann konnten wir dann das Problem auf diese Methode eingrenzen. Ich könnte noch lange um den heißen Brei herum reden, aber ich glaub ich fange jetzt erstmal mit dem Problem an, bevor ich alle zu tode langweile.

    Meine Kollegen und ich haben einfach den in PHP erlaubten call-by-reference Fall übersehen. Ruft man diese Funktion mit einem Verweis auf, z.B. mit

    HelperClass::trimArray( &$array );

    so funktioniert das ganze “natürlich” in der ersten, alten Variante. PHP denkt aber gar nicht dran, diesen Verweis auch als Verweis an die trim Methode der neuen Klassen weiterreichen. Dass heißt, das $array jetzt halt nicht mehr nach überschrieben wird und kein neuer Wert übergeben wird. Ok ich gebe ja zu, dass man dem Entwickler ,der dort eine Referenz rein gegeben hat, ein paar runterhauen sollte, aber über 4 Jahre alten Legacy Code sollte man sich nie zu sehr aufregen. Als wir den Fehler dann endlich gefunden hatten, dauerte es auch nicht mehr wirklich lange, bis wir eine Lösung hatten. Prinzipiell ist es ganz einfach, man muss nur eine “unnötige” Variable definieren.

    <?php
      class HelperClass
      {
        public static function trimArray( $array )
        {
          $array = ArrayHelperClass::trim( $array );
          return $array;
        }
      }
    >

    Und schon klappt das ganze auch per Referenz. Da ich aber gerne unnötige Variablen vermeide, werden wir wohl am Montag erstmal eine neue Regel definieren, dass man eine solche Funktion nur so verwenden sollte, wie sie vorgesehen ist und zwar nicht für call-by-reference.

    Nils Langner

    Auch wenn Ihr es mir nicht glauben werdet, aber ich habe nichts gegen PHP. Ich rege mich einfach nur gerne auf. Ok so schlimm ist es auch nicht. Eigentlich wollte ich schon immer einen Blog haben und da ...

    Zum Profil von Nils Langner

    2 Kommentare »


    • Interview mit Nils Langner - Teil 1 | PHP hates me
      am 18. September 2008 um 20:29 Uhr

      [...] jetzt den Höhepunkt der letzten Tage skizzieren sollte, dann wäre es ganz klar das Problem beim Refactoring. Oder war es vielleicht doch das Typehinting Verhalten? Um ehrlich zu sein, ich weiß es nicht [...]


    • Nils Langner
      am 1. Oktober 2008 um 21:47 Uhr

      Ich wusste doch, dass niemand auf den “Fehler” kommt.

    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.