In PHP 8, what are the differences between a switch statement and a match expression?
answer
- statement vs expression that returns
- == vs ===
- fall-through needs break; match never falls
- no arm matched: UnhandledMatchError
- comma-separated conditions, single expression per arm
basics
~20 sswitch is a statement that compares loosely (==) and falls through until a break. match, since PHP 8.0, is an expression that compares strictly (===), never falls through, returns a value and throws UnhandledMatchError when nothing matches.
solid answer
~40 s`switch` is a **statement**: it compares its subject with each `case` using **loose `==`**, runs from the first matching case onward and keeps **falling through** into later cases until a `break`. With no matching case and no `default`, it silently does nothing. `match`, available since **PHP 8.0**, is an **expression** that evaluates to a value, compares with **strict `===`**, runs exactly one arm with no fall-through, and throws **`UnhandledMatchError`** when nothing matches and there is no `default`. Each `match` arm is a single expression, several conditions share one arm with commas (`2, 3 => 'In transit'`), and conditions are evaluated lazily, top to bottom. Use `match` to map a value to a result, such as a parcel status code to a label; keep `switch` for running several statements per case.
code
php · 25 lines<?php
declare(strict_types=1);
$statusCode = 3;
switch ($statusCode) { // loose ==, statement
case 1:
$label = 'Label created';
break;
case 2:
case 3: // 2 and 3 share a body by falling through
$label = 'In transit';
break;
default:
$label = 'Unknown';
}
$label = match ($statusCode) { // strict ===, expression
1 => 'Label created',
2, 3 => 'In transit',
4 => 'Delivered',
default => 'Unknown',
};
echo $label, PHP_EOL; // In transitgo deeper
Know that match returns a value, compares strictly with ===, needs no break and throws UnhandledMatchError when nothing matches.
Explain fall-through in switch, lazy top-to-bottom evaluation of match conditions, comma-separated conditions and the match (true) idiom for ranges.
Judge where the stricter comparison and exhaustiveness of match catch real bugs, such as unmapped status codes, and where input types must be normalised first.
Set a codebase convention on when to use match, switch or polymorphism for branching on codes, weighing exhaustiveness errors against silent defaults.
## Two constructs for the same shape of problem Both `switch` and `match` take a **subject** value and compare it against a list of alternatives. That is where the similarity ends. `switch` is PHP's classic C-style statement; `match`, added in **PHP 8.0**, was designed to remove the traps of `switch` for the common case of "turn this value into that value". ## Side-by-side | Aspect | `switch` | `match` | |---|---|---| | Kind | statement | expression, evaluates to a value | | Comparison | loose `==` | strict `===` | | After a match | falls through to later cases until `break` | runs one arm only | | Nothing matches, no `default` | does nothing | throws `UnhandledMatchError` | | Arm body | any number of statements | a single expression | | Several values, one body | stacked `case 2: case 3:` | `2, 3 =>` | | Available since | always | PHP 8.0 | ## Loose versus strict comparison `switch` compares with `==`, so PHP's type-juggling rules apply. A string `"1"` from a query parameter matches `case 1:`; two numeric strings compare as numbers, so `"1"` even matches `case "01":` first if that case comes earlier. `match` compares with `===`: `"1"` does not match the arm `1 =>`. That makes `match` predictable, and it also means the types of your data must be right before you match. ## Fall-through In a `switch`, execution starts at the matching `case` and continues through every following `case` body until it hits `break` (or `return`, `continue`, `throw`). Forgetting a `break` is a classic bug: status 1 prints "Label created" *and* "In transit". Fall-through is sometimes deliberate, to share one body between cases, but it must be read carefully. `match` has no fall-through at all; shared bodies are written with commas. ## Exhaustiveness A `match` must handle its subject. If no condition is identical to the subject and there is no `default` arm, PHP throws **`UnhandledMatchError`**, a subclass of `Error`, with a message such as `Unhandled match case 5`. A `switch` without a `default` simply skips everything. The error is a feature: a new parcel status that nobody mapped fails loudly instead of producing an empty label. ## Expression semantics Because `match` is an expression, it is assigned or returned directly: ```php $label = match ($statusCode) { 1 => 'Label created', 2, 3 => 'In transit', 4 => 'Delivered', default => 'Unknown', }; ``` A few rules follow from being an expression: - Each arm holds **one expression**. For several steps, call a function, or use a `throw` expression (PHP 8.0 made `throw` an expression) to fail in an arm. - Conditions are evaluated **in order and lazily**: a condition is only evaluated if every earlier one failed, and only the winning arm's result is evaluated. - A `match` used as a statement on its own still needs a terminating semicolon. - Two `default` arms are a compile error. ## Ranges and other non-identity checks For conditions that are not "equals this value", the idiom is `match (true)` with boolean conditions: ```php $band = match (true) { $weightKg < 2 => 'small', $weightKg < 10 => 'medium', default => 'large', }; ``` The first arm whose condition is identical to `true` wins. With `switch (true)` the same trick works but compares loosely, so a truthy non-boolean case expression would also match. ## Choosing between them 1. Mapping a value to a value, such as a status code to a label: **`match`**. 2. Several statements per branch, or deliberate shared fall-through: **`switch`**, or `if`/`elseif`. 3. Input whose type is uncertain (strings from a request or a database): cast it first, then `match`; do not rely on `switch`'s loose comparison to paper over types. A PHP 8.5 detail on the old form: ending a `case` label with a semicolon (`case 1;`) instead of a colon was always accepted and is now deprecated.
- What happens in PHP when a switch case has no break?Execution falls through: after the matching case's statements, PHP keeps running the next case bodies, without testing their labels, until it reaches a `break`, `return`, `continue`, `throw` or the end of the switch. That is useful for deliberately sharing a body, but an accidental missing `break` runs code meant for other values.
- Can a match arm run several statements?Not directly: an arm's right-hand side is a single expression. Put the steps in a function or method and call it from the arm, or use an immediately invoked closure. To fail inside an arm, use a `throw` expression, which PHP 8.0 made legal in expression position.
- How do you express a range check such as weight below 2 kg with match?Use `match (true)` and write boolean conditions as the arms: `$weight < 2 => 'small', $weight < 10 => 'medium', default => 'large'`. The first condition identical to `true` wins. Because the comparison is strict, a condition must evaluate to real `true`, not just a truthy value.
saying these in an interview costs you the question
- Says match compares with == just like switch does.
- Believes a switch without a default throws an error when nothing matches.
- Claims match arms fall through to the next arm without a break.
- Thinks match is available in PHP 7.4.
- Says a match expression without default returns null when no arm matches.