01 · THE IDEAREAD → TRY → LEARN
Why match exists
A match expression chooses one result from several alternatives. It looks a little like switch, but it behaves more like a compact, safer lookup. The whole construct evaluates to a value, so you can assign it, return it, or pass it straight to echo.
The basic shape is a subject in parentheses followed by arms. Each arm has one or more conditions, then =>, then the expression to return. Use a comma to list several conditions that share one result. A standalone match ends with a semicolon, as does an assignment or return that uses it.
Strict comparison
The most important rule is that match compares with identity (===), not loose equality (==). The integer 1 and the string '1' are different subjects. That removes a whole class of surprises that switch can produce when values of different types compare as equal.
$code = '1';
$label = match ($code) {
1 => 'integer one',
'1' => 'string one',
default => 'other',
};
echo $label, "\n";string one
The subject is the string '1', so only the second arm matches. The default arm catches anything not matched earlier. Only the chosen result expression runs, and there is no fallthrough, so you never need break.
Being exhaustive
A match must handle every subject it can receive. If no arm matches and there is no default, PHP throws UnhandledMatchError. This is intentional: a forgotten case becomes a visible failure instead of a silent null.
$value = 5;
try {
echo match ($value) {
1, 2 => 'low',
3, 4 => 'mid',
}, "\n";
} catch (UnhandledMatchError $e) {
echo $e->getMessage(), "\n";
}Unhandled match case 5
Choose deliberately between the two designs. Add default when unexpected input should produce a fallback result. Omit it when unexpected input is a bug that should be loud.
Matching conditions with true
Because arms are compared strictly with the subject, you can use true as the subject and write boolean conditions in the arms. PHP tests the arms from top to bottom and stops at the first one that evaluates to exactly true. This is useful for ranges.
function gradeBand(int $score): string
{
return match (true) {
$score >= 90 => 'A',
$score >= 80 => 'B',
$score >= 70 => 'C',
default => 'review',
};
}
echo json_encode([gradeBand(95), gradeBand(80), gradeBand(12)], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";["A","B","review"]
Order matters here. A score of 95 satisfies all three comparisons, but the first arm wins. Put the most specific or highest threshold first. Each condition must be a real boolean expression; a merely truthy value such as 1 would not match true strictly.
Worked reasoning
In gradeBand(80), PHP checks 80 >= 90, which is false, so it moves on. Next 80 >= 80 is true, so the result is 'B' and the later arms are never evaluated. For 12, every comparison fails and default returns 'review'.
This lazy evaluation also applies to the conditions themselves. If an arm contains a function call, it is not made when an earlier arm has already matched.
Common mistakes
- Expecting loose matching, such as treating
'404'like404. - Forgetting
defaultwhen the input comes from outside your code. - Using
match (true)with conditions in the wrong order. - Putting statements in an arm. Each result must be a single expression.
- Forgetting the semicolon after the closing brace.
Exercise contract
Write statusLabel($code): string. It receives a JSON value and returns one of four labels, using strict matching:
200returnssuccess.404returnsnot-found.500or503returnsserver-error.- Anything else returns
unknown. This includes numeric strings such as"404"andnull.
Return the label rather than printing it. The tests compare the returned strings, including the case where a string looks like a valid integer.
Recap
match returns a value, compares with ===, never falls through, and throws UnhandledMatchError when nothing matches. Use commas to share a result, default for a fallback, and match (true) for ordered range checks.
Functions · Arrays · Types
- Use exhaustive strict matching with a fallback
- Implement and test a deterministic PHP 8.4 function