switch and match

When one value decides between many outcomes, PHP offers two constructs. The classic switch statement runs code for a matching case. The newer match expression (PHP 8.0) returns a value, compares strictly, and refuses to silently ignore unexpected values — it's usually the better choice.

The switch statement

PHP
<?php

$day = 3;

switch ($day) {
    case 1:
        echo "Monday\n";
        break;
    case 2:
        echo "Tuesday\n";
        break;
    case 3:
        echo "Wednesday\n";
        break;
    default:
        echo "Another day\n";
}
Output
Wednesday

switch compares the value with each case and starts running at the first match. default handles everything else.

Fall-through

Without break, execution falls through into the next case and keeps going. Sometimes that's useful — listing several cases that share code — but forgetting a break is a classic bug:

PHP
<?php

function season(string $month): string
{
    switch ($month) {
        case "Dec":
        case "Jan":
        case "Feb":
            return "winter";
        case "Jun":
        case "Jul":
        case "Aug":
            return "summer";
        default:
            return "spring or autumn";
    }
}

echo season("Jan"), "\n";

$level = 1;
switch ($level) {
    case 1:
        echo "Beginner\n";      // forgot break!
    case 2:
        echo "Intermediate\n";
        break;
}
Output
winter
Beginner
Intermediate

Inside a function, return ends the case just as well as break.

switch compares loosely

switch uses ==, so type juggling applies — another source of surprises:

PHP
<?php

$input = "1";
switch ($input) {
    case 1:
        echo "matched the integer 1\n";
        break;
    case "1":
        echo "matched the string \"1\"\n";
        break;
}
Output
matched the integer 1

The match expression

match compares a value against arms and returns the result of the first match. Each arm is conditions => result, separated by commas:

PHP
<?php

foreach ([1, 6, 7, 3] as $day) {
    $kind = match ($day) {
        6, 7 => "weekend",
        1, 2, 3, 4, 5 => "weekday",
    };
    echo "$day: $kind\n";
}
Output
1: weekday
6: weekend
7: weekend
3: weekday

Compared with switch, match:

  • is an expression — it produces a value you can assign, return or echo;
  • uses strict comparison (===);
  • has no fall-through and needs no break;
  • throws an error for unhandled values instead of quietly doing nothing.
PHP
<?php

$status = 404;

echo match ($status) {
    200 => "OK",
    301, 302 => "Redirect",
    404 => "Not found",
    default => "Unknown status",
}, "\n";

echo match ("1") {
    1 => "integer",
    "1" => "string",
}, "\n";

echo match (418) {
    200 => "OK",
    404 => "Not found",
};
Output
Not found
string

Fatal error: Uncaught UnhandledMatchError: Unhandled match case 418 in main.php:17
Stack trace:
#0 {main}
  thrown in main.php on line 17

That last error is a feature: if a new status code appears that your code doesn't know about, you find out immediately instead of getting silent wrong behaviour. Add a default arm when there genuinely is a sensible fallback.

match(true) for ranges

match compares values, so ranges need a trick: match against true, and write a condition in each arm. The first condition that's true wins:

PHP
<?php

foreach ([-5, 8, 19, 27, 38] as $t) {
    $feel = match (true) {
        $t < 0 => "freezing",
        $t < 15 => "cold",
        $t < 25 => "mild",
        $t <= 35 => "warm",
        default => "dangerously hot",
    };
    echo "$t °C: $feel\n";
}
Output
-5 °C: freezing
8 °C: cold
19 °C: mild
27 °C: warm
38 °C: dangerously hot

Arms can run code

An arm's result can be any expression — a function call, a calculation, even a throw:

PHP
<?php

function price(string $size): float
{
    return match ($size) {
        'S' => 2.40,
        'M' => 2.90,
        'L' => round(2.90 * 1.15, 2),
        default => throw new InvalidArgumentException("Unknown size '$size'"),
    };
}

echo price('M'), "\n";
echo price('L'), "\n";
echo price('XL'), "\n";
Output
2.9
3.33

Fatal error: Uncaught InvalidArgumentException: Unknown size 'XL' in main.php:9
Stack trace:
#0 main.php(15): price('XL')
#1 {main}
  thrown in main.php on line 9

For a fixed set of values like sizes, an enum (lesson 40) combined with match is even better.

Which one to use?

  • match — when you're choosing a value. This covers most cases.
  • switch — when several cases need to run multiple statements, or you deliberately want fall-through.
  • if/elseif — when the conditions test different things.

Try it yourself

Turn HTTP status codes into categories with match(true). Add a category for redirects (300–399).

Try it yourself · main.php
Edit the code, copy it into main.php, then run it with php main.php (PHP 8.5). How to install PHP
Output
200: success
201: success
404: client error
503: server error
99: unknown