PHP 8 · Intermediate
PHP match vs switch: five differences that change behaviour
Compare PHP match and switch where it matters: strictness, return values, fall-through, exhaustiveness, multiple conditions, and migration traps.
PHP 8 introduced match as an expression for selecting a value. It looks similar to switch, but replacing one with the other mechanically can change the program.
Here is the kind of mapping match handles well:
<?php
$message = match ($status) {
'draft' => 'Keep working',
'review' => 'Waiting for review',
'published' => 'Live',
default => 'Unknown status',
};
The comparable switch needs assignment and breaks:
switch ($status) {
case 'draft':
$message = 'Keep working';
break;
case 'review':
$message = 'Waiting for review';
break;
case 'published':
$message = 'Live';
break;
default:
$message = 'Unknown status';
}
The shorter syntax is useful, but the semantic differences are the real reason to choose carefully.
1. match compares strictly
match behaves as if it used ===. A string and an integer with the same visible characters are different:
$input = '1';
$type = match ($input) {
1 => 'integer',
'1' => 'string',
};
echo $type; // string
switch uses loose comparison for case matching:
$input = '1';
switch ($input) {
case 1:
echo 'integer case'; // runs
break;
case '1':
echo 'string case';
break;
}
The first case wins because '1' == 1. Moving old input-sensitive code from switch to match may reveal data that was relying on coercion. That is often a useful correction, but it needs tests.
2. match returns a value
A match arm is an expression, and the complete construct produces a result:
$httpStatus = match ($result) {
Result::Created => 201,
Result::Accepted => 202,
Result::Invalid => 422,
};
You can return it directly:
return match ($role) {
'owner' => Permission::Admin,
'editor' => Permission::Write,
default => Permission::Read,
};
A switch is a statement. It controls which block runs but does not itself yield a value.
Each match arm accepts one expression. If a case needs several procedural statements, call a function that owns those steps or use a switch/if block.
3. match never falls through
A matching arm completes the expression. There is no break to remember:
$label = match ($code) {
200, 201, 204 => 'success',
400, 404 => 'client error',
500, 503 => 'server error',
};
Comma-separated conditions share an arm. In switch, omitting break continues into later case bodies. That can express intentional cumulative behaviour, but it is also a common source of accidental results.
If you truly need fall-through, switch is the direct construct. If you only need several inputs to share an answer, match lists them safely on one arm.
4. An unmatched match throws
Without a matching arm or default, PHP throws UnhandledMatchError:
$label = match ('archived') {
'draft' => 'Draft',
'published' => 'Published',
};
That exhaustiveness can protect code built around enums or a closed state set. When a new enum case is added, an unhandled path fails visibly rather than drifting through with a missing value.
A switch with no matching case simply does nothing. Add default when unknown inputs have a legitimate fallback:
$label = match ($status) {
'draft' => 'Draft',
'published' => 'Published',
default => 'Unknown',
};
Do not add default automatically. For a domain enum, forcing every case to be named may be exactly the safety you want.
5. match (true) can handle ranges
Because arms are strict comparisons, matching against true supports ordered boolean conditions:
$band = match (true) {
$score >= 90 => 'excellent',
$score >= 70 => 'good',
$score >= 50 => 'pass',
default => 'not yet',
};
This is compact when each branch returns a value. The first true arm wins, so put higher thresholds first.
An if / elseif chain may still be clearer to teammates unfamiliar with the pattern, especially when conditions do more than calculate one value.
Which one should new code use?
Use match when the code:
- maps one input to one returned value;
- benefits from strict comparison;
- should not fall through;
- should fail when a closed set gains an unhandled case.
Use switch when the code:
- intentionally relies on fall-through;
- runs multi-statement procedural case bodies;
- must preserve legacy loose-matching behaviour while being carefully maintained.
For ranges or unrelated boolean questions, compare both with a straightforward if / elseif chain. Modern syntax is useful when it makes the program’s contract more explicit, not merely because it is shorter.
The definitive language rules are in the PHP manual for match expressions.
Every example was executed with PHP 8.4.4, including the loose switch comparison and unhandled match case. Semantics were checked against the PHP match expression manual.