PHP Break Continue

In PHP, break and continue are the two statements you use to change the normal flow of a loop from the inside. break stops a loop (or a switch) immediately and hands control to the code right after it, while continue skips the rest of the current iteration and jumps straight to the next one. Together they let you write loops that react to conditions the moment they’re discovered, instead of wrapping everything in extra if statements or letting every iteration run to completion.

Overview: How Break and Continue Work

Every loop in PHP — for, foreach, while, and do...while — has two moving parts: a condition that decides whether another iteration should run, and a body that executes once per iteration. Normally, PHP runs the body from top to bottom, then evaluates the condition again (re-checking it, or advancing a counter first, depending on the loop type), and repeats until the condition is false.

break and continue interrupt that normal path:

  • break exits the loop entirely. Execution jumps to the first statement after the loop’s closing brace, as if the loop’s condition had just become false.
  • continue skips the remaining statements in the current iteration only. Execution jumps back to the loop’s condition check (and, for a for loop, the increment expression runs first), then the loop carries on as normal.

Internally, when the Zend engine compiles your script it turns each loop into a block of opcodes with a known entry point (where the condition is tested) and a known exit point (the instruction right after the loop). break and continue compile into unconditional jump opcodes to one of those two addresses. Because the compiler resolves these jump targets at compile time by counting how many enclosing loop or switch structures surround the statement, both keywords accept an optional numeric argument to jump out of — or continue — more than one level at once.

PHP also treats switch as a break-able structure for this purpose. That single design choice is responsible for one of the most common bugs PHP beginners write, which is covered in the Common Mistakes section below.

Syntax

break [level];
continue [level];
Part Meaning
break; Immediately exits the innermost enclosing loop or switch.
break n; Exits n levels of enclosing loops/switch statements at once. break 1; is identical to plain break;.
continue; Skips to the next iteration of the innermost enclosing loop.
continue n; Skips to the next iteration of the loop n levels up, skipping the rest of every level in between entirely.

level must be a literal integer ≥ 1 — it cannot be a variable, because the compiler needs to resolve the jump target while compiling the script, before any code has actually run.

Examples

Example 1: break — stop searching once you find a match

<?php
$numbers = [4, 8, 15, 16, 23, 42, 108];
$target = 23;
$foundAt = -1;

foreach ($numbers as $index => $value) {
    if ($value === $target) {
        $foundAt = $index;
        break;
    }
}

if ($foundAt !== -1) {
    echo "Found {$target} at index {$foundAt}." . PHP_EOL;
} else {
    echo "{$target} not found." . PHP_EOL;
}

Output:

Found 23 at index 4.

The loop walks the array from the start. As soon as it finds the value it’s looking for, there’s no reason to keep checking the remaining elements, so break exits the foreach loop immediately. Without it, the loop would keep running, needlessly comparing every remaining element even though the answer was already found.

Example 2: continue — skip items instead of exiting

<?php
for ($i = 1; $i <= 10; $i++) {
    if ($i % 2 === 0) {
        continue;
    }
    echo $i . " ";
}
echo PHP_EOL;

Output:

1 3 5 7 9 

Here the goal is to print only odd numbers. When $i is even, continue skips the echo statement for that iteration — but the loop itself keeps going. Crucially, in a for loop the increment expression ($i++) still runs after continue, because continue jumps back to the loop's control clause, not past it. This is a frequent source of confusion for beginners coming from languages where continue behaves differently.

Example 3: multi-level break in nested loops

<?php
$grid = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
];

$search = 5;
$location = null;

foreach ($grid as $row => $cols) {
    foreach ($cols as $col => $value) {
        if ($value === $search) {
            $location = "row {$row}, col {$col}";
            break 2;
        }
    }
}

echo $location . PHP_EOL;

Output:

row 1, col 1

Without an argument, break inside the inner foreach would only exit that inner loop, and the outer loop would keep scanning further rows even after the value was already found. break 2; tells PHP to unwind two loop levels at once — the inner foreach and the outer foreach — stopping the search the instant a match is located. continue 2; works the same way, except it would resume the outer loop's next iteration instead of exiting entirely.

Under the Hood: Step by Step

When PHP compiles a script into opcodes, every loop and every switch pushes an entry onto an internal 'nesting' the compiler tracks. Each entry records two jump targets: where to go to re-test the loop's condition (the continue target) and where to go once the loop is finished (the break target). When the compiler encounters break n; or continue n;, it counts n entries outward from the current position and emits a single unconditional jump straight to that entry's target — there's no per-level overhead at runtime, the jump goes directly to the right place. This is also why the level must be a compile-time constant integer: the jump target has to be known while the opcodes are being generated, long before the loop ever executes.

