Generators und yield: große Datenmengen speicherschonend verarbeiten
AI generated
<?php
8.4
PHP 8.4 · Generators · yield · Performance
Generators und yield:
große Datenmengen speicherschonend verarbeiten

PHP-Generators verwandeln gewöhnliche Funktionen über das Schlüsselwort yield in Iteratoren, die Werte einzeln statt komplett im Speicher erzeugen. Statt zehntausende Zeilen einer CSV-Datei oder ein komplettes Datenbank-Resultset als Array zu laden, liefert ein Generator Element für Element und hält den Speicherverbrauch konstant, unabhängig von der Datenmenge. Wer große Importe, Exporte oder Streaming-Pipelines in PHP 8.4 baut, kommt an yield kaum vorbei.

18 Min. Lesezeit yield · yield from · send() · getReturn() PHP 8.4 · Iterator-Interface

1. Was Generators sind und warum Lazy Evaluation zählt

Ein Generator in PHP ist eine Funktion, die statt eines einzigen Rückgabewerts eine Folge von Werten liefert, und zwar einen nach dem anderen, erst dann, wenn der Aufrufer tatsächlich danach fragt. Sobald eine Funktion irgendwo im Körper das Schlüsselwort yield enthält, wird sie von PHP automatisch zu einem Generator. Der Rückgabewert ist kein Array und kein einzelner Wert, sondern eine Instanz der eingebauten Klasse Generator, die intern das Iterator-Interface implementiert und sich deshalb in jeder foreach-Schleife wie eine gewöhnliche Collection verhält.

Der entscheidende Unterschied zu einem Array liegt in der Auswertungsstrategie. Eine Funktion, die ein komplettes Array zurückgibt, muss zuerst jedes einzelne Element berechnen und im Speicher ablegen, bevor der Aufrufer auch nur das erste Element sieht. Das nennt man eager evaluation. Ein Generator dagegen berechnet nur das nächste Element, wenn es tatsächlich angefordert wird, und hält zwischen zwei Werten keinen wachsenden Puffer im Speicher. Diese Lazy Evaluation ist der Kern dessen, warum Generators und yield für große Datenmengen so wichtig sind. Der Speicherverbrauch bleibt über die gesamte Verarbeitung nahezu konstant, unabhängig davon, ob zehn oder zehn Millionen Elemente verarbeitet werden.

Am deutlichsten wird das an einem direkten Vergleich: eine Funktion, die eine komplette Datei mit file() in ein Array aus Zeilen einliest, gegen eine Generator-Funktion, die mit fgets() und yield Zeile für Zeile liefert. Der folgende Code misst beide Varianten mit memory_get_peak_usage() gegen dieselbe Datei.


<?php

declare(strict_types=1);

namespace App\Import;

use Generator;
use RuntimeException;

/**
 * Eager approach: loads the entire file into memory as an array of lines.
 */
function readLinesEager(string $path): array
{
    $lines = file($path, FILE_IGNORE_NEW_LINES);
    if ($lines === false) {
        throw new RuntimeException("Cannot read file: {$path}");
    }

    return $lines;
}

/**
 * Lazy approach: yields one line at a time, memory footprint stays constant.
 */
function readLinesLazy(string $path): Generator
{
    $handle = fopen($path, 'rb');
    if ($handle === false) {
        throw new RuntimeException("Cannot open file: {$path}");
    }

    try {
        while (($line = fgets($handle)) !== false) {
            yield rtrim($line, "\r\n");
        }
    } finally {
        fclose($handle);
    }
}

$file = __DIR__ . '/large-export.csv'; // 500,000 lines

$before = memory_get_peak_usage(true);
$eagerLines = readLinesEager($file);
$eagerPeak = memory_get_peak_usage(true) - $before;
unset($eagerLines);

$before = memory_get_peak_usage(true);
foreach (readLinesLazy($file) as $line) {
    // process one line at a time, nothing accumulates
}
$lazyPeak = memory_get_peak_usage(true) - $before;

printf("Eager: %d MB, Lazy: %d MB\n", (int) ($eagerPeak / 1_048_576), (int) ($lazyPeak / 1_048_576));
// Typical result on a 500,000-line CSV file: Eager approx 210 MB, Lazy approx 2 MB

Auf einer Testdatei mit 500.000 Zeilen liegt der Peak-Speicherverbrauch der eager-Variante bei über 200 MB, während die generator-basierte Variante durchgehend im niedrigen einstelligen MB-Bereich bleibt, weil zu jedem Zeitpunkt nur eine einzige Zeile im Speicher gehalten wird. Das ist der praktische Grund, warum Generators bei Imports, Exports und ETL-Jobs in PHP so relevant sind.

