Iterator apply: примеры (PHP)
iterator_apply(Traversable $iterator, callable $function, ?array $args = null): intОсновы функции iterator_apply
Функция iterator_apply применяет пользовательскую callback-функцию ко всем элементам итератора. Эта функция полезна для обработки больших наборов данных, которые представлены в виде итераторов, без загрузки всех элементов в память одновременно.
Функция используется при работе с итераторами, особенно когда необходимо обработать каждый элемент итератора с помощью пользовательской функции. Типичные сценарии включают обработку файлов построчно, работу с большими массивами данных, обработку результатов запросов к базам данных и манипуляции с объектами, реализующими интерфейс Iterator.
iterator_apply(Traversable $iterator, callable $callback, ?array $args = null): int$iterator - объект, реализующий интерфейс Traversable (обычно Iterator или IteratorAggregate).
$callback - callback-функция, которая вызывается для каждого элемента. Функция принимает два параметра: текущий элемент итератора (первый параметр) и вторым параметром - переданные аргументы из $args.
$args - массив аргументов, которые будут переданы в callback-функцию в качестве второго параметра. Необязательный параметр, по умолчанию null.
Возвращает количество примененных итераций (количество вызовов callback-функции).
Базовые примеры использования
function printItem($item, $args) {
echo $item . $args[0];
return true;
}
$array = ['apple', 'banana', 'cherry'];
$iterator = new ArrayIterator($array);
$count = iterator_apply($iterator, 'printItem', [', ']);
echo "\nОбработано элементов: $count";apple, banana, cherry,
Обработано элементов: 3function processWithLimit($item, $args) {
static $counter = 0;
$counter++;
echo "Элемент $counter: $item\n";
// Останавливаем обработку после 2 элементов
if ($counter >= 2) {
return false;
}
return true;
}
$iterator = new ArrayIterator(['A', 'B', 'C', 'D']);
$result = iterator_apply($iterator, 'processWithLimit');
echo "Обработано: $result элементов";Элемент 1: A
Элемент 2: B
Обработано: 2 элементовfunction multiplyItem($item, $args) {
$result = $item * $args['multiplier'];
echo "$item × {$args['multiplier']} = $result\n";
return true;
}
$numbers = new ArrayIterator([1, 2, 3, 4, 5]);
$args = ['multiplier' => 3];
iterator_apply($numbers, 'multiplyItem', [$args]);1 × 3 = 3
2 × 3 = 6
3 × 3 = 9
4 × 3 = 12
5 × 3 = 15Альтернативы в PHP
Функции для обработки массивов. array_walk применяет функцию к каждому элементу массива. Работает только с массивами, а не с итераторами.
Наиболее распространенная альтернатива. Проще в использовании, но не возвращает количество обработанных элементов автоматически. Предпочтительнее для простых операций без необходимости подсчета применений.
Создает новый массив, применяя callback-функцию к каждому элементу. Возвращает новый массив, а не модифицирует существующий.
iterator_apply выбирают для работы именно с итераторами, особенно при обработке больших данных. Для обычных массивов удобнее использовать foreach или array_walk.
Аналоги в других языках
# Аналог через map()
def process_item(x):
return x * 2
items = [1, 2, 3]
result = list(map(process_item, items))
print(result) # [2, 4, 6]
# Через цикл
for item in items:
print(item * 2)[2, 4, 6]
2
4
6const items = [1, 2, 3];
let count = 0;
items.forEach((item, index) => {
console.log(item * 2);
count++;
});
console.log(`Обработано: ${count} элементов`);2
4
6
Обработано: 3 элементовВ хранимых процедурах MySQL используются курсоры для построчной обработки результатов запросов, что аналогично итераторам в PHP.
В отличие от PHP функции iterator_apply, аналоги в других языках обычно работают с коллекциями и не требуют отдельного итераторного объекта. PHP подход более низкоуровневый и гибкий.
Типичные ошибки
$not_iterator = 'some string';
iterator_apply($not_iterator, 'some_function');TypeError: iterator_apply(): Argument #1 ($iterator) must be of type Traversable, string given$iterator = new ArrayIterator([1, 2, 3]);
iterator_apply($iterator, 'non_existent_function');Warning: iterator_apply() expects parameter 2 to be a valid callback, function 'non_existent_function' not found or invalid function namefunction modifyIterator($item, $args) {
echo $item;
// Попытка изменения итератора может привести к неожиданному поведению
$args['iterator']->next();
$args['iterator']->next();
return true;
}
$iterator = new ArrayIterator([1, 2, 3, 4, 5]);
iterator_apply($iterator, 'modifyIterator', [['iterator' => $iterator]]);1 3 5 или неопределенное поведениеfunction wrongCallback($item) { // Нет второго параметра
echo $item;
return true;
}
$iterator = new ArrayIterator([1, 2, 3]);
iterator_apply($iterator, 'wrongCallback', ['extra_arg']);Предупреждение и неожиданное поведениеИзменения в версиях PHP
Добавлена строгая типизация параметров. Параметр $iterator теперь имеет тип Traversable, что вызывает TypeError при передаче неверного типа. Ранее в PHP 7 принимался любой тип с генерацией предупреждения.
Улучшены сообщения об ошибках. Callback-параметр теперь проверяется более строго.
Изменений в самой функции не было, но улучшена работа с итераторами в целом.
Нет значительных изменений для iterator_apply.
Расширенные примеры
class LargeFileIterator implements Iterator {
private $fileHandle;
private $currentLine;
private $lineNumber = 0;
public function __construct($filename) {
$this->fileHandle = fopen($filename, 'r');
$this->next();
}
public function current() { return $this->currentLine; }
public function key() { return $this->lineNumber; }
public function next() {
$this->currentLine = fgets($this->fileHandle);
$this->lineNumber++;
}
public function rewind() {
fseek($this->fileHandle, 0);
$this->lineNumber = 0;
$this->next();
}
public function valid() { return $this->currentLine !== false; }
}
function processFileLine($line, $args) {
if (trim($line) !== '') {
$args['lines'][] = 'Строка ' . $args['counter'] . ': ' . trim($line);
$args['counter']++;
}
return true;
}
// Использование
$iterator = new LargeFileIterator('large_file.txt');
$context = ['lines' => [], 'counter' => 1];
$processed = iterator_apply($iterator, 'processFileLine', [$context]);
print_r($context['lines']);
echo "Обработано строк: $processed";function filterCallback($item, $args) {
if ($item % 2 === 0) {
$args['result'][] = $item;
}
return true;
}
$numbers = new ArrayIterator(range(1, 100));
$filtered = ['result' => []];
iterator_apply($numbers, 'filterCallback', [$filtered]);
echo "Четные числа: " . implode(', ', array_slice($filtered['result'], 0, 10)) . '...';
echo "\nВсего четных: " . count($filtered['result']);Четные числа: 2, 4, 6, 8, 10, 12, 14, 16, 18, 20...
Всего четных: 50function batchProcessor($item, $args) {
static $batch = [];
$batch[] = $item;
$args['processed']++;
if (count($batch) >= $args['batch_size']) {
echo "Обрабатываю пакет из " . count($batch) . " элементов\n";
// Здесь может быть запись в БД или другая операция
$batch = [];
}
return true;
}
$data = new ArrayIterator(range(1, 25));
$stats = ['processed' => 0, 'batch_size' => 5];
$count = iterator_apply($data, 'batchProcessor', [$stats]);
echo "\nВсего обработано: {$stats['processed']} элементов";
echo "\nВызовов callback: $count";Обрабатываю пакет из 5 элементов
Обрабатываю пакет из 5 элементов
Обрабатываю пакет из 5 элементов
Обрабатываю пакет из 5 элементов
Обрабатываю пакет из 5 элементов
Всего обработано: 25 элементов
Вызовов callback: 25function processFile($fileInfo, $args) {
if ($fileInfo->isFile() && $fileInfo->getExtension() === 'txt') {
$size = $fileInfo->getSize();
echo "Файл: {$fileInfo->getFilename()}, Размер: {$size} байт\n";
$args['total_size'] += $size;
$args['file_count']++;
}
return true;
}
$dirIterator = new DirectoryIterator(__DIR__);
$stats = ['total_size' => 0, 'file_count' => 0];
$processed = iterator_apply($dirIterator, 'processFile', [$stats]);
echo "\nПроанализировано элементов: $processed";
echo "\nНайдено txt файлов: {$stats['file_count']}";
echo "\nОбщий размер: {$stats['total_size']} байт";