Anonyme Funktionen

Definition

Die Definition einer anonymen (namenlosen) Funktion sieht aus wie eine Variablenzuweisung. Nur, dass statt eines Wertes ein Funktionskörper zugewiesen wird.
$kubik = function ($par) {  # kein Funktionsname, aber Parameter
  return ($par * $par * $par); 
}; # Semikolon erforderlich
Und wie bei einer regulären Variableninitialisierung muss ein Semikolon am Ende stehen.
Anonyme Funktionen sind in erster Linie dazu gedacht, als Call-Back-Funktionen verwendet zu werden. Die direkte Notierung z.B. als Argument einer anderen Funktion ohne den Umweg über eine Variablenzuweisung wäre der typischere Anwendungsfall.
Wir wollen hier aber in erster Linie die genaue Funktionsweise herausfinden.
Um die Funktion aufzurufen, verwenden wir die Variable wie einen Funktionsnamen.
print $kubik(3).PHP_EOL; // Ausgabe: 27
Laut Handbuch ist eine anonyme Funktion ein Objekt der Klasse Closure, die in PHP vordefiniert ist. Das kann man sehr leicht herausfinden:
print get_class($kubik).PHP_EOL; // Ausgabe: Closure

Scoping

Als Nächstes testen wir die Sichtbarkeit einer anonymen Funktion.
Die Vorbereitungen dazu sind etwas aufwendiger. Wir erweitern den obigen Code mit einer regulären Funktion mit dem Namen hochdrei(), die das Gleiche macht wie unsere anonyme Funktion.
function hochdrei ($par) {
	return ($par * $par * $par);
} # kein Semikolon bei Definition einer benannten Funktion erforderlich
Nun versuchen wir, beide Funktionen innerhalb der Funktion ausgabe() aufzurufen.
# Funktion wird definiert ...
function ausgabe ()
{
  print hochdrei(2).PHP_EOL;   # Aufruf der benannten Funktion // 8

  # und jetzt Aufruf unserer anonymen Funktion
  print $kubik(2).PHP_EOL; // Fataler Fehler!
}

# ... und hier ausgeführt
ausgabe();
Ein Syntaxfehler wird nicht gemeldet, jedoch führt die letzte Zeile echo $kubik(2); zu einem fatalen Fehler. Wir müssen diese Zeile auskommentieren.
Der Fehler erklärt sich daraus, das $kubik eine außerhalb von ausgabe() definierte Variable ist und daher innerhalb der Funktion nicht sichtbar ist.
Beziehen wir die Definition der anonymen Funktion in die andere Funktion mit ein, dann kann sie darin ohne weiteres aufgerufen werden.
function anzeige ()
{
  $kubik = function ($n){
	  return ($n * $n * $n);
  };

  print $kubik(7).PHP_EOL; # funktioniert
}
anzeige(); // 343

Weitere Möglichkeiten

Eine anonyme Funktion kann Rückgabewert einer regulären Funktion sein.
function dritte_potenz ()
{
  return function ($par){
	  return ($par * $par * $par);
  };
}
Weisen wir den Aufruf einer Variablen zu, dann ist diese eine anonyme Funktion.
$wert = dritte_potenz();
print $wert(5).PHP_EOL;  // 125
Man kann auf die Rückgabe an eine Variable auch verzichten und den Aufruf selbst als anonyme Funktion betrachten.
print dritte_potenz()(5).PHP_EOL; // 125
Oder wir verzichten auf die umhüllende Funktion und notieren die anonyme Funktion an Ort und Stelle wo sie gebraucht wird. Dann muss man die Definition aber in Klammern setzen.
print (function ($par) {
	return ($par * $par * $par); 
})(5).PHP_EOL;  // 125
Ausdrücke in Klammern werden zuerst ausgeführt bevor der Gesamtausdruck abgearbeitet wird. Damit ist sichergestellt, dass die Funktionsdefinition vor dem Funktionsaufruf ausgeführt wird.
Diese Schreibweise kommt den gedachten Einsatzfällen nahe. Kleine Funktionen, die nur einmal im Programm gebraucht werden, können so recht gut als anonyme Funktionen an Ort und Stelle notiert werden.
Man kann diese Möglichkeit cool finden und alle möglichen Funktionen als anonyme notieren. Das führt zu ineinander verschachtelten Funktionen, wie es zurzeit häufig in JavaScript gemacht wird. Die Lesbarkeit des Codes verbessert das m.E. nicht gerade und es erinnert eher an den früheren Spaghetti-Code. Weniger ist hier wohl mehr.

