Ab der Version 5.3 bietet auch PHP, wie auch schon viele andere Sprachen, die Möglichkeit mit anonymen Funktionen zu arbeiten. Dadurch können z.B. Callback-Funktionen zur Filterung o.Ä. implementiert werden, ohne dass man sich dafür tolle Namen ausdenken muss. Aber was kann man damit noch machen?
Bislang musste man Callback-Funktionen gewöhnlich definieren, bevor man sie verwenden konnte.
<?php function myCallback($output) { return '##' . $output . '##'; } echo call_user_func('myCallback', 'Hallo Welt'); // Ausgabe: ##Hallo Welt##
Als Closure geschrieben sähe das dann so aus:
<?php echo call_user_func( function($output) { return '##' . $output . '##'; }, 'Hallo Welt'); // Ausgabe: ##Hallo Welt##
Zugegeben, dieses Beispiel hätte man durch einfache Stringverkettung wesentlich einfacher schreiben können und eigentlich war das mit create_function() auch schon unter PHP4 möglich.
Wir können die Funktion natürlich wie früher auch schon in einer Variablen speichern und dann weiter verwenden.
<?php $myFunction = function($output) { return '##' . $output . '##'; }; echo call_user_func($myFunction, 'Hallo Welt') . '<br/>'; echo $myFunction('Hallo Welt') . '<br/>'; //Ausgabe: ##Hallo Welt## ##Hallo Welt##
Und auch das ist ja noch nichts Neues. Mit Closures haben wir nun aber auch die Möglichkeit, teile unserer “Umgebung” zum Zeitpunkt der Funktionsdefinition zu konservieren.
Dazu müssen wir nur die Definition etwas erweitern.
<?php $before = '>'; $after = '<'; $myFunction = function($output) use ($before, $after) { return $before . $output . $after; }; echo $myFunction('Hallo Welt') . '<br/>'; //Ausgabe: >Hallo Welt<
An dieser Stelle frieren wir mit “use” sozusagen die Werte für $before und $after innerhalb unserer Funktion ein. Spätere Änderungen der beiden Variablen haben keine Auswirkungen auf die Funktion.
$before = 'foo'; $after = 'bar'; echo $myFunction('Hallo Welt') . '<br/>'; //Ausgabe: >Hallo Welt<
In PHP wir mittlerweile sehr viel mit Standardinterfaces und -klassen (SPL) abgedeckt und so ist es auch in diesem Fall. In der Variablen $myFunction ist ein Objekt vom Typ “Closure” gespeichert.
<?php echo '<pre>' . print_r($myFunction, true) . '</pre>'; //Ausgabe: Closure Object ( [static] => Array ( [before] => > [after] => < ) [parameter] => Array ( [$output] => ) )
Will man eigene Objekte wie Funktionen aufrufen, dann geht das mit der magischen Methode __invoke(), die auch hier verwendet wird.
Anfangs erwähnte ich, dass Closure-Callbacks zur Filterung, aber auch zur Manipulation von Array verwendet werden können.
Wenn nur der Inhalt interessant ist, dann kommen wir mit array_map() und einem Parameter aus.
<?php $values = array( array( 'a' => array( 'b' => 'foo' ) ), array( 'a' => array( 'b' => 'bar' ) ), ); $filtered = array_map( function ($item) { return $item['a']['b']; }, $values ); echo '<pre>' . print_r($filtered, true) . '</pre>'; // Ausgabe: Array ( [0] => foo [1] => bar )
Wollen wir aber Werte im Array in Abhängigkeit zum Key/Index verändern, dann geht das mit array_walk und einem zweiten Parameter im Callback. Bei beiden kann natürlich auch “use” verwendet werden. Der Parameter $value wird dabei als Referenz übernommen.
$values = array( 'a' => 100, 'b' => 200, 'c' => 300, ); array_walk( $values, function (&$value, $key) { if ('b' == $key) { $value = 42; } } ); echo '<pre>' . print_r($values, true) . '</pre>'; // Ausgabe: Array ( [a] => 100 [b] => 42 [c] => 300 )
Ich hoffe, dass ich ein wenig Licht ins Dunkel bringen konnte und “Closure” nun kein geheimnisvolles Zauberwort mehr ist.
Wenn ihr noch gute Ergänzungen oder Anwendungbeispiele habt, dann immer her damit und bitte fleissig kommentieren.
31.01.2012 um 08:04 Uhr
danke, sehr gut erklärt!!!
01.02.2012 um 05:23 Uhr
Die Erklärung kam gerade zur rechten Zeit. Beschäftige mich gerade mit anderen Sprachen, wo anonyme Funktionen Standard sind. Und war gerade auf der Suche nach einer Auffrischung in PHP.
Danke dafür.
09.02.2012 um 08:35 Uhr
Hmm. Meine Meinung zu Closures:
Kann man machen. Muss man aber nicht!
Ein wirklich sinnvoller Praxisfall ist mir noch nicht unter gekommen. Wenn mir den jemand zeigt, lasse ich mich gerne überzeugen
Pingback: Sortieren mit PHP5 Closures und dem Java Comparator Pattern – ebene7