Web Analytics

PHP Loop Control

Beginner~20 min read

Sometimes you need to exit a loop early or skip certain iterations. PHP provides break and continue statements for precise control over loop execution, including the ability to break out of multiple nested loops at once.

Break Statement

break immediately exits the loop, skipping all remaining iterations:

<?php
// Find first match and stop
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

foreach ($numbers as $num) {
    if ($num === 5) {
        echo "Found 5!";
        break;  // Exit loop immediately
    }
    echo "$num ";
}
// Output: 1 2 3 4 Found 5!
?>
Statement Effect
break Exit the current loop immediately
break 1 Same as break
break 2 Exit two nested loops
break n Exit n levels of nesting
Output
Click Run to execute your code

Continue Statement

continue skips the rest of the current iteration and moves to the next:

<?php
// Print only odd numbers
for ($i = 1; $i <= 10; $i++) {
    if ($i % 2 === 0) {
        continue;  // Skip even numbers
    }
    echo "$i ";
}
// Output: 1 3 5 7 9

// Skip specific values
$items = ["apple", "SKIP", "banana", "SKIP", "cherry"];
foreach ($items as $item) {
    if ($item === "SKIP") {
        continue;
    }
    echo "$item ";
}
// Output: apple banana cherry
?>
Difference:
  • break = "I'm done with this loop entirely"
  • continue = "Skip this one, try the next"

Break in Nested Loops

By default, break only exits the innermost loop. Use levels to break out of multiple loops:

<?php
// Without level - only exits inner loop
for ($i = 1; $i <= 3; $i++) {
    for ($j = 1; $j <= 3; $j++) {
        if ($j === 2) {
            break;  // Only exits inner loop
        }
        echo "($i,$j) ";
    }
    echo "| ";
}
// Output: (1,1) | (2,1) | (3,1) |

// With level 2 - exits both loops
for ($i = 1; $i <= 3; $i++) {
    for ($j = 1; $j <= 3; $j++) {
        if ($i === 2 && $j === 2) {
            echo "Breaking out completely!";
            break 2;  // Exits BOTH loops
        }
        echo "($i,$j) ";
    }
}
// Output: (1,1) (1,2) (1,3) (2,1) Breaking out completely!
?>

Break and Continue Levels

Both statements accept a level number for nested loop control:

Output
Click Run to execute your code

Continue with Levels

<?php
// Skip entire row if condition met
$data = [
    [1, 2, 3],
    [4, -1, 6],  // Contains negative
    [7, 8, 9]
];

foreach ($data as $row) {
    foreach ($row as $val) {
        if ($val < 0) {
            echo "(skip row) ";
            continue 2;  // Skip to next outer iteration
        }
        echo "$val ";
    }
    echo "| ";
}
// Output: 1 2 3 | (skip row) 7 8 9 |
?>

Break in Switch Within Loop

Remember: break in a switch only exits the switch, not the surrounding loop:

<?php
$commands = ["run", "stop", "run", "exit"];

foreach ($commands as $cmd) {
    switch ($cmd) {
        case "run":
            echo "Running... ";
            break;  // Exits switch only, loop continues
        case "stop":
            echo "Stopped. ";
            break;  // Exits switch only
        case "exit":
            echo "Exiting!";
            break 2;  // Exits switch AND foreach loop
    }
}
// Output: Running... Stopped. Running... Exiting!
?>
Important: Using high level numbers (like break 5) is a code smell. If you need to break out of many nested loops, consider refactoring into a function and using return instead.

Practical Patterns

Early Exit Pattern

<?php
function findUser($users, $id) {
    foreach ($users as $user) {
        if ($user['id'] === $id) {
            return $user;  // Found! Exit function
        }
    }
    return null;  // Not found
}
?>

Skip Invalid Items

<?php
$items = [10, null, 20, "", 30, false, 40];
$sum = 0;