2. Die Mechanik von yield: Pause und Fortsetzung

Wenn eine Generator-Funktion aufgerufen wird, läuft ihr Code zunächst überhaupt nicht. PHP erstellt lediglich ein Generator-Objekt und merkt sich den Ausführungskontext der Funktion. Erst wenn der Aufrufer zum ersten Mal einen Wert anfordert, sei es über foreach, current() oder next(), beginnt die Funktion tatsächlich zu laufen und läuft bis zur ersten yield-Anweisung. Dort pausiert die Ausführung, der aktuelle Wert wird an den Aufrufer zurückgegeben, und der komplette Zustand der Funktion, alle lokalen Variablen, die Position im Code und die Aufrufstruktur, bleibt erhalten, bis die Ausführung fortgesetzt wird.

yield ist dabei kein reines Statement, sondern ein Ausdruck mit einem eigenen Wert. $empfangen = yield $wert; ist gültiger Code. $wert wird an den Aufrufer geliefert, die Ausführung pausiert, und sobald der Aufrufer send() aufruft, wird der übergebene Wert zum Ergebnis des yield-Ausdrucks und in $empfangen gespeichert. Dieses Detail ist die Grundlage dafür, dass Generators auch als leichte Koroutinen dienen können, die nicht nur Werte liefern, sondern auch Werte entgegennehmen.

Neben einfachen Werten unterstützt yield auch Schlüssel-Wert-Paare über die Syntax yield $key => $value. Damit lässt sich ein Generator bauen, der sich beim Durchlaufen mit foreach ($generator as $key => $value) exakt wie ein assoziatives Array verhält, ohne dass jemals ein komplettes assoziatives Array im Speicher existiert. Werden keine expliziten Schlüssel angegeben, vergibt PHP automatisch fortlaufende Integer-Schlüssel, genau wie bei einem regulären indexierten Array.

3. yield from: Generators verketten und verschachteln

yield from delegiert die Erzeugung von Werten an einen anderen Generator, ein Array oder ein beliebiges Traversable-Objekt. Statt jeden Wert eines verschachtelten Generators manuell in einer Schleife weiterzureichen, übernimmt yield from diese Arbeit direkt und reicht dabei sogar Schlüssel, Rückgabewerte und über send() hineingeschickte Werte transparent durch. Das macht es möglich, kleine Generator-Funktionen zu größeren Verarbeitungsketten zu komponieren, ohne die Lazy-Evaluation-Eigenschaft zu verlieren.

Ein klassischer Anwendungsfall ist das Abflachen verschachtelter Strukturen, etwa eines Kategoriebaums. Eine rekursive Funktion, die bei jedem verschachtelten Array yield from auf sich selbst aufruft, liefert am Ende einen flachen Strom aller Blattwerte, ohne dass die Struktur jemals als komplettes flaches Array im Speicher aufgebaut werden muss. Jede Ebene der Rekursion bleibt dabei ein eigener, unabhängiger Generator.


<?php

declare(strict_types=1);

namespace App\Tree;

use Generator;

/**
 * Recursively flattens a nested array structure using yield from.
 *
 * @param array<int|string, mixed> $node
 * @return Generator<int|string, mixed>
 */
function flatten(array $node): Generator
{
    foreach ($node as $key => $value) {
        if (is_array($value)) {
            // Delegate to a nested generator call, composing lazily
            yield from flatten($value);
            continue;
        }

        yield $key => $value;
    }
}

$categoryTree = [
    'electronics' => [
        'phones' => ['iphone-15', 'pixel-9'],
        'laptops' => [
            'gaming' => ['rog-strix', 'legion-7'],
            'business' => ['thinkpad-x1'],
        ],
    ],
    'books' => ['fiction', 'non-fiction'],
];

foreach (flatten($categoryTree) as $key => $slug) {
    echo "{$key} => {$slug}\n";
}

Wichtig ist, dass yield from selbst keinen neuen Wert erzeugt, sondern lediglich die Werte des delegierten Iterables durchreicht. Die Anzahl der tatsächlichen yield-Punkte in der Aufrufkette bleibt dabei unsichtbar für den Aufrufer, der einfach nur über das äußere Generator-Objekt iteriert.

4. Werte hineinschicken: send() und getReturn()

