for, foreach, while, do-while — when to use each
Concept
PHP has four loop constructs. Choosing the right one communicates intent and avoids common bugs.
for is the classic counted loop. Use it when you know the loop count upfront and need the counter variable (e.g., to use as an array index, to process adjacent elements). The three expressions in for(init; condition; increment) are all optional — for(;;) is an infinite loop.
foreach iterates over arrays and objects implementing Traversable. It creates a copy of the array's internal pointer and iterates the copy, which means modifying the array during iteration doesn't affect the loop. Modifying elements requires either passing by reference (foreach($arr as &$item)) or using the index. Always unset the reference variable after a reference-based foreach.
while loops while a condition is true. Use it when the loop count is unknown (reading from a stream, processing a queue). Check the condition before each iteration — the body may never execute.
do-while guarantees at least one execution. Use when the loop body must run before the first condition check — for example, "prompt user until valid input" or "generate unique ID until not in database". Rare but semantically exact for these cases.
Code Example
<?php
declare(strict_types=1);
// for — indexed iteration
$arr = ['a', 'b', 'c', 'd'];
for ($i = 0; $i < count($arr); $i++) {
echo $arr[$i];
}
// Note: count() is called every iteration in this form — cache it if array is large
for ($i = 0, $len = count($arr); $i < $len; $i++) {
echo $arr[$i]; // cache count in $len
}
// for — two-pointer technique (adjacent pairs)
for ($i = 0; $i < count($arr) - 1; $i++) {
echo "{$arr[$i]}->{$arr[$i+1]} ";
}
// foreach — standard iteration
foreach ($arr as $index => $value) {
echo "$index: $value\n";
}
// foreach reference — modifies original
$prices = [10.0, 20.0, 30.0];
foreach ($prices as &$price) {
$price *= 1.1; // 10% increase
}
unset($price); // ALWAYS unset after reference foreach
// while — unknown count
$handle = fopen('/path/to/file.txt', 'r');
while (($line = fgets($handle)) !== false) {
process($line); // line by line, never loads full file
}
fclose($handle);
// while — processing a queue
$queue = new \SplQueue();
$queue->enqueue('job-1');
$queue->enqueue('job-2');
while (!$queue->isEmpty()) {
$job = $queue->dequeue();
echo "Processing: $job\n";
}
// do-while — execute at least once
$attempts = 0;
do {
$pin = generateRandomPin(); // must generate before checking
$attempts++;
} while (isPinTaken($pin) && $attempts < 10);
// Nested foreach with generators (memory-efficient)
function chunks(array $arr, int $size): Generator
{
foreach (array_chunk($arr, $size) as $chunk) {
yield $chunk;
}
}
foreach (chunks(range(1, 100), 10) as $batch) {
// process 10 at a time
}