foreach ($items as $item) {
    if (!is_numeric($item)) {
        continue;  // Skip non-numeric values
    }
    $sum += $item;
}
echo "Sum: $sum";  // Sum: 100
?>

Limited Processing

<?php
$records = getLotsOfRecords();
$processed = 0;
$limit = 100;

foreach ($records as $record) {
    processRecord($record);
    $processed++;

    if ($processed >= $limit) {
        echo "Processed $limit records. Stopping.";
        break;
    }
}
?>

Common Mistakes

1. Using break instead of continue

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

// โŒ WRONG: Stops at first even number
foreach ($numbers as $num) {
    if ($num % 2 === 0) {
        break;  // Exits entire loop!
    }
    echo $num;
}
// Output: 1

// โœ… CORRECT: Skips even numbers, continues loop
foreach ($numbers as $num) {
    if ($num % 2 === 0) {
        continue;  // Skip to next iteration
    }
    echo $num;
}
// Output: 135
?>

2. Wrong level number

<?php
// โŒ WRONG: break 3 when only 2 loops exist
for ($i = 0; $i < 3; $i++) {
    for ($j = 0; $j < 3; $j++) {
        if ($condition) {
            break 3;  // Error: Cannot break 3 levels!
        }
    }
}

// โœ… CORRECT: Use appropriate level
for ($i = 0; $i < 3; $i++) {
    for ($j = 0; $j < 3; $j++) {
        if ($condition) {
            break 2;  // Exit both loops
        }
    }
}
?>

3. Forgetting switch counts as a level

<?php
// โŒ WRONG: break 1 only exits switch
foreach ($items as $item) {
    switch ($item) {
        case "exit":
            break;  // Only exits switch!
    }
    echo "Still in loop\n";  // This runs!
}

// โœ… CORRECT: break 2 exits switch AND loop
foreach ($items as $item) {
    switch ($item) {
        case "exit":
            break 2;  // Exits switch and foreach
    }
}
?>

Exercise: Data Validator

Task: Process a batch of user registrations with validation.

Requirements:

  • Skip users with empty email
  • Skip users under 18 years old
  • Stop processing if you find a banned email domain
  • Count valid registrations
Show Solution
<?php
$registrations = [
    ["name" => "Alice", "email" => "[email protected]", "age" => 25],
    ["name" => "Bob", "email" => "", "age" => 30],  // Empty email
    ["name" => "Carol", "email" => "[email protected]", "age" => 16],  // Under 18
    ["name" => "David", "email" => "[email protected]", "age" => 28],  // Banned domain!
    ["name" => "Eve", "email" => "[email protected]", "age" => 22]
];

$bannedDomains = ["spam.com", "fake.com", "temp.com"];
$validCount = 0;

foreach ($registrations as $user) {
    // Skip empty email
    if (empty($user['email'])) {
        echo "Skipping {$user['name']}: No email\n";
        continue;
    }

    // Skip underage
    if ($user['age'] < 18) {
        echo "Skipping {$user['name']}: Under 18\n";
        continue;
    }

    // Check for banned domain
    $emailDomain = substr($user['email'], strpos($user['email'], '@') + 1);
    if (in_array($emailDomain, $bannedDomains)) {
        echo "ALERT: Banned domain detected ({$emailDomain})!\n";
        echo "Stopping all processing.\n";
        break;
    }

    // Valid registration
    echo "Registered: {$user['name']} ({$user['email']})\n";
    $validCount++;
}

echo "\nTotal valid registrations: $validCount\n";
?>

Summary

  • break: Exit the loop completely
  • continue: Skip current iteration, continue to next
  • break n: Exit n levels of nested loops
  • continue n: Skip to next iteration of nth outer loop
  • switch: Counts as a level for break/continue
  • Best practice: Avoid high level numbers; refactor instead
  • Alternative: Use return in functions for cleaner exits

What's Next?

Congratulations! You've mastered PHP control structures - conditions, switches, and all types of loops! Next, we'll learn about Functions - reusable blocks of code that make your programs modular and maintainable.