Ein Generator ist keine Einbahnstraße. Über Generator::send($wert) kann der Aufrufer einen Wert in die pausierte Funktion hineinschicken, der zum Ergebnis des aktuellen yield-Ausdrucks wird. Die Funktion läuft danach weiter, bis zum nächsten yield oder bis zum Ende, und send() selbst gibt den nächsten produzierten Wert zurück. Dieses Muster macht Generators zu leichten Koroutinen: Zustand, Verarbeitung und Kommunikation in beide Richtungen, ganz ohne Threads oder externe Queues.

Beendet eine Generator-Funktion mit einer return-Anweisung statt mit einem finalen yield, geht dieser Wert nicht in den normalen Iterationsablauf ein, er taucht in keiner foreach-Schleife auf. Stattdessen lässt er sich nach vollständiger Ausführung über Generator::getReturn() abfragen. Das ist nützlich, um zum Beispiel eine Gesamtsumme, eine Fehlerzahl oder ein Zusammenfassungsobjekt zu liefern, nachdem alle Einzelwerte bereits konsumiert wurden.


<?php

declare(strict_types=1);

namespace App\Coroutine;

use Generator;

/**
 * A generator that consumes values sent into it and accumulates a total,
 * acting as a lightweight coroutine for streaming aggregation.
 *
 * @return Generator<int, float, float|null, float>
 */
function runningTotal(): Generator
{
    $total = 0.0;

    while (true) {
        // yield as an expression: receives the value passed to send()
        $amount = yield $total;

        if ($amount === null) {
            return $total;
        }

        $total += $amount;
    }
}

$totals = runningTotal();
$totals->current(); // primes the generator: runs to the first yield, current value is 0.0

echo $totals->send(100.0) . "\n"; // resumes with $amount = 100.0, prints 100
echo $totals->send(250.0) . "\n"; // prints 350
echo $totals->send(50.0) . "\n";  // prints 400
$totals->send(null);               // resumes with $amount = null, executes return, generator finishes

echo $totals->getReturn() . "\n"; // 400, the final accumulated value

5. Große Datenmengen: CSV- und DB-Streaming mit Generators

Der praktisch wichtigste Einsatzzweck von Generators ist das zeilenweise Verarbeiten großer Datensätze, ohne die komplette Quelle im Speicher zu halten. Statt eine CSV-Datei mit Millionen Zeilen komplett einzulesen oder ein Datenbank-Resultset vollständig zu materialisieren, liefert eine Generator-Funktion Zeile für Zeile beziehungsweise Datensatz für Datensatz, während die zugrunde liegende Ressource, etwa ein Datei-Handle oder ein Datenbank-Cursor, im Hintergrund geöffnet bleibt.

Damit diese Ressource auch bei einem vorzeitigen Abbruch zuverlässig geschlossen wird, gehört ein try/finally-Block direkt in die Generator-Funktion. Der finally-Block läuft in drei Fällen: wenn der Generator vollständig konsumiert wurde, wenn die Schleife des Aufrufers mit break vorzeitig verlassen wird, und wenn der Generator ohne vollständige Iteration die Garbage Collection erreicht. Dieses Verhalten macht Generators sicher genug für produktiven Ressourcenumgang, ganz ohne manuelles Cleanup im Aufrufercode.


<?php

declare(strict_types=1);

namespace App\Import;

use Generator;
use RuntimeException;

/**
 * Streams rows from a large CSV file without loading it into memory.
 */
final class CsvRowStreamer
{
    public function __construct(
        private readonly string $path,
        private readonly string $delimiter = ',',
    ) {
    }

    /**
     * @return Generator<int, array<string, string>>
     */
    public function rows(): Generator
    {
        $handle = fopen($this->path, 'rb');
        if ($handle === false) {
            throw new RuntimeException("Cannot open CSV file: {$this->path}");
        }

        try {
            $header = fgetcsv($handle, 0, $this->delimiter);
            if ($header === false) {
                return;
            }

            while (($row = fgetcsv($handle, 0, $this->delimiter)) !== false) {
                yield array_combine($header, $row);
            }
        } finally {
            // Runs on full consumption, on early break, and on generator destruction
            fclose($handle);
        }
    }
}

$streamer = new CsvRowStreamer(__DIR__ . '/orders-export.csv');

$imported = 0;
foreach ($streamer->rows() as $order) {
    if ((float) $order['total'] > 10_000.0) {
        break; // early exit still triggers the finally block above
    }

    $imported++;
}

echo "Imported {$imported} orders before hitting the threshold\n";

