Anonyme- /Lambda-Funktionen oder Closure in PHP

In PHP gibt seit fast 4 Jahren anonyme Funktionen. In diesem Artikel erläutert unser Senior Entwickler Ronald Marske anhand einfacher Beispiele die Nützlichkeit der anonymen Funktionen und weist auf die neuen Funktionen in PHP 5.4 hin.

Anonyme Funktionen

Eine anonyme Funktion ist eine Funktion (Programmteil), die nicht über ihren Namen sondern nur über Verweise wie Referenzen oder Zeiger angesprochen werden kann.

Lambda

Der Begriff Lambda-Funktion wird als synonym für anonyme Funktionen verwendet.

Closure

Als Closure wird eine Funktion beschrieben die sich ihren Erstellungskontext merkt. Bei einem Aufruf der Funktion kann dann auf den Erstellungskontext zurückgegriffen werden. (klassisch in Java-Script)

Seit Version 5.3 in PHP enthalten (30.06.2009)

// normale Funktion
function add($a, $b) {
    return $a + $b;
}
// Anonyme Funktion
$add 	= function($a, $b) {
    return $a + $b;
};
echo 	add(1,2); // Ausgabe: 3
echo 	$add(1,2); // Ausgabe: 3

Einsatzgebiete

PHP bietet schon seit langem das sogenannte „callback“ Prinzip an. Beispielsweise bei Sortierungen von Arrays, Session Handlern oder XML Parsern.

Als Beispiel eine einfache Array Sortierung.

// der klassische Weg
function arraySortCallback($a, $b) {
    return $a - $b;
}
$a = array(9, 2, 6, 7, 10, 12, 11, 5, 8, 1, 3, 4);
usort($a, 'arraySortCallback');
 
// mit anonymer Funktion
$a = array(9, 2, 6, 7, 10, 12, 11, 5, 8, 1, 3, 4);
$arraySortCallback = function($a, $b) {
    return $a - $b;
};
usort($a, $arraySortCallback);
 
// oder
usort($a, function($a, $b) {
return $a - $b;
});

Das klassische Closure Verhalten wird bei dem gezeigten Beispiel nicht angewendet, weshalb man bei diesen Verwendungen von anonymen Funktionen spricht. Das in Java-Script übliche Prinzip des Erstellungskontextes gibt es in der Form in PHP nicht. Die Entwickler von PHP haben dennoch eine Möglichkeit geschaffen, dieses Verhalten zu simulieren. Man muss bei der Erstellung des Closure die benötigten Variablen an die Funktion übergeben. Dazu wurde das „use“ Statement etabliert.

// Beispiel eines Closures
$foo = 'bar';
$hello = function($name = null) use ($foo) {
    return 'Hallo ' . ((null !== $name) ? $name : $foo) . '!';
};
echo 	$hello(); // Ausgabe: Hallo bar!;
echo 	$hello('Heinz'); // Ausgabe: Hallo Heinz!

Aber Vorsicht!

Bei der Verwendung vom Erstellungskontext sollte man beachten, dass eine Änderung eine Variable nach der Funktionsdeklaration nur greift, sofern die benötigte Variable als Referenz übergeben wird.

// Copy-on-write
$a = 1;
$f = function() use($a) {
    echo $a;
};
$a = 2;
$f(); // Ausgabe: 1
 
// call-by-reference
$a = 1;
$f = function() use(&$a) {
    echo $a;
};
$a = 2;
$f(); // Ausgabe: 2

Rekursionen mit Closures

Sofern man Closures rekursiv aufrufen möchte, sollte man bedenken, dass dies nur mit einem kleinen Trick funktioniert. Man muss den Erstellungskontext, also die Funktion selbst, als Referenz übergeben. Als Beispiel eine sehr einfache Potenzrechnung.

$pot = function($b, $e) use(&$pot) {
    if (0 < $e) {
        return $b * $pot($b, $e-1);
    } else {
        return 1;
    }
};
echo $pot(2,4); // Ausgabe: 16

