← Назад к вопросам

Что такое генераторы в PHP и как работает оператор yield?

2.0 Middle🔥 111 комментариев
#PHP Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Генераторы в PHP и Оператор yield

Генераторы — это специальные функции в PHP, которые позволяют создавать итеративные последовательности данных без необходимости загружать все данные в памяти сразу. Оператор yield превращает обычную функцию в генератор, возвращая значения по одному.

Основная идея

// ❌ Без генератора — загружает всё в память
function getNumbers($count) {
    $numbers = [];
    for ($i = 0; $i < $count; $i++) {
        $numbers[] = $i;
    }
    return $numbers; // Большой массив в памяти!
}

$nums = getNumbers(1000000); // Создаёт массив из 1 млн элементов
foreach ($nums as $num) {
    echo $num;
}

// ✅ С генератором — ленивое вычисление
function getNumbers($count) {
    for ($i = 0; $i < $count; $i++) {
        yield $i; // Возвращает по одному значению
    }
}

// Память минимальна, значения генерируются по мере итерации
foreach (getNumbers(1000000) as $num) {
    echo $num;
}

Как работает yield

function generateSequence() {
    yield 1; // Первая итерация вернёт 1
    yield 2; // Вторая итерация вернёт 2
    yield 3; // Третья итерация вернёт 3
}

foreach (generateSequence() as $value) {
    echo $value; // Выведет: 1 2 3
}

// Эквивалент (без генератора):
// return [1, 2, 3];

yield с ключами

// ✅ Можно указывать ключи
function getUsersFromDB() {
    yield 1 => ["name" => "Alice", "email" => "alice@example.com"];
    yield 2 => ["name" => "Bob", "email" => "bob@example.com"];
    yield 3 => ["name" => "Charlie", "email" => "charlie@example.com"];
}

foreach (getUsersFromDB() as $id => $user) {
    echo "$id: {$user["name"]}\n";
}

// Более практично — из БД
function fetchUsersFromDB(PDO $pdo) {
    $stmt = $pdo->query("SELECT * FROM users");
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
        yield $row["id"] => $row;
    }
}

foreach (fetchUsersFromDB($pdo) as $id => $user) {
    // Обрабатываем пользователя (памяти занимает только одна строка!)
    processUser($user);
}

yield from для делегирования

function generator1() {
    yield 1;
    yield 2;
}

function generator2() {
    yield 3;
    yield 4;
}

// ❌ Долгий путь
function combined1() {
    foreach (generator1() as $value) {
        yield $value;
    }
    foreach (generator2() as $value) {
        yield $value;
    }
}

// ✅ Короче с yield from
function combined2() {
    yield from generator1();
    yield from generator2();
}

foreach (combined2() as $num) {
    echo $num; // 1 2 3 4
}

Двусторонняя коммуникация с send()

function echoGenerator() {
    $received = yield "Hello";
    echo "Received: $received\n";
    
    $received = yield "World";
    echo "Received: $received\n";
}

$gen = echoGenerator();

echo $gen->current(); // Hello
$gen->send("foo");    // Печатает: Received: foo
echo $gen->current(); // World
$gen->send("bar");    // Печатает: Received: bar

Практические примеры

Чтение больших файлов:

// ✅ Потоковое чтение файла
function readLargeFile($filepath) {
    $handle = fopen($filepath, "r");
    
    while (!feof($handle)) {
        yield fgets($handle);
    }
    
    fclose($handle);
}

foreach (readLargeFile("/tmp/huge.csv") as $line) {
    processLine($line);
    // Всегда в памяти только одна строка!
}

Пагинация на лету:

function paginate($items, $pageSize = 10) {
    $offset = 0;
    
    while ($offset < count($items)) {
        yield array_slice($items, $offset, $pageSize);
        $offset += $pageSize;
    }
}

foreach (paginate($bigList, 50) as $page) {
    echo "Processing page\n";
    foreach ($page as $item) {
        // Обрабатываем по 50 элементов за раз
    }
}

Фильтрация данных:

function filterNumbers($numbers, $minValue) {
    foreach ($numbers as $num) {
        if ($num >= $minValue) {
            yield $num;
        }
    }
}

$allNumbers = range(1, 1000000);
foreach (filterNumbers($allNumbers, 500000) as $num) {
    echo $num;
}

Преимущества генераторов

  • Экономия памяти — не загружает всё в массив
  • Ленивые вычисления — вычисляет только нужное
  • Потоковая обработка — идеально для больших данных
  • Чистота кода — красивая итерация
  • Производительность — быстрее, чем большие массивы

Когда использовать

  • Чтение больших файлов
  • Работа с большими наборами данных из БД
  • API, возвращающие потоки данных
  • Финансовые калькуляции с большим числом операций
  • Обработка логов в реальном времени
Что такое генераторы в PHP и как работает оператор yield? | PrepBro