Ответ
Итератор — это объект, который позволяет последовательно обходить элементы коллекции (например, массива, объекта, результата запроса) без необходимости знать её внутреннее устройство. В PHP итераторы реализуют встроенный интерфейс Iterator или IteratorAggregate.
Основные методы интерфейса Iterator:
current(): mixed— возвращает текущий элемент.key(): mixed— возвращает ключ текущего элемента.next(): void— перемещает указатель вперёд к следующему элементу.rewind(): void— сбрасывает указатель на первый элемент.valid(): bool— проверяет, существует ли текущая позиция.
Практический пример — обход большого файла без загрузки в память:
class LargeFileIterator implements Iterator {
private $fileHandle;
private $currentLine;
private $lineNumber = 0;
public function __construct(string $filePath) {
if (!file_exists($filePath)) {
throw new RuntimeException("File not found: $filePath");
}
$this->fileHandle = fopen($filePath, 'r');
if (!$this->fileHandle) {
throw new RuntimeException("Could not open file: $filePath");
}
}
public function rewind(): void {
rewind($this->fileHandle);
$this->currentLine = fgets($this->fileHandle);
$this->lineNumber = 0;
}
public function current(): string {
return $this->currentLine;
}
public function key(): int {
return $this->lineNumber;
}
public function next(): void {
$this->currentLine = fgets($this->fileHandle);
$this->lineNumber++;
}
public function valid(): bool {
return $this->currentLine !== false;
}
public function __destruct() {
if ($this->fileHandle) {
fclose($this->fileHandle);
}
}
}
// Использование
$logIterator = new LargeFileIterator('/var/log/app.log');
foreach ($logIterator as $lineNumber => $line) {
if (str_contains($line, 'ERROR')) {
echo "[$lineNumber] $line";
}
}
// Файл читается построчно, не загружаясь целиком в память.
Интерфейс IteratorAggregate упрощает создание итераторов, требуя реализации только одного метода getIterator(), который должен возвращать объект Iterator. Все встроенные классы, реализующие Traversable (как ArrayObject), могут использоваться в цикле foreach.
Ответ 18+ 🔞
Давай разберём эту штуку, а то звучит умно, а на деле — проще пареной репы. Итератор, блядь... Ну представь, что у тесть есть мешок картошки, и ты должен каждую пощупать. Можно, конечно, вывалить всё на пол — но тогда овердохуища грязи будет, да и память жалко. А можно сунуть руку в мешок и таскать по одной, пока не нащупаешь гнилую. Вот итератор — это такая рука, которая лезет в коллекцию и тащит элементы по одному, не загружая всё сразу.
Короче, методы, которые надо реализовать, если ты вдруг решил стать итератором:
current(): mixed— что у тебя сейчас в руке? Картошка? Возвращай её.key(): mixed— а под каким номером ты её вытащил? Первая? Вторая?next(): void— выкинул эту картошку, полез за следующей.rewind(): void— оп, всё по новой. Снова лезем с начала мешка.valid(): bool— а там вообще ещё что-то есть? Или мешок пустой?
А вот реальный пример, где это, блядь, спасает жизнь. Допустим, у тесть лог-файл на 500 гигов. Попробуй его в память загнать — комп просто накроется медным тазом, и будет тебе хиросима. А с итератором — читаем построчно, как будто потягиваем пиво, а не выпиваем разом весь ящик.
class LargeFileIterator implements Iterator {
private $fileHandle;
private $currentLine;
private $lineNumber = 0;
public function __construct(string $filePath) {
if (!file_exists($filePath)) {
throw new RuntimeException("File not found: $filePath");
}
$this->fileHandle = fopen($filePath, 'r');
if (!$this->fileHandle) {
throw new RuntimeException("Could not open file: $filePath");
}
}
public function rewind(): void {
rewind($this->fileHandle);
$this->currentLine = fgets($this->fileHandle);
$this->lineNumber = 0;
}
public function current(): string {
return $this->currentLine;
}
public function key(): int {
return $this->lineNumber;
}
public function next(): void {
$this->currentLine = fgets($this->fileHandle);
$this->lineNumber++;
}
public function valid(): bool {
return $this->currentLine !== false;
}
public function __destruct() {
if ($this->fileHandle) {
fclose($this->fileHandle);
}
}
}
// Использование
$logIterator = new LargeFileIterator('/var/log/app.log');
foreach ($logIterator as $lineNumber => $line) {
if (str_contains($line, 'ERROR')) {
echo "[$lineNumber] $line";
}
}
// Файл читается построчно, не загружаясь целиком в память.
А ещё есть IteratorAggregate — это для ленивых, ёпта. Не хочешь городить все пять методов? Реализуй только один getIterator(), который вернёт тесть готовый итератор, и всё, ты красавчик. Все встроенные штуки вроде ArrayObject уже умеют в foreach, потому что они внутри — хитрая жопа, которая умеет возвращать того самого невидимого работягу, который и таскает за тебя данные.