Facebook
Twitter
Google+
Kommentare
12
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.

Parameter verändern

Nachdem wir die letzten Tagen kaum programmierlastige Themen hatten, hier mal wieder was zum Nachdenken. Und zwar geht es um einfache Parameter. Jeder kennt sie, jeder verwendet sie in Funktionen und Methoden. Parameter werden seit der Version 5 von PHP als Referenz übergeben auch wenn man den & Operator nicht explizit angibt. Zumindest gilt dies für Objekte. Primititve Typen verhalten sich anders. Hier arbeitet man mit Kopien.

Soviel zu den Grundlagen. Worauf ich eigentlich hinaus will, ich der Umgang in einer Methode mit denen ihr übergebenen Parametern. Sollte man die übergebenen Parameter verändern? Ich gebe mal ein Beispiel. Der ein oder andere kennt es vielleicht noch aus einem älteren Artikel.

function normalizeObject( $object )
{
  $object = doSth( $object );
  return $object;
}

$object = normalizeObject( $object );

In diesem Beispiel wird das per Parameter übergebene Objekt verändert und wieder zurückgegeben. Sieht eigentlich aus wie eine Standardmethode, wie sie in jedem Stück Software auch vorkommen kann. Aber warum macht der Mann an der Tastatur so einen Aufstand?

Ich verändere den übergebenen Parameter, ohne jemand einen Anhaltspunkt zu geben, dass ich dies mache. In meinem Beispiel macht das absolut nichts aus, denn ich hantiere danach wieder mit dem Objekt und überschreibe die geschehenen Änderungen wieder. Wenn wir aber die gegebene Funktion ein wenig verändern, dass sieht es schon ganz anders aus.

function normalizeObject( $object )
{
  $object = doSth( $object );
  return $object;
}

$object2 = normalizeObject( $object );

Führt man dieses Programm aus, so verändern wir nicht nur $object2, sondern auch $object, ohne das wir das mitbekommen. In den meisten Fällen wäre die Anwendung der Funktion eher die erste, deswegen macht man sich kaum Gedanken, aber wenn man es dann doch mal anders macht, so kann man die Seiteneffekte nicht abschätzen.

Tut mir also den Gefallen, alle Parameter unangetastet lasst. Wenn ihr sie verändern wollt, dann cloned sie einfach vorher.

function normalizeObject( $object )
{
  $workingObject = clone( $object );
  $workingObject = doSth( $workingObject );
  return $workingObject;
}

$object2 = normalizeObject( $object );

Sieht zwar ein wenig aufwändiger aus, aber es wird euch wirklich helfen, wenn ihr mal wieder einen Fehler finden müsst, der durch so eine Fehlkonstruktion passiert. Wenn ich ehrlich bin, würde ich sogar diesen Umgang mit primitiven Typen vorschlagen. Es ist zwar nicht notwendig, aber wenn man sich mal dran gewöhnt hat, dann stabilisiert man seine Programme auf diese Weise. Wer also Ahnung im Umgang mit dem PHP_CodeSniffer hat, der sollte mal eine Regel schreiben, die so etwas verbietet.

Ü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

12 Comments

  1. hi nils, frag nicht wieviele functionen (nicht methoden) ich beim wechsel von php4 auf php5 ändern musste weil manche programmierer felsenfest davon überzeugt waren das objecte immer kopiert werden.

    Reply
  2. @sven php macht ja eigentlcih keinen großen unterschied ob eine function innerhalb einer klasse ist oder nicht. „nicht methoden“ deswegen, weil ich die klassen größten teils refaktorisiert habe, der wiederverwendbarkeit wegen. aber so einzelne functions hab ich nur gefixt.

    Reply
  3. @Sebastian: Stimmt und steht im Text doch auch so drin. Ein oder 2 Sätze weiter 😉

    Ansonsten weiß ich nicht, ob der Ansatz NIE was zu verändern so gut ist. Es soll ja durchaus Methoden geben, die nur aus diesem Grund bestehen…

    Reply
  4. @Dominik: Danke für den Hinweis auf meinen Text. Call by reference finde ich gut, aber dann muss es ausgezeichnet sein. Also niemals den Parameter verändern, wenn es nicht explizit angegeben ist.

    Reply
  5. @Dominik: Es gibt auch Programmiersprachen (d.h. funktionale Sprachen), in denen keine Variablen verändert werden können, und das funktioniert sogar ganz gut.

    In der imperativen Welt ist das ständige Kopieren von Objekten allerdings meist nicht sehr effizient, aber dafür bekommt man meist robusteren Code. Hierbei ist es schwierig, die goldene Mitte zu finden, aber das Dokumentieren von Methoden, die ihre Parameter verändern, halte ich für einen guten Anfang. Vielleicht sollte man im Allgemeinen mehr auf „immutable objects“ setzen um weitere Fehlerquellen zu vermeiden.

    Reply
  6. Liegt denn das Problem in diesem Fall nicht eher bei der (unbekannten) Funktion doSth oder dient die nur als Platzhalter dafür, dass mit dem Objekt irgendwas geschieht?

    Darüberhinaus würde ich persönlich davon ausgehen, dass eine Funktion namens „normalizeObject“ das ihr übergebene Objekt auch verändert, allein schon wegen des Namens (im Gegensatz zu „createNormalizedObject“ oder „getNormalizedObject“).

    Das Klonen ist darüberhinaus u.U. nicht ausreichend, wenn nämlich das Objekt Referenzen auf weitere Objekte hält, die verändert werden, vielleicht sogar nur indirekt durch Aufrufe von Methoden des geklonten Objekts.

    Und last but not least: Der Begriff „Referenz“ hat in PHP eine feste Bedeutung, eine Übergabe per Referenz findet nämlich gar nicht statt! Die PHP-Dokumentation spricht von „Object Identifier“. Im Klartext:

    class Foo{}

    function perValue($obj){
    $obj=new Foo();}

    function perReference(&$obj){
    $obj=new Foo();}

    $foo=new Foo();
    perValue($foo); // $foo ist immer noch das alte $foo!
    perReference($foo); // $foo ist nun das in perReference erzeugte $foo!

    Lesenswert: http://www.php.net/manual/en/language.oop5.references.php

    Reply
  7. @Nils: Das Problem ist, dass der Satz „Parameter werden seit der Version 5 von PHP als Referenz übergeben auch wenn man den & Operator nicht explizit angibt.“ suggeriert, dass in PHP 5 bei der Übergabe von Objekten
    function doSomething($obj)
    und
    function doSomething(&$obj)
    identisch seien, das ist aber nicht der Fall.

    Reply
  8. @Timo: Ja da hast du Recht. Hab den Artikel auch ein wenig höher priorisiert und wenn er denn mal da ist, werde ich diesen Artikel auch darauf verlinken.
    PS: Du hast nicht etwa Lust, den Artikel zu schreiben? 😉

    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