PHP Advanced Iteration: The Magic Conveyor Belt đźŹ
Imagine a factory with a super-smart conveyor belt. Instead of loading ALL items at once (heavy!), it gives you ONE item at a time. That’s what PHP’s advanced iteration tools do—smart, memory-friendly, powerful!
🎯 The Big Picture
Think of a toy factory:
- Old way: Dump ALL toys in one giant box, then sort through → Heavy, slow, messy!
- New way: A conveyor belt hands you ONE toy at a time → Light, fast, clean!
PHP gives you tools to build these smart conveyor belts. Let’s explore each one!
1. Generators: The Lazy Factory Worker
What is it?
A Generator is like a worker who only makes ONE item when you ask for it. No wasted effort!
Simple Example
function countToThree() {
yield 1;
yield 2;
yield 3;
}
foreach (countToThree() as $num) {
echo $num; // Prints: 1, 2, 3
}
Why is this cool?
- Regular function: Makes ALL items, stores them in memory
- Generator: Makes ONE item, forgets it, makes the next
Real Life Example
// Reading a HUGE file?
// Don't load it all at once!
function readBigFile($file) {
$handle = fopen($file, 'r');
while ($line = fgets($handle)) {
yield $line;
}
fclose($handle);
}
// Uses almost NO memory!
foreach (readBigFile('huge.txt') as $line) {
echo $line;
}
2. The yield Keyword: The Pause Button
What is it?
yield is like pressing PAUSE on your work. You give out ONE result, then WAIT until someone asks for more.
Think of it like…
You’re reading a story to kids. After each page, you pause and wait for them to say “MORE!”
function storyPages() {
yield "Once upon a time...";
yield "The hero found a dragon!";
yield "They became friends.";
yield "The End.";
}
Key Difference from return
return |
yield |
|---|---|
| Stops everything | Pauses, waits |
| Gives ONE result | Gives MANY results |
| Function ends | Function continues |
Sending Values Back
function printer() {
while (true) {
$value = yield;
echo "Got: $value\n";
}
}
$gen = printer();
$gen->send("Hello"); // Got: Hello
$gen->send("World"); // Got: World
3. Iterator Interface: The Rule Book
What is it?
The Iterator Interface is like a rule book that says: “If you want to be loopable, you MUST have these 5 abilities!”
The 5 Magic Abilities
graph TD A["Iterator Interface"] --> B["rewind: Go to start"] A --> C["current: Get current item"] A --> D["key: Get current position"] A --> E["next: Move forward"] A --> F["valid: More items left?"]
Building Your Own Iterator
class NumberRange implements Iterator {
private $start;
private $end;
private $current;
public function __construct($start, $end) {
$this->start = $start;
$this->end = $end;
}
public function rewind(): void {
$this->current = $this->start;
}
public function current(): mixed {
return $this->current;
}
public function key(): mixed {
return $this->current - $this->start;
}
public function next(): void {
$this->current++;
}
public function valid(): bool {
return $this->current <= $this->end;
}
}
// Now use it!
foreach (new NumberRange(5, 10) as $num) {
echo $num; // 5, 6, 7, 8, 9, 10
}
4. IteratorAggregate: The Shortcut
What is it?
Too lazy to write 5 methods? IteratorAggregate says: “Just give me ONE method that returns an iterator, and I’ll handle the rest!”
The Simple Rule
Just implement getIterator() → Done!
class BookShelf implements IteratorAggregate {
private $books = [];
public function addBook($book) {
$this->books[] = $book;
}
// Just ONE method needed!
public function getIterator(): Traversable {
return new ArrayIterator($this->books);
}
}
$shelf = new BookShelf();
$shelf->addBook("Harry Potter");
$shelf->addBook("Lord of the Rings");
foreach ($shelf as $book) {
echo $book; // Works perfectly!
}
Iterator vs IteratorAggregate
| Iterator | IteratorAggregate |
|---|---|
| 5 methods to write | 1 method to write |
| Full control | Quick & easy |
| Custom logic | Delegates to another iterator |
5. ArrayIterator: The Ready-Made Helper
What is it?
ArrayIterator is like a pre-built conveyor belt. Give it an array, and it becomes loopable with extra powers!
Basic Usage
$fruits = ['apple', 'banana', 'cherry'];
$iterator = new ArrayIterator($fruits);
// Loop normally
foreach ($iterator as $fruit) {
echo $fruit;
}
// Or use methods
$iterator->rewind();
echo $iterator->current(); // apple
$iterator->next();
echo $iterator->current(); // banana
Extra Powers
$iterator = new ArrayIterator([3, 1, 2]);
// Sort it!
$iterator->asort();
// Count it!
echo $iterator->count(); // 3
// Seek to position!
$iterator->seek(1);
echo $iterator->current();
6. Countable Interface: The Counter
What is it?
Want your object to work with count()? Implement Countable!
The ONE Rule
Just implement count() method.
class Playlist implements Countable {
private $songs = [];
public function addSong($song) {
$this->songs[] = $song;
}
public function count(): int {
return count($this->songs);
}
}
$playlist = new Playlist();
$playlist->addSong("Song A");
$playlist->addSong("Song B");
echo count($playlist); // 2 - Works!
Combining with IteratorAggregate
class TodoList implements
IteratorAggregate,
Countable
{
private $tasks = [];
public function add($task) {
$this->tasks[] = $task;
}
public function getIterator(): Traversable {
return new ArrayIterator($this->tasks);
}
public function count(): int {
return count($this->tasks);
}
}
$todos = new TodoList();
$todos->add("Buy milk");
$todos->add("Walk dog");
echo count($todos); // 2
foreach ($todos as $task) {
echo $task;
}
7. ArrayAccess Interface: Act Like an Array!
What is it?
Want to use $object['key'] like an array? ArrayAccess gives your object array superpowers!
The 4 Methods
graph TD A["ArrayAccess"] --> B["offsetExists: Does key exist?"] A --> C["offsetGet: Get value by key"] A --> D["offsetSet: Set value by key"] A --> E["offsetUnset: Remove by key"]
Full Example
class Settings implements ArrayAccess {
private $data = [];
public function offsetExists($key): bool {
return isset($this->data[$key]);
}
public function offsetGet($key): mixed {
return $this->data[$key] ?? null;
}
public function offsetSet($key, $val): void {
$this->data[$key] = $val;
}
public function offsetUnset($key): void {
unset($this->data[$key]);
}
}
$settings = new Settings();
$settings['theme'] = 'dark'; // offsetSet
echo $settings['theme']; // offsetGet → dark
isset($settings['theme']); // offsetExists → true
unset($settings['theme']); // offsetUnset
🏆 The Ultimate Combo: All Powers Combined!
class SuperCollection implements
IteratorAggregate,
Countable,
ArrayAccess
{
private $items = [];
// ArrayAccess methods
public function offsetExists($k): bool {
return isset($this->items[$k]);
}
public function offsetGet($k): mixed {
return $this->items[$k];
}
public function offsetSet($k, $v): void {
$this->items[$k] = $v;
}
public function offsetUnset($k): void {
unset($this->items[$k]);
}
// Countable
public function count(): int {
return count($this->items);
}
// IteratorAggregate
public function getIterator(): Traversable {
return new ArrayIterator($this->items);
}
}
$c = new SuperCollection();
$c['a'] = 1; // Array syntax!
$c['b'] = 2;
echo count($c); // 2
foreach ($c as $v) { // Loopable!
echo $v;
}
🎯 Quick Summary
| Tool | Purpose | Key Point |
|---|---|---|
| Generator | Lazy data production | Saves memory |
| yield | Pause & return one value | Like a pause button |
| Iterator | Make anything loopable | 5 methods required |
| IteratorAggregate | Quick loopable shortcut | 1 method only |
| ArrayIterator | Ready-made iterator | Works with arrays |
| Countable | Enable count() |
1 method: count() |
| ArrayAccess | Array syntax for objects | 4 methods for [] |
🚀 When to Use What?
graph TD Q["Need to iterate?"] --> A{Memory concern?} A -->|Yes| G["Use Generator"] A -->|No| B{Custom logic?} B -->|Complex| I["Use Iterator"] B -->|Simple| IA["Use IteratorAggregate"] Q2["Need count?"] --> C["Implement Countable"] Q3["Need array syntax?"] --> AA["Implement ArrayAccess"]
Remember: These tools are like LEGO blocks. Mix and match to build exactly what you need!
