foreach ($array as $value) runs the block once for every element, with $value set to the current element. To get the key as well, write foreach ($array as $key => $value).
foreach works on any array, whatever its keys, and you never manage a counter or check a length. It is the loop you will write most often in PHP.
Loop over keys and values
In $key => $value, the names are yours to choose: $name => $age, $id => $user, $k => $v. On a list, the keys are the positions 0, 1, 2. On an associative array (連想配列), they are the string keys you defined.
The last loop shows a detail that surprises people: a string key that looks like a whole number, such as "1", is stored as the integer 1. "01" is not a canonical integer, so it stays a string.
Get the index in foreach
On a list, the key is the index, so foreach ($items as $i => $item) is all you need. On an associative array there is no numeric position, so keep a counter. To treat the first or last element differently, compare the key with array_key_first() or array_key_last().
The last loop prints a comma between names but not after the last one. For that exact job implode(', ', array_keys($scores)) is shorter; the array_key_last() check is for when the last pass needs different logic, not just a different separator.
Change array values in foreach (by reference)
$value is a copy. Assigning to it changes nothing in the array. To change the array, either loop by reference with &$value, or write back through the key.
Both work. The key version has no hidden state left over after the loop, which is why many teams prefer it. For building a new array rather than changing one, array_map() is another option.
A related question: what happens if you add elements while looping? A plain foreach walks over the array as it was when the loop started, so $prices[] = ... inside it does not create extra passes. A by-reference loop walks the live array and does visit the new elements.
The unset() gotcha with foreach by reference
After foreach ($arr as &$v), the variable $v is still a reference to the last element of the array. If you later reuse the name $v in a normal loop, every pass writes into that last element. Run this and look at the last item:
The second loop assigns A, then B, then the last element's own value to $letters[2], and by the last pass that slot already holds B. The fix is one line: put unset($v); right after every by-reference foreach.
break and continue in foreach
break leaves the loop, continue skips to the next element. A common pattern is searching: loop until you find what you need, then break so the rest of the array is not checked.
In nested loops, break 2 leaves the inner loop and the one around it, and continue 2 jumps to the next pass of the outer loop.
Nested arrays and foreach with list()
Rows of data are arrays inside an array. Loop over the rows, then over each row, or pull the fields out directly in the foreach line with array destructuring: [$a, $b] for lists, ['key' => $var] for associative rows. list($a, $b) is the older spelling of [$a, $b].
The destructuring page has more on the [...] = syntax, including skipped elements and nested patterns.
Build an HTML list or table with foreach
In a template, the colon form foreach (...): with endforeach; keeps the loop readable between tags. This block builds a table, and the column headers are links: click one in the Page tab and the same script runs again with $_GET['sort'] set, so the rows come back in a new order.
Two habits worth copying from this block: the sort value from the URL is checked against a list of allowed columns before it is used, and every value printed into HTML that could contain user text goes through htmlspecialchars().
foreach over null, an empty array or an object
An empty array is fine: the body never runs. null, a string or a number is not: PHP prints a warning and skips the loop. For $tags = null; foreach ($tags as $tag) it prints:
Warning: foreach() argument must be of type array|object, null given in /var/www/html/index.php on line 4
This happens most often with data that is sometimes missing, like an optional field in decoded JSON. Fall back to an empty array with ??:
foreach ($data['tags'] ?? [] as $tag) {
echo $tag;
}
The object in that message is real: foreach over an object visits its properties that are visible from where the loop runs (outside the class, the public ones), with the property name as the key. Classes that implement Iterator or IteratorAggregate, and generators, decide for themselves what a foreach sees.
foreach vs for vs array_map
| Use | When |
|---|---|
foreach | You want every element, with or without its key. Works on any array, list or associative. |
for | You count (1 to 100, every second element) or need neighbours by index ($a[$i - 1]). See the for loop. |
array_map() | You build a new array by transforming each element, often as a one-liner with fn. |
array_filter() | You keep only the elements that pass a test. |
A quick way to decide: if the loop body only computes a new value per element, array_map(fn($x) => $x * 2, $a) says that in one line. If it has side effects (printing, saving, counting several things at once) or needs break, write the foreach.
Frequently Asked Questions
How do I get the key and value in a PHP foreach loop?
Use the => form: foreach ($array as $key => $value) { echo "$key: $value"; }. On a plain list the key is the index (0, 1, 2); on an associative array it is the string key, such as 'name'.
How do I get the index in a PHP foreach loop?
For a list, the key already is the index: foreach ($items as $i => $item). For an associative array, keep your own counter: set $i = 0; before the loop and $i++ at the end of each pass. array_key_first() and array_key_last() tell you which key is first or last.
How do I modify array values inside foreach in PHP?
Loop by reference with &: foreach ($prices as &$price) { $price *= 2; } unset($price);. Or write through the key: foreach ($prices as $key => $price) { $prices[$key] = $price * 2; }. A plain $price = ... only changes a copy.
Why does PHP foreach by reference duplicate the last element?
After foreach ($arr as &$v), $v is still a reference to the last element. A second foreach ($arr as $v) then writes each value into that last slot, so it ends up repeating the second to last value. Call unset($v); right after the reference loop.
How do I break out of a foreach loop in PHP?
break; stops the loop immediately and continue; skips to the next element. In nested loops, break 2; leaves both the inner and the outer loop.