match() Default Arm vs No Default
debt(d3/e1/b3/t5)
Closest to 'default linter catches the common case' (d3). PHPStan and Psalm (cited in detection_hints.tools) can detect missing match arms, especially when used with enums or union types. These are commonly configured static analysis tools that catch the pattern automatically.
Closest to 'one-line patch or single-call swap' (e1). The quick_fix explicitly states: 'Always add a default arm to match()' — this is literally adding one line like 'default => throw new \UnexpectedValueException()' to the existing match expression.
Closest to 'localised tax' (b3). The choice affects individual match expressions within specific functions. While applies_to shows it's relevant across web/cli/queue contexts, each match() is isolated and doesn't impose structural weight on the broader codebase architecture.
Closest to 'notable trap (a documented gotcha most devs eventually learn)' (t5). The misconception field directly states developers assume 'match() without default silently returns null like switch' when it actually throws UnhandledMatchError. This contradicts expectations from switch behavior, but it's a well-documented PHP 8 gotcha that developers learn through experience.
TL;DR
Explanation
match() is exhaustive by design. When no arm matches and there is no default, PHP throws \UnhandledMatchError (extends \Error). This is intentional — match() forces you to handle all cases explicitly. Add default => throw new \InvalidArgumentException() for expected-but-invalid values, or default => null when missing is acceptable. Use with enums: PHP will warn if match on a backed enum is non-exhaustive in a future version. Contrast with switch: switch falls through silently, match throws.
Common Misconception
Why It Matters
Common Mistakes
- Forgetting default arm when new enum values are added.
- Using match() for nullable values without handling null explicitly.
- Catching generic \Error instead of \UnhandledMatchError.
Code Examples
$status = match($code) {
200 => 'ok',
404 => 'not found',
// No default — code 500 throws UnhandledMatchError
};
$status = match($code) {
200 => 'ok',
404 => 'not found',
500, 503 => 'server error',
default => throw new \InvalidArgumentException("Unknown HTTP code: $code"),
};