PHP Switch

The switch statement is PHP’s tool for comparing one value against many possible cases without writing a long chain of if/elseif blocks. It evaluates a single expression once, then jumps to the matching case label and runs the code there. Once you understand how PHP compares values and how execution flows between cases, switch becomes one of the clearest ways to express “do one of these several things” logic.

Overview: How switch Works

A switch statement takes one expression and compares it, in order, against a list of case values using a loose equality comparison — the same rules as the == operator (not the strict ===). The very first case whose value loosely equals the switch expression “wins”, and execution begins at that point.

This is the detail that surprises the most beginners: switch does not re-evaluate the condition for every case the way an if/elseif chain does. It computes the switch expression exactly once, then walks the case labels top to bottom looking for the first loose match. Internally, the Zend Engine (PHP’s runtime) compiles the case labels into a sequence of comparison opcodes; when every case is a plain integer or string literal, the engine can optimize this into a hash-based jump table instead of comparing one by one, but the observable behavior is identical to “check cases from top to bottom, use the first match”.

After a match is found, PHP does not automatically stop after that case’s statements. It keeps executing every statement it encounters — including the code inside the next case blocks — until it hits a break, return, continue, throw, or the closing brace of the switch. This behavior is called fallthrough, and it is both switch’s most powerful feature (for intentionally grouping cases) and its most common source of bugs (when a break is forgotten).

Syntax

<?php
$expression = 2;

switch ($expression) {
    case 1:
        echo "Matched 1";
        break;
    case 2:
        echo "Matched 2";
        break;
    default:
        echo "No match";
}

Output:

Matched 2
Part Purpose
switch (expression) The value evaluated exactly once and compared against each case.
case value: A label PHP compares against the switch expression using loose (==) equality.
break; Stops fallthrough and exits the switch immediately.
default: Runs when no case matches. It can appear anywhere in the switch, but is only reached if nothing else matched.

PHP also supports an alternative colon syntax, useful when a switch is embedded in a template mixed with HTML:

<?php
$isLoggedIn = true;

switch ($isLoggedIn):
    case true:
        echo "Logged in";
        break;
    case false:
        echo "Logged out";
        break;
endswitch;

Output:

Logged in

Examples

Example 1: Basic switch on an integer

<?php
$dayNumber = 3;

switch ($dayNumber) {
    case 1:
        echo "Monday";
        break;
    case 2:
        echo "Tuesday";
        break;
    case 3:
        echo "Wednesday";
        break;
    case 4:
        echo "Thursday";
        break;
    case 5:
        echo "Friday";
        break;
    default:
        echo "Weekend or invalid day";
}

Output:

Wednesday

PHP checks $dayNumber == 1, then == 2, and so on, stopping at the first match (3). Every branch ends in break, so only one message ever prints.

Example 2: Intentional fallthrough to group cases

<?php
function seasonFromMonth(int $month): string
{
    switch ($month) {
        case 12:
        case 1:
        case 2:
            return "Winter";
        case 3:
        case 4:
        case 5:
            return "Spring";
        case 6:
        case 7:
        case 8:
            return "Summer";
        case 9:
        case 10:
        case 11:
            return "Autumn";
        default:
            return "Invalid month";
    }
}

echo seasonFromMonth(7) . PHP_EOL;
echo seasonFromMonth(12) . PHP_EOL;
echo seasonFromMonth(13) . PHP_EOL;

Output:

Summer
Winter
Invalid month

Stacking case 12:, case 1:, and case 2: with no statements between them is deliberate fallthrough: any of those three values runs the same return "Winter"; line. Because each branch uses return, no break is even needed — returning from the function exits the switch too.

Example 3: switch(true) for range matching

<?php
function describeStatus(int $code): string
{
    switch (true) {
        case $code >= 200 && $code < 300:
            $result = "Success";
            break;
        case $code >= 300 && $code < 400:
            $result = "Redirection";
            break;
        case $code >= 400 && $code < 500:
            $result = "Client Error";
            break;
        case $code >= 500 && $code < 600:
            $result = "Server Error";
            break;
        default:
            $result = "Unknown Status";
    }

    return "$code: $result";
}

$codes = [200, 301, 404, 500, 999];

foreach ($codes as $code) {
    echo describeStatus($code) . PHP_EOL;
}

Output:

200: Success
301: Redirection
404: Client Error
500: Server Error
999: Unknown Status

A plain switch can only test equality, not ranges. The switch (true) trick works around that: each case is itself a boolean expression, and since the switch value is literally true, the first case that evaluates to true is the one that matches. This reads almost like a cleaner if/elseif chain while keeping switch’s structure.

Under the Hood: Step by Step

When PHP executes a switch, it performs these steps in order:

  • Evaluate the switch expression exactly once and store the result.
  • Walk the case labels from top to bottom in source order (their order matters — it is not sorted or hashed by value in a way that changes results).
  • For each case, evaluate the case’s expression and compare it to the stored value with loose (==) equality.
  • On the first match, jump execution to that case’s first statement.
  • Keep executing statements — including sliding into the next case’s body — until a break, return, continue, or throw is reached, or the switch simply ends.
  • If nothing matched, jump to default if one exists (regardless of where it’s written in the source); otherwise do nothing.