Dasselbe Muster funktioniert identisch für Datenbank-Resultsets. Ein PDO-Statement mit ungepuffertem Cursor liefert Zeilen einzeln an eine Generator-Funktion, die sie weiterreicht, während die Verbindung erst nach der letzten Zeile oder nach dem finally-Block freigegeben wird. So lassen sich auch Millionen Datenbankzeilen mit konstantem Speicherverbrauch verarbeiten.

6. Grenzen von Generators: Forward-Only und Arrays

Ein Generator ist grundsätzlich forward-only. Er kann nur einmal von Anfang bis Ende durchlaufen werden. Ruft man rewind() ein zweites Mal auf einem Generator auf, der bereits mit der Iteration begonnen hat, wirft PHP eine Exception mit der Meldung, dass der Generator bereits läuft oder beendet ist. Auch eine zweite foreach-Schleife über denselben Generator liefert keine Werte mehr, weil die interne Ausführung bereits das Ende erreicht hat.

Die Funktion iterator_to_array() wandelt einen Generator in ein echtes Array um, hebt damit aber genau den Speichervorteil auf, für den man den Generator ursprünglich eingesetzt hat. Eine zusätzliche Falle: Ohne yield $key => $value mit eindeutigen Schlüsseln überschreiben sich Werte mit demselben automatisch vergebenen Integer-Schlüssel gegenseitig, sofern der zweite Parameter preserve_keys nicht explizit auf false gesetzt wird.

Es gibt reale Situationen, in denen ein Array trotzdem die richtige Wahl bleibt: wenn man die Gesamtzahl der Elemente vorab mit count() kennen muss, wenn dieselben Daten mehrfach durchlaufen werden, oder wenn array_map(), usort() oder ein wahlfreier Zugriff per Index direkt auf der Struktur benötigt werden. In diesen Fällen ist ein Generator nicht die bessere Lösung, sondern zusätzlicher Umweg über iterator_to_array(), der den eigentlichen Vorteil zunichtemacht.

7. Performance und Speicherverbrauch in Zahlen

Generators sind nicht kostenlos. Jeder Schritt einer Iteration bedarf eines internen Suspend- und Resume-Vorgangs in der Zend Engine, der einen messbaren, wenn auch kleinen Overhead pro Element erzeugt, typischerweise im niedrigen Mikrosekundenbereich. Anders als ein Array bietet ein Generator außerdem keinen wahlfreien Zugriff in konstanter Zeit. Um das tausendste Element zu erreichen, müssen alle vorherigen 999 Werte tatsächlich durchlaufen worden sein, es gibt keinen Index-Zugriff wie bei $array[999].

Was ein Generator im Gegenzug einspart, ist erheblich. Der Speicherverbrauch skaliert mit der Größe eines einzelnen Elements, nicht mit der Anzahl aller Elemente. Ein Array mit einer Million Integer-Werten belegt je nach PHP-Version und internem Overhead pro Zelle mehrere hundert Megabyte, weil jedes Element als eigenständige zval-Struktur im Speicher verwaltet wird. Ein Generator, der dieselbe Million Werte einzeln liefert, hält zu jedem Zeitpunkt nur ein einziges Element und den internen Ausführungszustand im Speicher, üblicherweise wenige Kilobyte.

Die praktische Faustregel: Wo die Anzahl der Elemente unter einigen tausend liegt und der Datensatz mehrfach oder wahlfrei benötigt wird, überwiegt der Overhead pro Iterationsschritt kaum spürbar. Ab einigen zehntausend Elementen, insbesondere bei Datei- oder Datenbank-Streaming, kippt die Rechnung klar zugunsten des Generators, weil der eingesparte Speicher den kleinen CPU-Overhead pro Schritt um Größenordnungen übersteigt.

8. Praktische Einsatzfälle: Pagination, Sequenzen, Pipelines

Neben Datei-Import eignen sich Generators hervorragend für das seitenweise Abrufen von API-Antworten. Eine Generator-Funktion kann eine paginierte REST-API automatisch Seite für Seite abfragen und dabei transparent yield from auf die einzelnen Datensätze jeder Seite anwenden, sodass der Aufrufer einfach über einzelne Elemente statt über Seiten iteriert, ohne die Pagination-Logik jemals selbst zu sehen.

