PHP · Beginner

Conditions and Loops in PHP

9 min readUpdated September 24, 2026Every example verified

In short: PHP branches with if, elseif and else, and with the match expression (PHP 8.0), which compares strictly and returns a value. It repeats with while, do...while, for and foreach; break leaves a loop early and continue skips to the next pass. Conditions are evaluated as booleans, and 0, "", "0", null and an empty array all count as false.

Deciding and repeating

A condition is any expression that PHP can treat as true or false. Comparisons produce booleans directly: <, <=, >, >=, ==, ===, != and !==. Other values are converted when they appear in a condition, and the rule is worth memorising: the integer 0, the float 0.0, the empty string, the string "0", null and an empty array are false; everything else, including "false", " " and -1, is true. &&, || and ! combine conditions. (and and or also exist but bind more loosely than =, which causes surprises; stick to the symbols.)

if (condition) { ... } runs its block when the condition holds. elseif adds another test that is only tried when the earlier ones failed, and else catches everything left. Only the first matching branch runs. The braces are optional for a single statement, but always writing them prevents the classic bug where an indented second line looks like part of the branch and is not.

match (PHP 8.0) is an expression: it compares a value against each arm with ===, returns the arm's result, and throws UnhandledMatchError if nothing matches and there is no default arm. Several values can share one arm, separated by commas. It replaces most uses of the older switch, which compares loosely with == and falls through into the next case unless you write break. match (true) with conditions as the arms is a tidy way to write a chain of range checks.

Loops repeat a block. while (condition) tests first and may run zero times; do { } while (condition) runs once before testing. for (start; condition; step) gathers the counter setup, test and increment in one line and suits counting. foreach ($array as $value), or foreach ($array as $key => $value), visits every element of an array and is the loop you will write most. The while (($line = fgets(STDIN)) !== false) form reads input until it runs out; the !== false matters because an empty line is a string that would otherwise look false.

Inside any loop, break leaves it immediately and continue jumps to the next pass. Both accept a number, break 2, to act on an enclosing loop. A loop that never changes the value its condition tests runs forever; when a program on this site hangs, look for the missing increment first.

Syntax

 PHP · syntax
if ($temp < 12) {
    // ...
} elseif ($temp <= 26) {
    // ...
} else {
    // ...
}

$label = match ($code) {
    "A", "B" => "group one",
    "C"      => "group two",
    default  => "unknown",
};

while ($condition) { ... }
do { ... } while ($condition);
for ($i = 0; $i < 10; $i++) { ... }
foreach ($items as $item) { ... }
foreach ($items as $index => $item) { ... }

break;      // leave the loop
continue;   // next pass
$sign = $n >= 0 ? "positive" : "negative";   // ternary: a small if in an expression

match arms use ===, do not fall through, and the whole match is a value you can assign or echo.

Classifying readings until input ends

Greenhouse temperatures arrive one per line; each is labelled by an if/elseif/else chain.

 PHP
<?php
while (($line = fgets(STDIN)) !== false) {
    $temp = (float) trim($line);
    if ($temp < 12) {
        $state = "too cold";
    } elseif ($temp <= 26) {
        $state = "fine";
    } else {
        $state = "too hot";
    }
    echo $temp, " C: ", $state, "\n";
}

Input given to the program: 918.52726

Output

9 C: too cold
18.5 C: fine
27 C: too hot
26 C: fine

The loop condition reads a line and compares it with false in one step; when input is exhausted fgets returns false and the loop ends. The chain tests in order, so 26 reaches the elseif and is fine because the first test failed and 26 <= 26 holds. The float 9 prints as 9, without a decimal point, because PHP drops a zero fraction when printing.

match and a counting loop

A bus timetable maps a day code to a number of departures with match, then a for loop lists the morning buses.

 PHP
<?php
$days = ["mon", "sat", "sun", "wed"];
foreach ($days as $day) {
    $departures = match ($day) {
        "mon", "tue", "wed", "thu", "fri" => 12,
        "sat" => 8,
        "sun" => 4,
    };
    echo str_pad($day, 4), $departures, " departures\n";
}

for ($hour = 6; $hour <= 9; $hour++) {
    echo "Bus at ", $hour, ":00\n";
}

Output

mon 12 departures
sat 8 departures
sun 4 departures
wed 12 departures
Bus at 6:00
Bus at 7:00
Bus at 8:00
Bus at 9:00

Five weekday codes share one arm, so the match stays short. Because match is an expression, its result is assigned straight to $departures; with switch you would need a break in every case and an assignment in each. There is no default here, so a code such as "hol" would throw UnhandledMatchError, which is usually better than silently doing nothing. The for loop runs four times, for hours 6, 7, 8 and 9, and stops when $hour <= 9 becomes false.

continue, break and do...while

Sensor readings are summed; zero means a missing reading and is skipped, a negative value means a fault and stops the loop.

 PHP