Because a switch statement pushes its own entry onto that same nesting list, it counts as one level for both break and continue, exactly like a loop does. That single fact explains the behavior in the next section.

Common Mistakes

Mistake 1: expecting break to exit the loop from inside a switch

Because a switch counts as one breakable level, a plain break; inside a switch that lives inside a loop only exits the switch — the loop keeps running.

<?php
$commands = ['run', 'stop', 'jump'];

foreach ($commands as $command) {
    switch ($command) {
        case 'stop':
            break;
        default:
            echo "Executing: {$command}" . PHP_EOL;
    }
}
echo "Done" . PHP_EOL;

Output:

Executing: run
Executing: jump
Done

The intent was probably to stop the whole loop as soon as 'stop' is seen, but the break; only escapes the switch, so 'jump' still gets executed. The fix is to break two levels — out of the switch and out of the foreach:

<?php
$commands = ['run', 'stop', 'jump'];

foreach ($commands as $command) {
    switch ($command) {
        case 'stop':
            break 2;
        default:
            echo "Executing: {$command}" . PHP_EOL;
    }
}
echo "Done" . PHP_EOL;

Output:

Executing: run
Done

Mistake 2: expecting continue to skip an iteration from inside a switch

The same rule trips people up with continue. Because switch is a breakable level, continue; inside a switch behaves exactly like break; — it just exits the switch and execution falls through to whatever comes after it in the loop body, instead of skipping to the next iteration.

<?php
$values = [1, 2, 3, 4, 5];

foreach ($values as $value) {
    switch ($value) {
        case 3:
            continue;
    }
    echo "Processing {$value}" . PHP_EOL;
}

Output:

Processing 1
Processing 2
Processing 3
Processing 4
Processing 5

The developer likely wanted to skip printing for the value 3, but since continue; only exits the switch, the echo statement after it still runs. PHP even emits an E_WARNING at runtime for this exact pattern ('continue' targeting switch is equivalent to 'break'). The fix is continue 2;, which reaches past the switch to the enclosing foreach:

<?php
$values = [1, 2, 3, 4, 5];

foreach ($values as $value) {
    switch ($value) {
        case 3:
            continue 2;
    }
    echo "Processing {$value}" . PHP_EOL;
}

Output:

Processing 1
Processing 2
Processing 4
Processing 5

Best Practices

  • When a switch sits inside a loop and you need to affect the loop (not just the switch), always double-check whether you need break 2; or continue 2; instead of the unqualified form.
  • Avoid relying on levels higher than 2 or 3 — if your logic needs to unwind four levels of nested loops, that's usually a sign the logic belongs in its own function, where you can use return instead.
  • Prefer continue over wrapping the rest of a loop body in a large if (condition) { ... } block — an early continue keeps the 'happy path' unindented and easier to read.
  • Use break to stop search-style loops as soon as the answer is found, rather than adding a flag variable that you check every iteration.
  • Comment any break n; or continue n; where the level isn't immediately obvious from the surrounding code — future readers (including you) shouldn't have to count braces to understand the jump target.
  • Never pass a variable as the level argument — PHP requires a literal integer because the jump target is resolved at compile time, and a variable there is a fatal error.

Practice Exercises

  • Write a script with a for loop from 1 to 30 that prints every number that is divisible by 3 but skips (using continue) any number that is also divisible by 5, so 15 and 30 never get printed.
  • Given a two-dimensional array representing a seating chart (rows of seat labels, some of which are the string 'reserved'), write nested loops that use break 2 to stop as soon as the first non-reserved seat is found, and print its row and column.
  • Write a loop over an array of user roles that uses a switch inside a foreach. For the role 'banned', the loop should stop processing entirely; for the role 'guest', it should skip to the next user without printing anything; for every other role, print a welcome message. Decide for each case whether you need break, break 2, continue, or continue 2.

Summary

  • break exits a loop (or switch) immediately; continue skips the rest of the current iteration and moves on to the next one.
  • Both accept an optional integer level (break 2;, continue 3;) to affect an outer loop instead of the innermost one; the level must be a literal integer known at compile time.
  • switch counts as one breakable level, which means a bare break or continue inside a switch nested in a loop only affects the switch — a classic source of bugs.
  • In a for loop, continue still runs the increment expression before re-checking the condition.
  • Use these statements to keep loop bodies flat and readable, but keep nesting levels shallow — deeply nested break n/continue n logic is a sign to refactor into a function.