Ein zweiter starker Anwendungsfall sind unendliche oder bedarfsgesteuerte Sequenzen. Die Fibonacci-Folge, Primzahlen oder fortlaufende IDs lassen sich als Generator mit einer Endlosschleife und yield im Rumpf modellieren. Da nur so viele Werte berechnet werden, wie der Aufrufer tatsächlich abruft, kostet eine solche Endlosschleife nichts, solange niemand ohne break darüber iteriert. Das erlaubt Formulierungen, die mit einem klassischen Array niemals möglich wären, ein unendliches Array würde schlicht nie fertig aufgebaut.

Verkettet man mehrere kleine Generator-Funktionen, entsteht eine Pipeline, in der jede Stufe unabhängig von den anderen bleibt. Eine Quelle produziert Rohwerte, ein Filter lässt nur bestimmte Werte durch, eine Transformation verändert sie. Weil jede Stufe selbst wieder ein Generator ist, bleibt die gesamte Kette lazy, und der Konsument am Ende entscheidet, wie viele Werte tatsächlich verarbeitet werden.


<?php

declare(strict_types=1);

namespace App\Pipeline;

use Generator;

/**
 * Produces an infinite sequence of natural numbers, lazily.
 *
 * @return Generator<int, int>
 */
function naturalNumbers(): Generator
{
    $n = 1;
    while (true) {
        yield $n++;
    }
}

/**
 * Filters an iterable lazily using a predicate.
 *
 * @param iterable<int> $numbers
 * @return Generator<int, int>
 */
function filterBy(iterable $numbers, callable $predicate): Generator
{
    foreach ($numbers as $number) {
        if ($predicate($number)) {
            yield $number;
        }
    }
}

/**
 * Maps an iterable lazily using a transformer.
 *
 * @param iterable<int> $numbers
 * @return Generator<int, int>
 */
function mapWith(iterable $numbers, callable $transformer): Generator
{
    foreach ($numbers as $number) {
        yield $transformer($number);
    }
}

$isPrime = static function (int $n): bool {
    if ($n < 2) {
        return false;
    }
    for ($i = 2; $i * $i <= $n; $i++) {
        if ($n % $i === 0) {
            return false;
        }
    }
    return true;
};

// Compose a pipeline: nothing is computed until the consumer iterates
$pipeline = mapWith(
    filterBy(naturalNumbers(), $isPrime),
    static fn (int $prime): int => $prime * $prime,
);

$firstFiveSquaredPrimes = [];
foreach ($pipeline as $value) {
    $firstFiveSquaredPrimes[] = $value;
    if (count($firstFiveSquaredPrimes) === 5) {
        break;
    }
}

echo implode(', ', $firstFiveSquaredPrimes) . "\n"; // 4, 9, 25, 49, 121

9. Array vs. Generator im Vergleich

Die Entscheidung zwischen Array und Generator ist keine Geschmacksfrage, sondern hängt von Datenmenge, Zugriffsmuster und Anzahl der benötigten Durchläufe ab. Die folgende Tabelle fasst die wichtigsten Unterschiede zusammen und zeigt, wann welche Struktur die bessere Wahl ist.

Merkmal Array Generator Empfehlung
Speicherverbrauch Wächst mit der Anzahl der Elemente Konstant, unabhängig von der Menge Generator bei großen Datenmengen
Mehrfach durchlaufbar Ja, beliebig oft Nein, nur einmal vorwärts Array bei mehreren Durchläufen
Direkte array_*-Funktionen Funktionieren direkt Erst nach iterator_to_array() Array wenn array_map/usort nötig
Wahlfreier Zugriff per Index O(1) direkter Zugriff Nicht möglich, nur sequenziell Array für Lookup-Strukturen
Typischer Einsatzzweck Kleine, mehrfach genutzte Datensätze Große Datenmengen, Streams, Sequenzen Nach Datenvolumen entscheiden

Die Tabelle macht deutlich, dass es sich nicht um ein Entweder-Oder handelt. In vielen realen Anwendungen kombiniert man beide Ansätze, etwa indem ein Generator Daten aus einer Datei streamt und nur der aktuell benötigte Ausschnitt am Ende mit iterator_to_array() in ein kleines Array überführt wird, um darauf mit array_map() oder usort() zu arbeiten. Der Generator übernimmt die speicherschonende Beschaffung, das Array die punktuelle, mehrfach nutzbare Verarbeitung.

10. Zusammenfassung

Generators und yield lösen ein sehr konkretes Problem: große oder potenziell unbegrenzte Datenmengen verarbeiten, ohne sie komplett im Speicher zu halten. Statt eine Funktion zu schreiben, die zuerst alles berechnet und dann zurückgibt, liefert eine Generator-Funktion Werte einzeln, im exakten Moment, in dem der Aufrufer sie anfordert. yield from erlaubt das Komponieren mehrerer Generators zu größeren Verarbeitungsketten, send() und getReturn() erweitern das Modell um Kommunikation in beide Richtungen und um einen finalen Rückgabewert nach der Iteration.

