PHPIterating Arrays

Iterating Arrays

Reading through an array's elements one at a time is one of the most common operations in any PHP script. foreach is the tool you will reach for the vast majority of the time, but PHP arrays also carry an internal pointer with its own set of functions, and there is an entire Iterator interface for objects that behave like arrays. This page ties those pieces together.

foreach, recap

foreach walks every element of an array in insertion order, without you having to manage an index or a pointer yourself. You can pull just the value, or the key and the value together.

Values only, and key + value

PHP
<?php
$scores = ["Ana" => 91, "Bo" => 78, "Cy" => 85];

foreach ($scores as $score) {
    echo $score . " ";
}
echo "\n";

foreach ($scores as $name => $score) {
    echo "{$name}: {$score}\n";
}
91 78 85
Ana: 91
Bo: 78
Cy: 85

By default foreach works on a copy of the array, so modifying $score inside the loop does not change $scores. To modify the original array while iterating, take the value by reference with & - but remember to unset() that reference variable right after the loop, or it will keep pointing at the last element and silently corrupt the next loop that reuses the same variable name.

Modifying in place with a reference

PHP
<?php
$scores = ["Ana" => 91, "Bo" => 78, "Cy" => 85];

foreach ($scores as $name => &$score) {
    $score += 5; // curve everyone's score
}
unset($score); // break the reference - important!

print_r($scores);
Array
(
    [Ana] => 96
    [Bo] => 83
    [Cy] => 90
)
array_walk() for side effects

array_walk() applies a callback to every element of an array, passing the value (by reference, so it can modify it) and the key. It is most useful when you already have a named function or need to pass extra context via a third argument, rather than writing an inline foreach.

Applying a callback to every element

PHP
<?php
$prices = ["apple" => 0.50, "banana" => 0.25];

array_walk($prices, function (&$price, $name) {
    $price = round($price * 1.08, 2); // apply 8% tax, modify by reference
});

print_r($prices);
Array
(
    [apple] => 0.54
    [banana] => 0.27
)
The internal pointer: current(), next(), reset()

Every PHP array carries an internal pointer that tracks "the current element," independent of foreach. current() reads the value the pointer is on, next() advances it, reset() moves it back to the first element, and end() jumps to the last. These functions predate foreach and are rarely needed in modern code, but you will still encounter them in older codebases, and they matter if you need to manually walk two arrays in lockstep.

Manual pointer walking

PHP
<?php
$colors = ["red", "green", "blue"];

echo current($colors); // red - pointer starts at the first element
echo "\n";
echo next($colors);    // green - advances and returns the new current value
echo "\n";
echo next($colors);    // blue
echo "\n";
var_dump(next($colors)); // bool(false) - moved past the end
red
green
blue
bool(false)
each() is gone in PHP 8
`each()`, which used to return the current key/value pair and advance the pointer in one call, was **deprecated in PHP 7.2 and removed entirely in PHP 8.0**. If you encounter it in older code (often paired with `while (list($k, $v) = each($arr))`), replace that loop with a plain `foreach ($arr as $k => $v)`, which does the same job more clearly and still works.
Beyond arrays: the Iterator interface

foreach is not limited to arrays - it also works on any object that implements PHP's Iterator interface (with methods like current(), key(), next(), rewind(), and valid()), or the simpler IteratorAggregate interface. This is how custom collection classes, database result sets, and generators (functions using yield) all plug into the same foreach syntax you already know. That is a deeper topic on its own, but recognizing the name is useful the first time you see implements Iterator in a class definition.

  • foreach is almost always the right default - reach for the internal-pointer functions only when you specifically need manual, non-linear control.

  • Always unset() a by-reference loop variable immediately after the loop ends.

  • array_walk() mutates in place and returns true on success, unlike array_map() which returns a brand-new array.

  • Generators (yield) let you write iterable logic without building the whole array in memory first - worth knowing exists even before you need it.

Nested foreach and the reference gotcha
If you use `foreach ($arr as &$item)` and then later run a *second* `foreach` over the same array without unsetting `$item` first, the last element can get silently overwritten with a copy of the second-to-last one. This is one of PHP's most-cited "gotchas" - the fix is always the same: `unset()` the reference variable right after its loop.
Tip
Prefer `foreach` for readability, `array_map()`/`array_filter()` for transformations that produce a new array, and `array_walk()` only when you specifically need to mutate elements in place through a reusable callback.