Himanshu SharmaSoftware engineer
Let’s talk

PHP · Lesson 16 of 54

for and foreach loops

Count with for loops and walk through arrays with foreach, read keys and values, change elements by reference safely, and use the alternative loop syntax.

12 minControl flowUpdated 22 October 2026

Awaiting technical review. This lesson is readable but not yet listed in search engines.

By the end of this lessonLoop over numbers with for and over arrays with foreach, including keys and nested arrays.

for: counting loops

When you know how many times something should happen, a for loop keeps everything about the counting in one line:

php
<?php
for ($i = 1; $i <= 5; $i++) {
    echo "Step $i\n";
}
result
Step 1
Step 2
Step 3
Step 4
Step 5

The brackets hold three parts separated by semicolons:

  1. Start: $i = 1 runs once, before the first pass.
  2. Condition: $i <= 5 is checked before every pass; the loop stops when it is false.
  3. Step: $i++ runs after every pass.

This is the same as the while loop from the previous lesson, with the setup and the update moved into the header where they are hard to forget. The step can be anything, such as counting down or in fives:

php
<?php
for ($t = 10; $t >= 0; $t -= 5) {
    echo "T minus $t\n";
}

for ($row = 1; $row <= 3; $row++) {
    echo "7 x $row = " . 7 * $row . "\n";
}
result
T minus 10
T minus 5
T minus 0
7 x 1 = 7
7 x 2 = 14
7 x 3 = 21

You can loop over an indexed array with for and count(), but there is a better tool for that.

foreach: visiting every element

foreach goes through an array from the first element to the last, giving you each value in turn. No counter, no count(), no risk of an off-by-one mistake:

php
<?php
$products = ["Mechanical Keyboard", "Ceramic Mug Set", "Water Bottle"];

foreach ($products as $product) {
    echo "- $product\n";
}
result
- Mechanical Keyboard
- Ceramic Mug Set
- Water Bottle

Keys and values

Arrays in PHP can have meaningful keys, such as product names mapped to prices (arrays get their own section). The key => value form gives you both:

php
<?php
$prices = [
    "Mechanical Keyboard" => 89.5,
    "Ceramic Mug Set" => 18.75,
    "Water Bottle" => 12.0,
];

$total = 0;
foreach ($prices as $name => $price) {
    printf("%-20s %6.2f\n", $name, $price);
    $total += $price;
}
printf("%-20s %6.2f\n", "Total", $total);
result
Mechanical Keyboard   89.50
Ceramic Mug Set       18.75
Water Bottle          12.00
Total                120.25

For an ordinary list, the key is the position, starting at 0. Add one when showing it to people:

php
<?php
$steps = ["Add to cart", "Enter address", "Pay"];
foreach ($steps as $index => $step) {
    echo ($index + 1) . ". $step\n";
}
result
1. Add to cart
2. Enter address
3. Pay

Unpacking rows

When each element is itself a small array, you can unpack it straight into variables with square brackets:

php
<?php
$orders = [
    [1001, "Asha", 108.25],
    [1002, "Liam", 24.99],
];

foreach ($orders as [$id, $customer, $amount]) {
    echo "Order $id for $customer: $amount\n";
}
result
Order 1001 for Asha: 108.25
Order 1002 for Liam: 24.99

Changing elements while looping

foreach gives you a copy of each value, so assigning to $price inside the loop does not change the array. There are two ways to change elements in place. The clearest is to write back through the key:

php
<?php
$prices = ["Keyboard" => 89.5, "Mug" => 18.75];

foreach ($prices as $name => $price) {
    $prices[$name] = round($price * 1.18, 2); // add 18% tax
}

print_r($prices);
result
Array
(
    [Keyboard] => 105.61
    [Mug] => 22.13
)

The other is a reference, written &$value, which makes the loop variable an alias for the element. It works, but it has a trap: after the loop, $value still points at the last element. Always unset() it straight away:

php
<?php
$quantities = [1, 2, 3];

foreach ($quantities as &$quantity) {
    $quantity *= 10;
}
unset($quantity); // break the link to the last element

print_r($quantities);
result
Array
(
    [0] => 10
    [1] => 20
    [2] => 30
)

Without that unset(), a later foreach ($quantities as $quantity) would write each value into the last element in turn and corrupt the array. Many teams avoid references in loops entirely and use the key, or array_map() from array_map, array_filter and array_reduce.

break and continue

break and continue work in for and foreach exactly as in while: leave the loop, or skip to the next element.

php
<?php
$stock = ["Keyboard" => 60, "Puzzle" => 0, "Mug" => 120, "Bands" => 0];

foreach ($stock as $name => $units) {
    if ($units > 0) {
        continue;
    }
    echo "$name is sold out\n";
}
result
Puzzle is sold out
Bands is sold out

Alternative syntax for templates

In HTML templates, foreach and for can use a colon and endforeach; / endfor;, which is easier to match up between tags:

php
<ul>
<?php foreach ($products as $product): ?>
  <li><?= htmlspecialchars($product) ?></li>
<?php endforeach; ?>
</ul>

Common mistakes

  • Using for with count() on arrays whose keys are not 0, 1, 2... foreach works for every array.
  • Expecting assignment to the loop variable to change the array. Use the key or a reference.
  • Forgetting unset() after a by-reference foreach.
  • Changing an array's size inside a for loop that uses count() in its condition.

Exercise

Given the stock list ["Keyboard" => 60, "Headphones" => 25, "Desk Mat" => 40, "Puzzle" => 0], use one foreach to print each product with "low" next to anything under 30, and count how many products are low.

Show one possible answer
php
<?php
$stock = ["Keyboard" => 60, "Headphones" => 25, "Desk Mat" => 40, "Puzzle" => 0];
$low = 0;

foreach ($stock as $name => $units) {
    $flag = "";
    if ($units < 30) {
        $flag = " (low)";
        $low++;
    }
    echo "$name: $units$flag\n";
}
echo "$low products are low\n";
result
Keyboard: 60
Headphones: 25 (low)
Desk Mat: 40
Puzzle: 0 (low)
2 products are low

Key points

  • for (start; condition; step) is ideal when you know how many passes you need.
  • foreach ($items as $item) visits every element of an array; foreach ($items as $key => $value) also gives the key.
  • foreach works on a copy; to change elements in place use &$value and unset($value) afterwards, or write back with the key.
  • list-style destructuring, foreach ($rows as [$name, $price]), unpacks each element into variables.

Check yourself

1. How many times does this run? for ($i = 0; $i < 5; $i++) { }
2. What does foreach ($prices as $name => $price) give you in $name?
3. Why should you call unset($value) after foreach ($items as &$value)?
4. Which loop is the usual choice for visiting every element of an associative array?