Introduction: Why foreach is More Than Just "For Each"
If you've been writing PHP for at least a week, you've used foreach a hundred times. It's perhaps every developer's favorite loop: short, elegant, no extra counters. But have you ever wondered what actually happens under the hood when you write foreach ($array as $value)?
The question "How does PHP 'foreach' actually work?" has garnered thousands of views on Stack Overflow. And that's no coincidence. The behavior of foreach hides several surprises, especially when it comes to references, nested loops, and non-standard arrays.
In this tutorial, we won't just repeat the syntax. We'll look inside the PHP engine, understand the internal array pointer, see how iterators work, and learn to avoid pitfalls. Let's go!
1. Anatomy of foreach: What the Documentation Says
Let's start with the basics. foreach is designed for iterating over arrays and objects. The syntax is simple:
<?php$fruits = ['apple', 'banana', 'cherry'];
foreach ($fruits as $fruit) { echo $fruit . "";}// Output: apple banana cherry?>But there's also a second form — with a key:
<?php$prices = ['apple' => 100, 'banana' => 50];
foreach ($prices as $name => $price) { echo "$name costs $price rub.";}?>It all seems obvious. But let's dig deeper. PHP processes foreach differently than for or while. Internally, it uses the internal array pointer and a special iteration mechanism.
2. The Internal Array Pointer: A Secret Agent
Every array in PHP has an invisible "cursor" — the internal pointer. It points to the current element. The functions current(), next(), prev(), end(), and reset() manage it.
When you run foreach, PHP does the following:
- Resets the internal pointer to the first element (like
reset()). - On each iteration, takes the element the pointer points to.
- Moves the pointer to the next element (like
next()). - Repeats until it reaches the end.
Let's see this in action:
<?php$array = [10, 20, 30];
// Manual imitation of foreachreset($array); // Pointer to the first elementwhile ($value = current($array)) { echo $value . " "; next($array); // Move the pointer}// Output: 10 20 30?>But there's a nuance: if the array is empty or contains false, this trick will break. foreach works correctly always because it uses a more reliable internal mechanism — iterators.
3. Iterators and foreach: A Modern Approach
Starting with PHP 5, foreach can work not only with arrays but also with any objects implementing the Traversable interface (or its child Iterator).
When you pass an object to foreach, PHP calls the iterator methods:
rewind()— reset to the beginningvalid()— check if an element existscurrent()— get the valuekey()— get the keynext()— move to the next
Example of a custom iterator:
<?phpclass NumbersIterator implements Iterator { private $position = 0; private $numbers = [1, 2, 3, 4, 5];
public function rewind() { $this->position = 0; }
public function current() { return $this->numbers[$this->position]; }
public function key() { return $this->position;