SPL Iterators In Depth
debt(d7/e3/b3/t5)
Closest to 'only careful code review or runtime testing' (d7), because file() vs SplFileObject usage isn't flagged by phpstan by default; blackfire profiling would reveal memory issues only at runtime under load.
Closest to 'simple parameterised fix' (e3), since quick_fix is swapping file() for SplFileObject — a small localized refactor of the read loop, slightly more than one-line because the iteration pattern changes.
Closest to 'localised tax' (b3), as SPL iterator usage is typically contained to specific file/directory handling components rather than spreading system-wide.
Closest to 'notable trap' (t5), matching the misconception that file() is fine for large files, plus documented gotchas like needing RecursiveIteratorIterator wrapper and rewinding SplFileObject — gotchas most devs learn after hitting them.
Also Known As
TL;DR
Explanation
SPL iterators implement the Iterator interface for lazy traversal — only loading data when requested. Key iterators: SplFileObject (iterate file lines without loading all into memory), DirectoryIterator / RecursiveDirectoryIterator (filesystem traversal), RecursiveIteratorIterator (flatten recursive iterators), FilterIterator (filter elements lazily), LimitIterator (slice an iterator), CachingIterator (look-ahead). They all work with foreach and can be chained. Critical for processing large files (CSV imports, log files) where file_get_contents() would exhaust memory.
Common Misconception
Why It Matters
Common Mistakes
- file() or file_get_contents() for large files — use SplFileObject instead.
- Not using RecursiveIteratorIterator to flatten RecursiveDirectoryIterator — it returns recursive structure.
- Creating custom iterators without implementing valid() — foreach relies on valid() to stop.
- Not rewinding an SplFileObject before re-iterating — it tracks position persistently.
Code Examples
// Loads entire 2GB file into memory:
$lines = file('/var/log/huge.log'); // Fatal: memory exhausted
foreach ($lines as $line) {
processLine($line);
}
// SplFileObject — O(1) memory regardless of file size:
$file = new SplFileObject('/var/log/huge.log');
$file->setFlags(SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY);
foreach ($file as $line) {
processLine($line); // Only current line in memory
}
// FilterIterator — lazy filter without loading all:
class ErrorLineIterator extends FilterIterator {
public function accept(): bool {
return str_contains($this->current(), 'ERROR');
}
}
$errors = new ErrorLineIterator(new SplFileObject('/var/log/app.log'));
foreach ($errors as $errorLine) { /* only ERROR lines */ }