No Update Loop Vars
ID |
php.no_update_loop_vars |
Severity |
low |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Php |
Tags |
control-flow, reliability |
Description
Reports a for loop whose control variable — the variable assigned or stepped in the loop
header — is also modified inside the loop body. The rule covers every mutation form: plain
assignment ($i = …), compound assignment ($i += …), post-increment/decrement
($i`, `$i--`) and pre-increment/decrement (`$i, --$i).
Rationale
A for loop reads as a self-contained contract: the header declares how the control
variable starts, when the loop ends, and how it advances each pass. When the body also
changes that variable, the real iteration count no longer matches the header, and the loop
becomes hard to follow. The result is frequently an off-by-one error, a skipped element, or
an unintended infinite loop. Keeping all control-variable updates in the header restores the
single, obvious advance step.
<?php
function process(array $items): void
{
for ($i = 0; $i < count($items); $i++) {
echo $items[$i];
$i++; // FLAW - the body advances the counter a second time
}
for ($j = 0; $j < count($items); $j++) {
echo $items[$j]; // OK - the control variable is only read in the body
}
}
Remediation
Decide where the control variable should advance and keep it in one place. If two steps per
pass are intended, encode that in the header (for example $i += 2). If the extra update was
accidental, remove it from the body. When the body genuinely needs to skip ahead based on
data, prefer a while loop so the manual advance is explicit rather than hidden alongside a
header that says otherwise.