<?php
$readings = [3, 0, 7, -1, 5, 9];
$total = 0;
foreach ($readings as $r) {
    if ($r === 0) {
        continue;
    }
    if ($r < 0) {
        echo "Sensor fault, stopping\n";
        break;
    }
    $total += $r;
}
echo "Total: ", $total, "\n";

$attempt = 0;
do {
    $attempt++;
    echo "Attempt ", $attempt, "\n";
} while ($attempt < 3);

Output

Sensor fault, stopping
Total: 10
Attempt 1
Attempt 2
Attempt 3

continue skips the rest of the block for the 0, so it is neither added nor treated as a fault. break on the -1 ends the loop before 5 and 9 are seen, which is why the total is 3 + 7 = 10. The do...while prints before it tests, so the body is guaranteed to run at least once; here it runs three times because the test is at the bottom.

Common mistakes

  • Assignment inside a condition

    Why it goes wrong: if ($status = "open") assigns and then tests the string "open", which is always true. The intended comparison never happens.

    Fix: Use == or, better, ===. Some people write the constant first, "open" === $status, so a typo becomes a parse error.

     PHP · fix
    <?php
    $status = "closed";
    if ($status === "open") {
        echo "open\n";
    }
  • Forgetting break in a switch

    Why it goes wrong: After a matching case, execution continues into the following cases until it meets a break, so several branches run.

    Fix: End each case with break, or use match, which never falls through.

  • Testing fgets against a truthy value

    Why it goes wrong: while ($line = fgets(STDIN)) stops early on a line containing only 0, because the string "0" is false.

    Fix: Compare with !== false so only the end of input ends the loop.

     PHP · fix
    <?php
    while (($line = fgets(STDIN)) !== false) {
        echo trim($line), "\n";
    }
  • A loop whose condition never changes

    Why it goes wrong: while ($i < 5) { echo $i; } without $i++ prints forever and the program is killed for exceeding its time limit.

    Fix: Make sure something in the body moves the condition towards false, or use for so the step is written next to the test.

Values that count as false in a condition

ValueIn a conditionNote
0, 0.0, -0.0falseany other number, including -1, is true
"" and "0"false"0.0", " " and "false" are true
nullfalsealso what an unset variable yields
[]falsean array with any element, even [0], is true
falsefalsetrue is true

Where you use this

Nearly every exercise combines one loop that reads input with one condition that decides what to do with each line: count the values above a threshold, stop at a sentinel, or classify each record. The read-until-false loop and the for loop over a known count are the two input shapes you will see again and again, and match (true) is the clean way to turn a number into a category name when the categories are ranges rather than exact values.

 PHP · in practice
$band = match (true) {
    $score >= 90 => "gold",
    $score >= 70 => "silver",
    default      => "bronze",
};

Key points

  • if / elseif / else runs the first branch whose condition is true; always use braces.
  • match compares with ===, returns a value, has no fallthrough and throws if nothing matches.
  • while tests first, do...while tests after, for bundles counter setup, test and step.
  • foreach is the natural way to visit every array element, with or without keys.
  • Read input with while (($line = fgets(STDIN)) !== false).
  • break leaves the loop, continue skips to the next pass; a number targets an outer loop.
  • 0, 0.0, "", "0", null and [] are false; everything else is true.

Try it yourself

The program reads a count and then that many integers, but never counts anything. Complete the loop so it counts the even numbers and prints the count.

Your program
<?php
$n = (int) trim(fgets(STDIN));
$evens = 0;
for ($i = 0; $i < $n; $i++) {
    $value = (int) trim(fgets(STDIN));
    // increase $evens when $value is even
}
echo $evens, "\n";
Input the program receives: 5 ↵ 4 ↵ 7 ↵ 10 ↵ 3 ↵ 8
Expected output: 3

Practise this

Open the PHP playground

Frequently asked questions

What is the difference between match and switch in PHP?

match (PHP 8.0) is an expression that returns a value, compares with ===, allows several values per arm, never falls through, and throws UnhandledMatchError when no arm matches and there is no default. switch is a statement, compares with ==, and continues into the next case unless each one ends with break. Prefer match for mapping a value to a result; use switch only when a branch must run several statements and you want the older, looser behaviour.

Is the string "0" true or false in PHP?

False. The only strings that are false in a condition are the empty string and "0". "0.0", "false" and a single space are all true. This is why input loops should compare fgets with !== false instead of relying on truthiness.

When should I use foreach instead of for?

Use foreach whenever you want every element of an array, in order; it needs no counter and cannot run past the end. Use for when you are counting rather than visiting, for example reading exactly $n lines of input, or when you need to step by more than one or walk backwards.

Progress is stored only in this browser.

How this page was checked. Every program on it was run with PHP 8.4 at build time by the publishing checks, and the output shown is what it printed. Running PHP inside the browser is not available yet, so the Run button is absent rather than pretending; copy the code and run it with PHP 8.4 locally.