PHPfor Loop

for Loop

The for loop is PHP's tool for repeating a block of code a known or countable number of times. Where a while loop only tracks a condition, and a foreach loop only tracks "the next item in this array," a for loop packs three separate ideas — where to start, when to stop, and how to move forward — into a single line. That makes it the natural choice whenever the number of repetitions can be expressed as a range, a count, or a step, rather than as "until some array runs out."

The three clauses

A for statement has the shape for (initialization; condition; increment) { ... }. Each of the three clauses has a distinct job, and PHP evaluates them in a very specific order: the initialization runs exactly once, before the loop starts; the condition is checked before every iteration, including the very first one; and the increment runs after every iteration, right before the condition is checked again.

A basic counting loop

PHP
<?php
for ($i = 1; $i <= 5; $i++) {
    echo "Count: {$i}\n";
}
Count: 1
Count: 2
Count: 3
Count: 4
Count: 5
  • $i = 1 — the initialization, run once, typically used to create and set a loop counter.

  • $i <= 5 — the condition, checked before each pass; the loop keeps running as long as this is truthy.

  • $i++ — the increment, run after each pass, typically used to move the counter forward.

All three clauses are optional

None of the three clauses is actually required. You can leave any of them empty as long as you keep the semicolons in place, and PHP will simply skip whatever you omitted. Taken to the extreme, for (;;) { ... } is an infinite loop with no built-in exit condition at all — you would need a break statement inside the body to escape it.

Moving parts outside the for statement

PHP
<?php
$i = 1; // initialization moved above the loop

for (; $i <= 3; $i++) {
    echo "Item {$i}\n";
}

// A deliberately open-ended loop that breaks itself
$attempts = 0;
for (;;) {
    $attempts++;
    if ($attempts >= 3) {
        break;
    }
}
echo "Stopped after {$attempts} attempts\n";
Item 1
Item 2
Item 3
Stopped after 3 attempts
Multiple expressions in one clause

Each clause can actually hold more than one expression, separated by commas. This is most often used to initialize or update two related counters in lockstep, without needing a separate variable declared outside the loop. Only the last expression in the condition clause is checked as the loop's stopping test — PHP still evaluates all of them, but only the final one's truthiness decides whether the loop continues.

Two counters moving in opposite directions

PHP
<?php
for ($low = 0, $high = 10; $low < $high; $low++, $high--) {
    echo "low={$low} high={$high}\n";
}
low=0 high=10
low=1 high=9
low=2 high=8
low=3 high=7
low=4 high=6
Nested for loops

A for loop can contain another for loop, which is how you build anything grid-shaped: multiplication tables, calendars, board patterns, or coordinate scans. The outer loop controls the rows and the inner loop controls the columns; the inner loop runs completely, for every single pass of the outer loop.

A small multiplication table

PHP
<?php
for ($row = 1; $row <= 3; $row++) {
    for ($col = 1; $col <= 3; $col++) {
        echo str_pad((string) ($row * $col), 4, ' ', STR_PAD_LEFT);
    }
    echo "\n";
}
   1   2   3
   2   4   6
   3   6   9

The same nesting pattern draws shapes instead of numbers. Here the outer loop picks the row, and the inner loop prints one * per column, growing wider on each row:

A simple triangle pattern

PHP
<?php
for ($row = 1; $row <= 4; $row++) {
    for ($col = 1; $col <= $row; $col++) {
        echo '*';
    }
    echo "\n";
}
*
**
***
****
Stepping by something other than 1

Because the increment clause can be any expression, a for loop handles ranges that skip values just as naturally as it handles simple counting. This is one of the clearest cases where for outperforms foreach: there is no array to iterate over, only a mathematical sequence.

Every third number, and counting backward

PHP
<?php
for ($i = 0; $i <= 15; $i += 3) {
    echo "{$i} ";
}
echo "\n";

for ($i = 10; $i >= 0; $i -= 2) {
    echo "{$i} ";
}
echo "\n";
0 3 6 9 12 15
10 8 6 4 2 0
for vs foreach for iterating arrays

It is entirely possible to walk an array with a for loop, indexing in manually with $array[$i]. It is also, in most cases, the wrong choice. Manual indexing assumes the array has sequential integer keys starting at zero, which breaks silently the moment a key is removed, the array is filtered, or the array uses string keys at all. A foreach loop reads whatever keys and values actually exist, so it cannot go out of bounds and does not care how the array is keyed.

Fragile indexing vs foreach

PHP
<?php
$fruits = ['apple', 'banana', 'cherry'];
unset($fruits[1]); // gap left at index 1

// for loop: silently skips or errors on the gap
for ($i = 0; $i < count($fruits); $i++) {
    echo ($fruits[$i] ?? '(missing)') . "\n";
}

// foreach: only ever visits keys that really exist
foreach ($fruits as $index => $fruit) {
    echo "{$index} => {$fruit}\n";
}
apple
(missing)
cherry
0 => apple
2 => cherry

for still earns its place when you genuinely need the index for arithmetic — comparing an element to its neighbor, walking two arrays in parallel by position, or processing every second element — or when you are counting through a numeric range that was never an array to begin with. If the task is simply "do something with each value in this array," reach for foreach; it is shorter, cannot misalign with the array's real keys, and communicates intent more clearly to anyone reading the code later.

Off-by-one errors and re-evaluated conditions
The condition is re-checked before every single iteration, which means it is also re-evaluated against whatever the counter is at that moment — a classic source of off-by-one bugs. `$i <= count($array) - 1` and `$i < count($array)` cover the same range, but mixing up `<` and `<=` is one of the most common mistakes in loop code. It also matters that `count($array)` is being called fresh on every comparison unless you cache it first, which brings a small performance cost on very large arrays.
The counter still exists after the loop
PHP does not scope the loop counter to the loop body. After `for ($i = 0; $i < 10; $i++) { ... }` finishes, `$i` is still `10` in the surrounding code, unlike some languages where the counter would be out of scope. Relying on this is usually a sign the logic should be restructured, but it is worth knowing it will not simply disappear.
Tip
Cache anything expensive to recompute — like `count($array)` — in a variable before the loop starts, and use that variable in the condition instead of calling the function on every pass: `for ($i = 0, $len = count($array); $i < $len; $i++)`. It keeps the loop's stopping test cheap and makes the intent ("iterate the whole length, fixed at the start") explicit rather than implicit.