Lesson 45 of 100
Loops15 min5 checksPHP 8.5

Work with Nested Loops in PHP

Learn how an inner for loop runs inside an outer one, how that fixes output order, and how to build a Cartesian product of two lists.

01 · THE IDEAREAD → TRY → LEARN

Loops inside loops

A nested loop is a loop placed inside the body of another loop. The outer loop picks one item, then the inner loop runs completely for that item. Only after the inner loop finishes does the outer loop move on. This is how you walk through grids, tables, and every combination of two lists.

A for loop has three parts: for (expr1; expr2; expr3). The first expression runs once at the start. The second is checked at the start of each iteration, and the loop ends when it is false. The third runs at the end of each iteration. Nesting works because the inner for runs its own expr1 again every time the outer body runs. The inner counter is therefore reset on every outer pass.

A first worked example

The outer variable below is the row and the inner variable is the column. Each row builds its cells, then prints one line.

Example
<?php
for ($row = 1; $row <= 3; $row++) {
    $cells = [];
    for ($col = 1; $col <= 3; $col++) {
        $cells[] = $row * $col;
    }
    echo implode(" ", $cells), "\n";
}
Output
1 2 3
2 4 6
3 6 9

The inner counter changes fastest. Row 1 is finished before row 2 starts. Remember this rule: the innermost loop varies fastest.

Cartesian products with stable order

A Cartesian product pairs every item of one list with every item of another. With 2 sizes and 2 colors you get 4 combinations. The loop nesting decides the order. If sizes are in the outer loop, all colors for the first size come first.

The manual advises keeping the expr2 condition simple, because it is evaluated on every iteration. So the next example stores each count() result in a variable first.

Example
<?php
$sizes = ["S", "M"];
$colors = ["red", "blue"];
$sizeCount = count($sizes);
$colorCount = count($colors);
$items = [];
for ($i = 0; $i < $sizeCount; $i++) {
    for ($j = 0; $j < $colorCount; $j++) {
        $items[] = $sizes[$i] . "-" . $colors[$j];
    }
}
echo json_encode($items, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
["S-red","S-blue","M-red","M-blue"]

If you swapped the two loops, the result would contain the same pairs in a different order: S-red, M-red, S-blue, M-blue. When a test or a report expects a specific order, the loop nesting is part of the contract.

Leaving both loops

A plain break leaves only the innermost loop. To leave two levels at once, write break 2. This is useful when you search for a first match.

Example
<?php
$numbers = [3, 5, 7, 2];
$count = count($numbers);
$found = null;
for ($i = 0; $i < $count; $i++) {
    for ($j = $i + 1; $j < $count; $j++) {
        if ($numbers[$i] + $numbers[$j] === 10) {
            $found = [$numbers[$i], $numbers[$j]];
            break 2;
        }
    }
}
echo json_encode($found), "\n";
Output
[3,7]

Note that the inner loop starts at $i + 1. That choice avoids pairing an item with itself and avoids checking the same pair twice in reverse. Starting the inner counter at a different place changes which combinations you visit, so choose it deliberately.

Edge cases and common mistakes

  • Empty input. If either list is empty, a Cartesian product is empty. The loops handle this naturally, because an inner or outer condition is false immediately.
  • Reusing a counter name. Using $i in both loops makes the inner loop interfere with the outer one. Use distinct names such as $i and $j, or $row and $col.
  • Swapped loops. The pairs are the same but the order differs. Decide which list is the outer one.
  • Forgetting to reset accumulators. A row buffer like $cells must be created inside the outer loop, not before it.
  • Growth. Two lists of 1,000 items produce 1,000,000 pairs. Nested loops multiply work.

The manual also notes that foreach is usually more convenient for arrays. Use for when you need explicit index control, such as the triangular $j = $i + 1 pattern.

Exercise contract

Write pairLabels(array $rows, array $cols): array. It returns a list of strings in the form row-col for every combination. The result must be ordered by row first, then by column, both in their original order. If either input is empty, return an empty array. Use two nested loops.

Recap

  • The inner loop runs fully for each pass of the outer loop, and its expr1 runs again each time.
  • The innermost counter changes fastest, which determines the output order.
  • Use distinct counter names and keep loop conditions simple.
  • break 2 exits two loop levels.
  • A Cartesian product of empty input is empty.
Before you start

Functions · Arrays · Types

You’ll practice
  • Build a small Cartesian product with stable ordering
  • Implement and test a deterministic PHP 8.4 function
Your task

Make pairLabels() pass all 5 checks. Run your code as often as you like, then check your answer.

exercise.phpSaved in this browserPHP 8.5
CONSOLE & CHECKS● Ready

Ready when you are

Your output will appear here.
Make it work. Then make it pass.

PHP loads on your first run. Each check starts fresh. Your code stays in this browser; clearing site data removes your progress. A check or the task looks wrong? Report a problem with this exercise.