Ein, wie ich finde, sehr schönes Beispiel der Benutzung von Closures ist das Filtern von Arrays über einen regulären Ausdruck.

function createMatcher($pattern) {
    return function($input) use($pattern) {
        return preg_match($input, $pattern);
    }
}
$array = array('a', 'b', 'c', 'd');
$filtered = array_filter($array, createMatcher('/[ad]/i'));
// array('a', 'd')

Neuerungen in PHP 5.4

Auch bei den Neuerungen haben sich die PHP Entwickler von Java-Script inspirieren lassen. In JavaScript ist es möglich Objekte oder Variable an die Funktion zu binden. Dieses Verhalten bietet PHP nun viele neue Möglichkeiten im Einsatzgebiet. Die Closure Klasse wurde um die Methoden „bind“ und „bindTo“ erweitert. Hierbei ist wichtig zu wissen, dass die Verwendung einen „gebunden Klon“ zurückliefert.

Durch die Bindung an Objekten besteht nun die Möglichkeit auf „protected“ oder „private“ Variablen zuzugreifen. Dazu reicht es jedoch nicht aus allein das Closure die Objektinstanz zu binden. Man muss dazu den 2ten Parameter, den Klassennamen, mit übergeben um diesen Zugriff zu erhalten.

Class A {
    private $private = 'private';
    protected $protected = 'protected';
    public $public = 'public';
}
Class B extends A {}
 
$accessor = function($var) {
    var_dump($this->{$var});
}
 
$a = new A();
$cAccessor = $accessor->bindTo($a);
$cAccessor('public'); // Ausgabe: public
$cAccessor('protected'); // Fatal Error
$cAccessor('private'); // Fatal Error
 
$cAccessor = $accessor->bindTo($a, 'A');
$cAccessor('public'); // Ausgabe: public
$cAccessor('protected'); // Ausgabe protected
$cAccessor('private'); // Ausgabe: private
 
$b = new B();
$cAccessor = $accessor->bindTo($b);
$cAccessor('public'); // Ausgabe: public
$cAccessor('protected'); // Fatal Error
$cAccessor('private'); // Fatal Error
 
$cAccessor = $accessor->bindTo($b, 'B');
$cAccessor('public'); // Ausgabe: public
$cAccessor('protected'); // Ausgabe protected
$cAccessor('private'); // Fatal Error
 
$cAccessor = $accessor->bindTo($b, 'A');
$cAccessor('public'); // Ausgabe: public
$cAccessor('protected'); // Ausgabe protected
$cAccessor('private'); // Ausgabe: private

Selbiges gilt für Statische Variablen

Class 	A {
    public static $public = 'public';
    protected static $protected = 'protected';
    private static $private = 'private';
}
Class 	B extends A {}
 
$accessor = function($var) {
    var_dump(self::{$var});
};
 
$cAccessor = $accessor->bindTo(null, 'A');
$cAccessor('public'); // Ausgabe: public
$cAccessor('protected'); // Ausgabe: protected
$cAccessor('private'); // Ausgabe: private
 
$cAccessor = $accessor->bindTo(null, 'B');
$cAccessor('public'); // Ausgabe: public
$cAccessor('protected'); // Ausgabe: protected
$cAccessor('private'); // Fatal Error

Quelle: PHPMagazin 2.2013 (Ganz ohne Namen)

2 responses on “Anonyme- /Lambda-Funktionen oder Closure in PHP

  1. Ronald Marske

    Traits sind ebenfalls eine Neuerung in PHP 5.4. Das Verhalten und die Anwendung von Traits sind jedoch andere als die von anonymen Funktionen. Traits ermöglichen es dem Programmierer, Funktionen zu definieren, die in ein jedes beliebiges Klassenobjekt eingebunden werden können. Durch ihre vorherige Deklaration in einer Art Klassenobjekt sind sie jedoch nicht anonym.

  2. Sonja Dif

    der Facebook-User Andreas Möller stellte die Frage: Und was ist mit Traits? Die Frage wird von unserem Webentwickler beantwortet.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.