One subtle gotcha involves continue. Because PHP treats switch as a loop-like structure for branching purposes, a bare continue inside a switch behaves exactly like break — it only exits the switch. To continue an enclosing loop from inside a switch, you must use continue 2:

<?php
for ($i = 1; $i <= 3; $i++) {
    switch ($i) {
        case 2:
            continue 2;
        default:
            echo "Number: $i" . PHP_EOL;
    }
}

Output:

Number: 1
Number: 3

When $i is 2, continue 2 skips straight to the for loop’s increment step, bypassing the default case entirely — that’s why 2 never prints.

Common Mistakes

Mistake 1: Forgetting break causes unintended fallthrough

<?php
$role = "editor";

switch ($role) {
    case "admin":
        echo "Full access granted" . PHP_EOL;
    case "editor":
        echo "Edit access granted" . PHP_EOL;
    case "viewer":
        echo "Read-only access granted" . PHP_EOL;
        break;
    default:
        echo "No access" . PHP_EOL;
}

Output:

Edit access granted
Read-only access granted

The "editor" case is missing a break, so after printing its own message execution slides straight into the "viewer" case and prints that too — even though the role is not “viewer”. Add the missing break:

<?php
$role = "editor";

switch ($role) {
    case "admin":
        echo "Full access granted" . PHP_EOL;
        break;
    case "editor":
        echo "Edit access granted" . PHP_EOL;
        break;
    case "viewer":
        echo "Read-only access granted" . PHP_EOL;
        break;
    default:
        echo "No access" . PHP_EOL;
}

Output:

Edit access granted

Mistake 2: Using boolean case expressions against the wrong switch value

<?php
$score = 0;

switch ($score) {
    case $score >= 90:
        echo "Grade: A";
        break;
    case $score >= 80:
        echo "Grade: B";
        break;
    case $score >= 70:
        echo "Grade: C";
        break;
    default:
        echo "Grade: F";
}

Output:

Grade: A

This looks like it tests ranges, but it doesn’t — the switch expression is $score, an integer, not true. Each case value ($score >= 90, etc.) evaluates to a boolean, and comparing an int to a bool converts the int to a bool first. Since $score is 0, (bool) 0 is false, and the very first case (0 >= 90, which is also false) matches by accident, wrongly reporting an A. The fix is to switch on true itself, as shown earlier:

<?php
$score = 0;

switch (true) {
    case $score >= 90:
        echo "Grade: A";
        break;
    case $score >= 80:
        echo "Grade: B";
        break;
    case $score >= 70:
        echo "Grade: C";
        break;
    default:
        echo "Grade: F";
}

Output:

Grade: F

Best Practices

  • Always end each case with break, return, continue N, or throw unless fallthrough is deliberate — and if it is, add a short // no break comment so the next reader knows it’s intentional.
  • Always include a default case, even if it just throws an exception for an unexpected value — silent “nothing happens” bugs are hard to trace.
  • Remember comparisons are loose (==), not strict. Be extra careful mixing types across cases (strings, integers, booleans).
  • Use switch (true) sparingly for range checks; if the conditions get complex, an if/elseif chain or PHP 8’s match expression is often clearer.
  • For PHP 8+, prefer match when every branch is a single expression you want to return or assign — it uses strict comparison and has no fallthrough, which eliminates both mistakes shown above. Reach for switch when branches need multiple statements or intentional case grouping.
  • Group related cases through fallthrough instead of duplicating the same code in several branches.
  • Keep each case body short; move nontrivial logic into a dedicated function so the switch itself stays easy to scan.

Practice Exercises

  • Write a function trafficLightAction(string $color): string that uses switch to return "Stop" for "red", "Slow down" for "yellow", "Go" for "green", and "Unknown color" for anything else.
  • Write a script that takes an integer score from 0 to 100 and uses switch (true) to echo a letter grade: A (90+), B (80-89), C (70-79), or F (below 70). Test it with a score of 0 to make sure it doesn’t fall into the boolean-comparison trap from this lesson.
  • Rewrite the seasonFromMonth() example from this lesson using an if/elseif chain instead of fallthrough grouping, then compare which version is easier to read.

Summary

  • switch evaluates its expression once and compares it against each case using loose (==) equality, top to bottom.
  • Without a break, execution falls through into the next case’s statements — useful when intentional, a bug when accidental.
  • default runs when nothing else matches, no matter where it appears in the switch.
  • The switch (true) pattern lets you match ranges or complex boolean conditions, but never switch on a real value while writing boolean case expressions.
  • continue inside a switch only exits the switch; use continue 2 to continue an enclosing loop.
  • For simple value-to-value mappings without fallthrough, consider PHP 8’s match expression instead.