Генераторы в PHP — это мощный инструмент для создания итераторов без лишних сложностей. Вместо того чтобы создавать в памяти огромный массив данных и возвращать его, генератор позволяет "отдавать" значения по одному, как только они понадобятся. Это достигается с помощью ключевого слова `yield`.
Ключевое отличие от обычных функций в том, что `yield` приостанавливает выполнение генератора, запоминает его состояние и возвращает значение. При следующем обращении к генератору выполнение возобновляется с того же места. Такой подход невероятно эффективен по памяти, особенно при работе с большими файлами, базами данных или бесконечными последовательностями.
В этом тренажере вы научитесь создавать простые и сложные генераторы, использовать `yield` для возврата значений и ключей, а также поймете ключевые отличия генераторов от традиционных функций, возвращающих массивы.
- Модуль 1: Основы синтаксиса PHP
- Модуль 2: Переменные и типы данных
- Модуль 3: Операторы
- Модуль 4: Условные конструкции
- Модуль 5: Циклы
- Модуль 6: Массивы
- Индексированные массивы.
- Ассоциативные массивы.
- Многомерные массивы.
- Добавление элементов в массив.
- Удаление элементов из массива.
- Функция count().
- Функция array_push() и array_pop().
- Функция array_shift() и array_unshift().
- Функция array_merge().
- Функция array_slice().
- Функция array_splice().
- Функция in_array().
- Функция array_search().
- Функция array_keys() и array_values().
- Функция array_unique().
- Функция array_reverse().
- Сортировка: sort(), rsort().
- Сортировка: asort(), arsort().
- Сортировка: ksort(), krsort().
- Функция array_map().
- Функция array_filter().
- Функция array_reduce().
- Функция array_walk().
- Функция array_column().
- Деструктуризация массивов.
- Модуль 7: Функции
- Объявление функций.
- Вызов функций.
- Параметры функций.
- Возврат значений return.
- Значения параметров по умолчанию.
- Передача по значению.
- Передача по ссылке.
- Переменное количество аргументов.
- Оператор распаковки ....
- Типизация параметров.
- Типизация возвращаемых значений.
- Nullable типы.
- Union типы (PHP 8).
- Именованные аргументы (PHP 8).
- Анонимные функции.
- Стрелочные функции.
- Замыкания и use.
- Рекурсивные функции.
- Глобальные переменные в функциях.
- Статические переменные в функциях.
- Модуль 8: Работа со строками
- Одинарные и двойные кавычки.
- Экранирование символов.
- Heredoc и Nowdoc синтаксис.
- Функция strlen().
- Функция substr().
- Функция str_replace().
- Функция strpos() и strrpos().
- Функция explode() и implode().
- Функция trim(), ltrim(), rtrim().
- Функция strtolower() и strtoupper().
- Функция ucfirst() и ucwords().
- Функция str_repeat().
- Модуль 9: Суперглобальные переменные
- Модуль 10: Работа с формами
- Модуль 11: Работа с файлами
- Модуль 12: Дата и время
- Модуль 13: Регулярные выражения
- Модуль 14: Сессии и Cookie
- Модуль 15: Include и Require
- Модуль 16: Объектно-ориентированное программирование
- Модуль 17: Пространства имен
- Модуль 18: Обработка ошибок и исключений
- Модуль 19: Работа с JSON и XML
- Модуль 20: Работа с базами данных MySQLi
- Модуль 21: PDO - PHP Data Objects
- Модуль 22: Composer и зависимости
- Модуль 23: cURL и HTTP запросы
- Модуль 24: REST API
- Модуль 25: Безопасность
- Модуль 26: Генераторы и итераторы
- Yield ключевое слово.
- Модуль 27: Reflection API
- Модуль 28: Работа с изображениями
- Модуль 29: Отправка email
- Модуль 30: Паттерны проектирования
- Модуль 31: Тестирование
- Модуль 32: Продвинутые возможности PHP 8+
Создание простого генератора
Превратите эту функцию в генератор. Вместо того чтобы собирать все числа в массив, функция должна "отдавать" каждое число по одному прямо в цикле. Для этого нужно использовать специальное ключевое слово.
function numberRange(int $start, int $end) {
for ($i = $start; $i <= $end; $i++) {
input1S $i;
}
}
foreach (numberRange(1, 3) as $number) {
echo "$number ";
}Первое значение генератора
Генератор выполняется "лениво", то есть код внутри него не работает, пока не запрошено первое значение. Что выведет этот скрипт, который запрашивает только один элемент?
function getLetters() {
echo "Start ";
yield 'A';
echo "Middle ";
yield 'B';
echo "End ";
}
foreach (getLetters() as $letter) {
echo $letter;
break;
}Ошибка: `return` вместо `yield`
Эта функция должна генерировать числа от 1 до 3, но вместо этого она сразу завершает работу после первого числа. Найдите строку с логической ошибкой и исправьте ее, чтобы функция стала полноценным генератором.
function count_to_three() { for ($i = 1; $i <= 3; $i++) { return $i; }} foreach (count_to_three() as $num) { echo $num;}Сборка файлового ридера
Соберите из фрагментов кода функцию-генератор, которая построчно читает файл. Такой подход очень эффективен, так как не загружает весь файл в память. Лишние фрагменты использовать не нужно.
function readFileGenerator($filename) {$file = fopen($filename, 'r');while (($line = fgets($file)) !== false) {yield $line;}fclose($file);return $file;$lines[] = $line;Что в итоге?
Проанализируйте этот генератор и цикл. Какая строка будет выведена в результате работы кода? Введите итоговую строку без кавычек.
function getAcronym() {
yield 'P';
yield 'H';
yield 'P';
}
$parts = [];
foreach (getAcronym() as $char) {
$parts[] = $char;
}
$result = implode('-', $parts);
echo $result;Генерация пар ключ-значение
Генераторы могут возвращать не только значения, но и ключи, как в ассоциативных массивах. Дополните код, чтобы он генерировал пары "ключ => значение" для элементов исходного массива.
function arrayWithKeys(array $data) {
foreach ($data as $key => $value) {
yield input1S => input2S;
}
}
$items = ['a' => 'Apple', 'b' => 'Banana'];
foreach (arrayWithKeys($items) as $key => $value) {
echo "$key: $value; ";
}Повторная итерация генератора
Генератор — это итератор одноразового использования. Его нельзя "перемотать" назад. Что выведет этот код, который пытается пройти по одному и тому же экземпляру генератора дважды?
function getNumbers() {
yield 1;
yield 2;
}
$generator = getNumbers();
foreach ($generator as $number) {
echo $number;
}
echo '-';
// Попытка повторной итерации
foreach ($generator as $number) {
echo $number;
}Делегирование с `yield from`
Вместо того чтобы вручную перебирать один генератор внутри другого, можно "делегировать" ему работу. Вставьте правильную конструкцию из банка, чтобы генератор `mainGenerator` сначала вернул свои значения, а затем все значения из `subGenerator`.
function subGenerator() {
yield 3;
yield 4;
}
function mainGenerator() {
yield 1;
yield 2;
input1S subGenerator();
yield 5;
}
foreach (mainGenerator() as $num) {
echo "$num ";
}Генератор против Массива
Сопоставьте утверждения с подходом, который они описывают. Какой из них эффективен по памяти, а какой возвращает все данные сразу?