for and foreach Loops
for loops count; foreach loops walk through arrays and other collections. Between them they cover most repetition in PHP — foreach in particular is the loop you'll write most often, because so much PHP code processes arrays of rows, items and records.
The for loop
A for loop puts initialisation, condition and update on one line:
<?php for ($i = 1; $i <= 5; $i++) { echo "Pass $i\n"; }
Pass 1 Pass 2 Pass 3 Pass 4 Pass 5
The first part runs once, the condition is checked before every pass, and the update runs after every pass. Other step sizes work too:
<?php for ($i = 10; $i > 0; $i -= 3) { echo $i, " "; } echo "\n"; for ($i = 1; $i <= 1000; $i *= 10) { echo $i, " "; } echo "\n";
10 7 4 1 1 10 100 1000
foreach over a list
foreach visits every element of an array in order. No index bookkeeping, no off-by-one errors:
<?php $planets = ["Mercury", "Venus", "Earth", "Mars"]; foreach ($planets as $planet) { echo strtoupper($planet), "\n"; } foreach ($planets as $index => $planet) { echo $index + 1, ". $planet\n"; }
MERCURY VENUS EARTH MARS 1. Mercury 2. Venus 3. Earth 4. Mars
foreach over keys and values
With an associative array (lesson 22), $key => $value gives you both:
<?php $prices = ["coffee" => 3.20, "tea" => 2.50, "cake" => 4.75]; foreach ($prices as $item => $price) { printf("%-7s %5.2f\n", $item, $price); }
coffee 3.20 tea 2.50 cake 4.75
Changing elements while looping
foreach works on a copy of each value, so assigning to the loop variable doesn't change the array. Either write back using the key, or loop by reference with &:
<?php $prices = [10, 20, 30]; foreach ($prices as $key => $price) { $prices[$key] = $price * 2; } echo implode(", ", $prices), "\n"; foreach ($prices as &$price) { $price += 1; } unset($price); // important: break the reference after the loop echo implode(", ", $prices), "\n";
20, 40, 60 21, 41, 61
After a by-reference loop, $price is still a reference to the last element. Forgetting unset($price) leads to a famous bug where a later loop silently overwrites that element. Often the cleanest option is to build a new array with array_map (lesson 23) instead.
Nested loops
<?php for ($row = 1; $row <= 4; $row++) { for ($col = 1; $col <= 4; $col++) { printf("%4d", $row * $col); } echo "\n"; }
1 2 3 4 2 4 6 8 3 6 9 12 4 8 12 16
break and continue with levels
In nested loops, break and continue accept a number saying how many levels to affect — break 2 leaves both loops:
<?php $grid = [ [4, 8, 15], [16, 23, 42], [7, 9, 11], ]; foreach ($grid as $r => $row) { foreach ($row as $c => $value) { if ($value === 23) { echo "Found 23 at row $r, column $c\n"; break 2; } } } echo "Search finished\n";
Found 23 at row 1, column 1 Search finished
Ranges
range() creates an array of numbers or letters, handy for quick loops:
<?php foreach (range(1, 5) as $n) { echo $n * $n, " "; } echo "\n"; echo implode("", range('a', 'e')), "\n"; echo implode(" ", range(0, 100, 25)), "\n"; echo array_sum(range(1, 100)), "\n";
1 4 9 16 25 abcde 0 25 50 75 100 5050
Loops in templates
In HTML templates, foreach with the alternative syntax keeps markup readable:
<?php $menu = ['Home' => '/', 'Blog' => '/blog', 'About' => '/about']; $current = '/blog'; ?> <nav> <?php foreach ($menu as $label => $url): ?> <a href="<?= $url ?>"<?= $url === $current ? ' class="active"' : '' ?>><?= $label ?></a> <?php endforeach; ?> </nav>
GET /menu.php
> GET /menu.php <nav> <a href="/">Home</a> <a href="/blog" class="active">Blog</a> <a href="/about">About</a> </nav>
Choosing a loop
| Loop | Use it when |
|---|---|
foreach | You want every element of an array or iterable. The default choice. |
for | You're counting, or need a custom step. |
while | You repeat until something changes and don't know how many passes it'll take. |
do-while | The body must run at least once. |
Try it yourself
Print a right-angled triangle of stars, then change it to print numbers or count down.
* ** *** **** ***** -----