Äußere Werte einbeziehen und Pfeilfunktionen

Erweiterung use()

Die folgende anonyme Funktion berechnet den prozentalen Anteil einer Zahl (im Beispiel der Wert 72) und gibt das Ergebnis mit einer Maßeinheit (im Beispiel kg) aus.
$prozent = 60;
$einheit = 'kg';
$anteil = function ($zahl) use ($prozent, $einheit) {
  return ($zahl * $prozent / 100) . " $einheit";
};
print $anteil(72).PHP_EOL; // 43.2 kg
Die Prozentzahl und die Maßeinheit sind außerhalb definiert und sind innerhalb der Funktion nicht sichtbar, wie wir bereits festgestellt haben.
Mithilfe der Syntaxerweiterung use() ist jedoch möglich, äußere Werte in die Funktionsdefinition einzubeziehen. Dabei funktioniert die Übergabe der Werte über use() genauso wie die Übergabe von Argumenten an Parameter. Das heißt, die Werte der äußeren Variablen vor der Funktionsdefinition gehen in diese ein und bleiben darin unverändert.
Ein use()-Konstrukt gibt es auch bei Verwendung von traits. Das hat aber mit dem use() hier nichts zu tun.
Bei einem erneuten Aufruf mit einer anderen Prozentzahl und einer anderen Einheit, etwa so:
$prozent = 40;
$einheit = "Kilogramm";
print $anteil(72).PHP_EOL; // immer noch 43.2 kg
ändert sich gar nichts, weil eben die Werte von use() bei der Definition gelesen werden und nicht beim Aufruf.
Mit einer kleinen Änderung kann es trotzdem funktionieren. Wir notieren die Werte in use() als Referenzen auf die äußeren Werte:
...
$anteil = function ($zahl) use (&$prozent, &$einheit) {
  return ($zahl * $prozent / 100) . " $einheit";
};
...
Jetzt werden die äußeren Variablen quasi intern verwendet und wir erhalten beim zweiten Aufruf die erwartete Ausgabe 28.8 Kilogramm.

Pfeilfunktionen

Pfeilfunktionen sind eine andere Schreibweise anonymer Funktionen, jedoch keine alternative Syntax für jede anonyme Funktion.
Anonyme Funktionen, die nur eine Anweisung enthalten, können als Pfeilfunktionen notiert werden. Das trifft auf unser vorheriges Beispiel zu, und wir notieren es als Pfeilfunktion.
$prozent = 30;
$einheit = 'mm';
$anteil = fn($zahl) => ($zahl * $prozent / 100) . " $einheit" ;
print $anteil(72).PHP_EOL; // 21.6 mm 
  • Das Schlüsselwort function schrumpft zum Kürzel fn (fn ist ein neues Schlüsselwort)
  • Der Funktionskörper ist nicht in Klammern gefasst, sondern nach nach dem Pfeil => notiert.
  • Der resultierende Wert des Ausdrucks im Funktionskörper ist automatisch auch der Rückgabewert.
  • Ein use() wird ebenfalls implizit ausgeführt, wenn im Funktionskörper die Namen externer Werte verwendet werden (implicit by-value scope binding).
Damit funktioniert dieses Beispiel wie das vorhergehende, jedoch mit einer Einschränkung.
  • Referenzen im Funktionskörper, z.B. auf $prozent und/oder $einheit, führten zu Syntaxfehlern.