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

Как реализовать собственный итератор в PHP?

2.4 Senior🔥 141 комментариев
#PHP Core

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

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

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

Реализация собственного итератора в PHP

Создание кастомного итератора в PHP позволяет работать с любыми структурами данных как с итерируемыми объектами. Это особенно полезно для обхода сложных данных, ленивой загрузки или обработки больших наборов информации.

Основные интерфейсы для реализации

PHP предоставляет два основных подхода:

1. Интерфейс Iterator

Базовый интерфейс, требующий реализации 5 методов:

interface Iterator extends Traversable {
    public function current(): mixed;
    public function key(): mixed;
    public function next(): void;
    public function rewind(): void;
    public function valid(): bool;
}

2. Интерфейс IteratorAggregate

Более простой вариант, где достаточно реализовать метод getIterator():

interface IteratorAggregate extends Traversable {
    public function getIterator(): Traversable;
}

Полная реализация через Iterator

Рассмотрим пример итератора для обхода коллекции пользователей:

<?php

class UserCollectionIterator implements Iterator
{
    private array $users;
    private int $position = 0;
    
    public function __construct(array $users)
    {
        $this->users = $users;
    }
    
    /**
     * Возвращает текущий элемент
     */
    public function current(): mixed
    {
        return $this->users[$this->position];
    }
    
    /**
     * Возвращает ключ текущего элемента
     */
    public function key(): mixed
    {
        return $this->position;
    }
    
    /**
     * Перемещает указатель к следующему элементу
     */
    public function next(): void
    {
        $this->position++;
    }
    
    /**
     * Сбрасывает указатель в начало
     */
    public function rewind(): void
    {
        $this->position = 0;
    }
    
    /**
     * Проверяет, существует ли текущий элемент
     */
    public function valid(): bool
    {
        return isset($this->users[$this->position]);
    }
}

Реализация через IteratorAggregate

Более элегантный способ для коллекций:

<?php

class UserCollection implements IteratorAggregate
{
    private array $users = [];
    
    public function addUser(User $user): void
    {
        $this->users[] = $user;
    }
    
    public function getIterator(): Traversable
    {
        return new ArrayIterator($this->users);
        // или возвращаем кастомный итератор:
        // return new UserCollectionIterator($this->users);
    }
}

// Использование
$collection = new UserCollection();
$collection->addUser(new User('Анна'));
$collection->addUser(new User('Петр'));

foreach ($collection as $user) {
    echo $user->getName() . "\n";
}

Генераторы как альтернатива

С PHP 5.5 появились генераторы, которые упрощают создание итераторов:

<?php

class LargeDatasetProcessor
{
    public function fetchAll(): Generator
    {
        $chunkSize = 100;
        $offset = 0;
        
        while ($data = $this->fetchFromDatabase($offset, $chunkSize)) {
            foreach ($data as $item) {
                yield $item;
            }
            $offset += $chunkSize;
        }
    }
    
    private function fetchFromDatabase(int $offset, int $limit): array
    {
        // Запрос к БД с LIMIT
        return [/* данные */];
    }
}

Практический пример: итератор для файла

Создадим итератор для чтения большого файла построчно:

<?php

class FileLineIterator implements Iterator
{
    private $fileHandle;
    private $currentLine;
    private $lineNumber = 0;
    
    public function __construct(string $filePath)
    {
        if (!file_exists($filePath)) {
            throw new RuntimeException("Файл не найден: {$filePath}");
        }
        
        $this->fileHandle = fopen($filePath, 'r');
        if ($this->fileHandle === false) {
            throw new RuntimeException("Не удалось открыть файл: {$filePath}");
        }
    }
    
    public function current(): mixed
    {
        return $this->currentLine;
    }
    
    public function key(): mixed
    {
        return $this->lineNumber;
    }
    
    public function next(): void
    {
        $this->currentLine = fgets($this->fileHandle);
        $this->lineNumber++;
    }
    
    public function rewind(): void
    {
        fseek($this->fileHandle, 0);
        $this->lineNumber = 0;
        $this->currentLine = fgets($this->fileHandle);
    }
    
    public function valid(): bool
    {
        return $this->currentLine !== false;
    }
    
    public function __destruct()
    {
        if ($this->fileHandle) {
            fclose($this->fileHandle);
        }
    }
}

Ключевые преимущества собственных итераторов

  • Инкапсуляция логики обхода – скрытие сложной внутренней структуры
  • Ленивая загрузка – данные загружаются только при необходимости
  • Экономия памяти – обработка больших данных без загрузки в память
  • Единый интерфейс – унификация работы с разными типами коллекций
  • Поддержка PHP функций – работа с foreach, iterator_to_array()

Рекомендации по реализации

  1. Всегда документируйте контракт итератора
  2. Обрабатывайте граничные случаи (пустая коллекция, конец данных)
  3. Используйте генераторы для простых сценариев
  4. Для неизменяемых коллекций реализуйте интерфейс SeekableIterator
  5. Рассмотрите использование ArrayIterator или FilterIterator из SPL

Собственные итераторы делают код более декларативным и позволяют эффективно работать с данными любой сложности, соблюдая принципы ООП и обеспечивая чистоту архитектуры приложения.