PHPswitch Statement

switch Statement

A switch statement takes a single value and compares it against a list of possible matches, running the block of code attached to whichever case matches first. It exists to replace long chains of if / elseif / elseif ... that all test the same variable — once you have four or five branches checking the same thing, switch reads more clearly than a stack of elseifs. Under the hood it is not magic: PHP walks the case labels from top to bottom and stops at the first one that matches, so a switch is really just sugar over sequential comparisons with a couple of important quirks you need to know about.

Basic syntax

A switch block opens with the value being tested, followed by one or more case labels and an optional default label for anything that does not match. Each case should normally end with a break so execution does not continue into the next case.

A simple switch

PHP
<?php
$day = "Tuesday";

switch ($day) {
    case "Monday":
        echo "Start of the work week.";
        break;
    case "Tuesday":
        echo "Second day in.";
        break;
    case "Saturday":
    case "Sunday":
        echo "Weekend!";
        break;
    default:
        echo "Just another day.";
        break;
}
Second day in.

Notice "Saturday" and "Sunday" share the same block above — that stacked-case pattern is covered in detail further down.

Why break matters: fall-through

If you omit break, PHP does not stop after running the matched case's code — it keeps executing every statement in the cases below it, regardless of whether they match, until it hits a break or reaches the end of the switch block. This is called fall-through, and it is one of the most common sources of bugs in switch statements written by developers coming from languages that auto-break (or by anyone who simply forgot the keyword).

Accidental fall-through (a bug)

PHP
<?php
$level = "warning";

switch ($level) {
    case "error":
        echo "Critical! ";
        // no break here
    case "warning":
        echo "Please review. ";
        // no break here either
    case "info":
        echo "Logged.";
        break;
}
Please review. Logged.

Because $level matched "warning", execution starts there — but without a break, it also runs the "info" block. In this example that happens to produce readable output, but imagine the same pattern applied to something like applying a discount or setting a permission level: silently running every case below the match is rarely what you want.

Missing break is a silent bug, not a syntax error
PHP will not warn you if a `case` is missing a `break` — the code runs and produces output, it is just the *wrong* output. Always double check that every `case` block ends in `break`, `return`, `continue`, or `throw` unless the fall-through is intentional. Static analysis tools and linters can catch this, but it is worth training your eye to spot it during code review.
Intentional fall-through

Sometimes you want several cases to run the same trailing logic — for example, treating multiple related statuses as needing the same follow-up action after their own specific handling. Intentional fall-through is fine as long as it is obvious and, ideally, commented so the next reader knows it was not a mistake.

Fall-through used on purpose

PHP
<?php
$status = "shipped";

switch ($status) {
    case "shipped":
        echo "Package is on its way. ";
        // fall through intentionally: shipped and delivered both notify
    case "delivered":
        echo "Customer will be notified.";
        break;
    case "cancelled":
        echo "Order was cancelled.";
        break;
}
Package is on its way. Customer will be notified.
Grouping cases (stacked case labels)

When several distinct values should all run the exact same block, you can "stack" multiple case labels with no code between them. Only the last label in the stack needs the actual logic and its break — the labels above it just fall through to it, which in this specific pattern is completely intentional and idiomatic.

Grouping weekend days

PHP
<?php
function describeDay(string $day): string
{
    switch ($day) {
        case "Saturday":
        case "Sunday":
            return "Weekend";
        case "Monday":
        case "Tuesday":
        case "Wednesday":
        case "Thursday":
        case "Friday":
            return "Weekday";
        default:
            return "Unknown day";
    }
}

echo describeDay("Sunday") . "\n";
echo describeDay("Wednesday") . "\n";
echo describeDay("Someday") . "\n";
Weekend
Weekday
Unknown day

Using return inside a function's switch cases (as above) is a clean alternative to break — it exits the function immediately, so there is no risk of accidental fall-through at all.

Loose comparison: the real gotcha

This is the quirk that catches almost everyone at least once: switch compares the switched value against each case using loose comparison (==), the same rules PHP uses for == everywhere else. That means type juggling applies, and values that look completely different can be treated as equal.

A string matching an unexpected numeric case

PHP
<?php
$input = "0 items";

switch ($input) {
    case 0:
        echo "Matched the number zero!";
        break;
    default:
        echo "No match.";
        break;
}
Matched the number zero!

That output is almost certainly not what you expect from a string like "0 items". What happens is that PHP converts "0 items" to a number for the comparison against case 0, and the leading numeric portion of the string (0) makes the comparison true under == rules. The exact conversion behavior has shifted across PHP versions — PHP 8 tightened several string-to-number comparison rules compared to PHP 7 — but the underlying danger is the same: any loose comparison between a string and a number can produce surprising matches, and switch inherits that danger silently because there is no visual == in the code to remind you.

Confirming it with var_dump

PHP
<?php
var_dump("0 items" == 0);   // does this string equal the integer 0?
var_dump("abc" == 0);        // PHP 8: false (fixed from PHP 7's surprising true)
var_dump("0" == 0);          // true - "0" is numeric
var_dump("" == 0);           // false in PHP 8 for a plain switch/== comparison of empty string vs 0
bool(true)
bool(false)
bool(true)
bool(false)

The safe, reliable fix is to make the type explicit before the switch runs, either by casting the value or by switching on a clearly-typed expression, and to keep your case values the same type as whatever you are testing.

Avoiding the loose-comparison trap

PHP
<?php
$input = "0 items";
$numericPart = (int) $input; // 0, but now the intent is explicit

switch (true) {
    case $input === "0":
        echo "Exactly the string zero.";
        break;
    case $numericPart === 0 && $input !== "0":
        echo "Starts with zero but isn't the string \"0\".";
        break;
    default:
        echo "Something else entirely.";
        break;
}
Starts with zero but isn't the string "0".

That last example also shows a useful trick: switch (true) lets every case be a full boolean expression evaluated with strict comparison (===), which sidesteps the loose-comparison problem entirely at the cost of losing the simple "match a single value" readability that switch is best at.

default doesn't have to be last

Conventionally default is written as the final label, but PHP does not require that. Wherever it appears, default only runs when nothing above it matched — and just like any other case, it will fall through into whatever comes after it if it lacks a break. Placing it anywhere other than last is legal but tends to confuse readers, so it is best treated as a style rule: always put default last.

switch vs match
PHP 8 introduced the `match` expression as a stricter, more concise alternative to `switch`. `match` uses strict comparison (`===`) by default, has no fall-through at all, and returns a value directly instead of requiring assignment inside each branch. For new code that only needs simple value-to-value mapping, `match` avoids both problems described on this page. `switch` remains useful when you need multiple statements per branch, intentional fall-through, or are working in a codebase that predates PHP 8. Choosing between `switch`, `match`, and `if/elseif` mostly comes down to how many branches you have and whether you need loose or strict comparison.
  • switch evaluates the tested expression once, then compares it top-to-bottom against each case using ==.

  • Every case should end in break (or return/continue/throw) unless fall-through is intentional and commented.

  • Stacked case labels with no code between them are the idiomatic way to group values that share one block.

  • switch (true) combined with boolean case expressions is a common workaround for the loose-comparison pitfall.

Tip
Before writing a `switch`, ask whether every case is a plain one-to-one value match with no shared logic between branches. If so, prefer PHP 8's `match` expression — it is shorter, returns a value directly, and its strict, no-fall-through comparison rules remove the two biggest classes of `switch` bugs covered on this page.