Wichtig bleibt die ehrliche Einordnung der Grenzen. Ein Generator ist forward-only, nicht wahlfrei zugreifbar und kein Ersatz für ein Array, wenn mehrere Durchläufe, count() oder direkte array_*-Funktionen gebraucht werden. Wer diese Grenzen kennt und Generators gezielt für Streaming, große Datenmengen und Pipelines einsetzt, statt sie überall unreflektiert zu verwenden, gewinnt spürbar an Speichereffizienz, ohne an Lesbarkeit zu verlieren.

Generators und yield in PHP, das Wichtigste auf einen Blick

Lazy Evaluation

yield macht eine Funktion zum Generator, der Werte einzeln liefert, statt vorab alles im Speicher zu berechnen.

yield from

Delegiert an andere Generators oder Iterables, ideal zum Verketten und zum Abflachen verschachtelter Strukturen.

send() und getReturn()

Werte in einen laufenden Generator hineinschicken und nach Abschluss einen finalen Rückgabewert abfragen.

Grenzen kennen

Forward-only, kein wahlfreier Zugriff. Für mehrfache Durchläufe oder array_*-Funktionen bleibt ein Array die richtige Wahl.

11. FAQ: Generators und yield in PHP

1Ist ein Generator ein Ersatz für ein Array?
Teilweise. Für einmaliges, sequenzielles Durchlaufen ja. Bei count(), mehreren Durchläufen oder direkten array_*-Funktionen bleibt ein Array richtig.
2Kann ich einen Generator zweimal durchlaufen?
Nein, ein Generator ist forward-only. Ein zweiter Durchlauf liefert nichts mehr, ein zweiter rewind()-Aufruf nach Iterationsbeginn wirft eine Exception.
3Was kostet yield an Performance?
Ein kleiner Suspend/Resume-Overhead pro Schritt, im niedrigen Mikrosekundenbereich. Bei großen Datenmengen überwiegt der eingesparte Speicher deutlich.
4Wie beende ich einen Generator vorzeitig?
Mit break in der foreach-Schleife. Ein try/finally-Block in der Generator-Funktion läuft trotzdem zuverlässig und schließt offene Ressourcen.
5Unterschied zwischen yield und return?
return beendet die Funktion mit einem Wert. yield pausiert sie und liefert einen von potenziell vielen Werten, ohne die Funktion zu beenden.
6Kann ein Generator einen Rückgabewert haben?
Ja, über return $wert; am Ende der Funktion. Der Wert erscheint nicht in der Iteration, sondern über Generator::getReturn() nach Abschluss.
7rewind() nach Iterationsbeginn?
Wirft eine Exception. rewind() funktioniert nur vor dem ersten gelieferten Wert, ein Generator lässt sich danach nicht zurücksetzen.
8Wofür ist yield from gut?
Zum Delegieren an einen anderen Generator, ein Array oder Traversable, inklusive Schlüsseln. Ideal zum Verketten und Abflachen verschachtelter Strukturen.
9Kann ich mit send() Werte hineinschicken?
Ja. send($wert) resumiert den Generator und liefert den Wert als Ergebnis des aktuellen yield-Ausdrucks, wie eine leichte Koroutine.
10Wann trotzdem ein Array verwenden?
Bei kleinen Datenmengen, mehreren Durchläufen, wenn count() vorab gebraucht wird, oder array_map(), usort() und Indexzugriff direkt nötig sind.

Mironsoft

PHP-Entwicklung, Performance-Optimierung und Legacy-Refactoring

Datenverarbeitung in eurem PHP-Projekt speicherschonend umbauen?

Wir analysieren bestehende Importe, Exporte und Batch-Jobs und ersetzen speicherhungrige Array-Verarbeitung durch Generators und yield, dort wo es wirklich etwas bringt, mit klaren Grenzen und messbaren Ergebnissen.

Code-Review

Analyse bestehender Verarbeitungslogik auf unnötigen Speicherverbrauch und fehlende Lazy Evaluation

Performance-Audit

Speicher- und Laufzeitmessung an echten Datenmengen, vor und nach dem Umbau auf Generators

Refactoring

Umbau von Import-, Export- und Batch-Jobs auf yield, yield from und sichere Ressourcen-Freigabe