Generatoren, yield

Das Auslesen eines Arrays mithilfe von foreach wird recht häufig benötigt. Nicht selten werden Werte in einem Array gesammelt und danach das Array durchlaufen. Alternativ kann man einen Generator verwenden.

Generator anstelle Array für foreach

Das Ganze beginnt mit der Erstellung einer sogenannten Generatorfunktion (generator function) Eine Generatorfunktion ist als solche nur daran zu erkennen, dass das Schlüsselwort yield verwendet wird, als Beispiel die Funktion back_to-zero():
function back_to_zero($p = 4)
{
  yield "Start";
  while ($p > 0)
  {
    yield $p; 
    $p--;
  }
  yield "Stop";
}
Diese Funktion soll nur Werte bereitstellen. Wo die Werte herkommen, ist vollkommen nebensächlich. Zur Demonstration werden die Zeichenketten "Start" und "Stop" bereitgestellt und dazwischen wird der Parameter $p (Vorgabewert: 4) ausgegeben und heruntergezählt, bis er 0 ist.
Vor jedem Wert, der ausgegeben werden soll, wird die Anweisung yield gesetzt. Tatsächlich kann man beliebig viele yield-Anweisungen notieren.
An dieser Stelle ist noch nicht klar, wie das Ganze funktionieren soll.
Bitte nicht darüber nachdenken, erst mal weiterlesen!

Eine Generatorfunktion kann nur einen Generator erzeugen

Der Aufruf einer Generatorfunktion liefert nicht das Ergebnis, was man von einer normalen Funktion erwarten würde.
Schauen Sie sich diese Anweisung an:
$gen = back_to_zero();
Man könnte denken, die Funktion arbeitet ihren Code ab und sendet das Ergebnis an $gen. Aber genau das passiert nicht! Diese Anweisung funktioniert eher wie eine Deklaration, d.h. PHP leitet aus der Generatorfunktion ein Generator-Objekt ab.
Die Variable $gen ist jetzt also ein Generator.
Und zwar ist der Generator ein Objekt, das man einer foreach-Schleife übergeben kann.
foreach ($gen as $key => $val) 
{
  print "  $key: $val\n";
}
In diesem Code wird der Generator ($gen) so verwendet, als sei er ein reguläres PHP-Array. Und tatsächlich erkennt die Schleife einen Index und den Wert.
$ php gen_and_yield.php 
  0: Start
  1: 4
  2: 3
  3: 2
  4: 1
  5: Stop
Man kann sich vorstellen, dass erst bei Abruf der Werte durch foreach die Generatorfunktion startet, einen Wert liefert, und bis zum nächsten Wertabruf wartet. Beim nächsten Schleifendurchgang den nächsten Wert liefert, na usw.
Wurden alle Werte abgerufen, ist es nicht mehr möglich, weitere Werte aus dem Generator innerhalb derselben Laufzeit zu bekommen.
Soweit das grundlegende Prinzip.

Diskussion

Es ergibt sich die Frage, wozu das Ganze gut ist. Laut PHP-Handbuch muss man nicht alle zu durchlaufenden Werte erst in einem Array vorhalten, bevor foreach arbeiten kann.
Das kann sicher sinnvoll sein, wenn es sich um sehr komplexe Werte handelt, oder um sehr viele Werte, oder um Werte, deren Beschaffung einen größeren programmtechnischen Aufwand bedeutet.
Diesen Aufwand könnte man in einen Generator packen, und jeder Wert wird erst dann besorgt, wenn er auch wirklich benötigt wird. Dadurch kann der Bedarf an Hauptspeicher (RAM) während der Laufzeit kleiner sein, als wenn man vorher alle Werte erst in einem Array einsammelt.
Viel Spaß beim Experimentieren mit Generatoren!