Ответ
Генераторы в PHP — это специальные функции, которые позволяют итерироваться по набору данных, не создавая весь набор в памяти сразу. Вместо ключевого слова return используется yield. При каждом вызове yield функция приостанавливает своё выполнение и возвращает текущее значение. При следующей итерации выполнение возобновляется с того же места.
Ключевая польза: Экономия оперативной памяти при работе с большими наборами данных (логи, большие файлы, объёмные результаты запросов из БД).
Сравним два подхода:
// ПЛОХО: Функция возвращает массив из миллиона чисел, загружая их все в память.
function getMillionNumbers(): array
{
$numbers = [];
for ($i = 0; $i < 1_000_000; $i++) {
$numbers[] = $i;
}
return $numbers; // Массив из 1М элементов создан в памяти
}
foreach (getMillionNumbers() as $number) {
// В начале цикла массив уже целиком в памяти.
if ($number > 100) break; // Но мы использовали только 101 элемент!
}
// Пиковое использование памяти: ~130 МБ
// ХОРОШО: Генератор возвращает значения по одному.
function generateMillionNumbers(): Generator
{
for ($i = 0; $i < 1_000_000; $i++) {
yield $i; // Возвращает $i и приостанавливается
}
}
foreach (generateMillionNumbers() as $number) {
// На каждой итерации в памяти только текущее значение $i.
if ($number > 100) break;
}
// Пиковое использование памяти: ~1 МБ
Практический пример: Чтение большого файла построчно.
function readLargeFile(string $filename): Generator
{
$handle = fopen($filename, 'r');
if (!$handle) {
throw new RuntimeException("Could not open file: {$filename}");
}
try {
while (($line = fgets($handle)) !== false) {
yield trim($line); // Отдаём строку по одной
}
} finally {
fclose($handle); // Гарантированное закрытие файла
}
}
// Использование
foreach (readLargeFile('huge_log.txt') as $lineNumber => $lineContent) {
// $lineNumber автоматически инкрементируется с каждым yield
echo "Line {$lineNumber}: {$lineContent}" . PHP_EOL;
// Файл никогда не загружается в память целиком.
}
Ключевые особенности генераторов в PHP:
- Функция-генератор возвращает объект внутреннего класса
Generator, который реализует интерфейсыIteratorиTraversable. yieldможет возвращать ключ-значение:yield $key => $value;.- Можно делегировать другой генератор с помощью
yield from(аналогawaitв некоторых других языках). - Начиная с PHP 7.0, генератор может возвращать финальное значение с помощью
return, которое можно получить через$generator->getReturn().
Генераторы — это мощный инструмент для написания эффективного по памяти кода, особенно в контексте обработки потоков данных или реализации ленивых вычислений.
Ответ 18+ 🔞
А, генераторы в PHP! Ну это вообще тема, которая многих вводит в ступор, а на самом деле всё проще пареной репы, если объяснить нормально. Смотри, представь себе такую ситуацию.
Вот ты пишешь функцию, которая должна вернуть, допустим, миллион чисел. Обычный мужик делает так — создаёт массив на миллион элементов, туда всё пихает и возвращает. Ёпта, а потом удивляется, почему скрипт сожрал овердохуища памяти, хотя ему реально нужно было только первые сто чисел проверить. Это как если бы ты пошёл в магазин за бутылкой пива, а купил целый грузовик, потому что "а вдруг понадобится". Хуй с горы, логика-то где?
А генератор — он умный. Он не создаёт всё сразу. Он как ленивый, но эффективный работяга: делает по чуть-чуть, отдаёт тебе результат и присаживается на бутылку покурить, ждёт следующей команды. Вместо return он использует yield. И каждый раз, когда в цикле ты просишь у него следующее значение, он просыпается, делает ещё один шаг и снова засыпает. В рот меня чих-пых, какая же это экономия!
Вот смотри на разницу, тут всё понятно станет.
// ПЛОХОЙ ПУТЬ: Делаем как все, по-старинке. Жрём память как не в себя.
function getMillionNumbers(): array
{
$numbers = [];
for ($i = 0; $i < 1_000_000; $i++) {
$numbers[] = $i;
}
return $numbers; // Всё, приехали. Весь этот здоровенный массив уже торчит в оперативке.
}
foreach (getMillionNumbers() as $number) {
// И вот тут мы, такие умные, смотрим только первые 101 элемент и break.
if ($number > 100) break;
}
// А память-то уже уплыла, 130 мегабайт как корова языком слизала. **Ядрёна вошь!**
// ХОРОШИЙ ПУТЬ: Включаем голову и делаем через генератор.
function generateMillionNumbers(): Generator
{
for ($i = 0; $i < 1_000_000; $i++) {
yield $i; // Отдал цифру, и приостановился. Никаких массивов.
}
}
foreach (generateMillionNumbers() as $number) {
// На каждой итерации в памяти только одна циферка $i. Красота!
if ($number > 100) break;
}
// Памяти съедено — кот наплакал, около мегабайта. Вот это я понимаю — технология!
Самый жирный кейс — это чтение здоровенных файлов, логов там всяких. Раньше люди или file() использовали (кошмар!), или голову ломали. А с генератором — да похуй на размер файла, хоть гигабайтный.
function readLargeFile(string $filename): Generator
{
$handle = fopen($filename, 'r');
if (!$handle) {
throw new RuntimeException("Could not open file: {$filename}");
}
try {
while (($line = fgets($handle)) !== false) {
yield trim($line); // Вот тебе строчка, держи. Следующую дам, когда попросишь.
}
} finally {
fclose($handle); // Это чтобы даже если что-то пойдёт не так, файл закрылся. Аккуратненько.
}
}
// Используем
foreach (readLargeFile('huge_log.txt') as $lineNumber => $lineContent) {
echo "Line {$lineNumber}: {$lineContent}" . PHP_EOL;
// И файл НИКОГДА не лежит в памяти целиком. Он по строчке подаётся, как суши на конвейере.
}
Что ещё важно знать про этих ебашки-воробушки?
- Функция-генератор возвращает не массив, а объект класса
Generator. Он умеет в итераторы, с ним можно вforeach. - Через
yieldможно отдавать не только значение, но и ключ:yield 'ключ_такой' => 'значение_эдакое'. - Есть такая классная штука
yield from— это типа делегирование. Можно один генератор из другого вызывать, чтобы не городить код. - С PHP 7.0 они ещё и финальное значение через
returnуметь стали, которое потомgetReturn()вытащит. Но это уже для особых ценителей.
Короче, генераторы — это пиздопроебибна штука, когда нужно работать с потоками данных, которые в память не влезают, или просто не хочется грузить её почём зря. Включил — и код сразу стал элегантнее и эффективнее. Сам от себя охуел, когда первый раз понял, как это работает.