0

switch vs match — exhaustiveness, strict comparison

Beginner5 min read·php-05-002
interview

Concept

switch and match both dispatch on a value, but they have fundamentally different semantics. Choosing the wrong one introduces bugs that are difficult to spot.

switch uses loose comparison (==) — the same type-juggling rules as if. switch (0) matches case "foo" because 0 == "foo" is true in PHP (string coerced to 0). switch also supports fall-through: execution continues into subsequent cases unless you explicitly break. This is occasionally useful but usually a bug.

match (PHP 8.0) uses strict comparison (===) — no type coercion, no fall-through. match is also an expression (returns a value), and it throws \UnhandledMatchError if no arm matches and there's no default arm. Multiple match values can share an arm: match($x) { 1, 2 => 'low', ... }.

Exhaustiveness: switch silently does nothing if no case matches (falls through to after the switch). match throws an exception — forcing you to handle every case or add default. This makes match safer for dispatch logic.

Performance: Both use hash-based jump tables internally for integer/string values — effectively O(1). The practical difference is in correctness, not speed.

Code Example

php
<?php
declare(strict_types=1);

// switch — loose comparison (dangerous)
$value = 0;
switch ($value) {
    case "hello": // 0 == "hello" is TRUE due to type coercion!
        echo "This runs unexpectedly!\n";
        break;
    case 0:
        echo "Zero\n";
        break;
}

// Fall-through (intentional)
$day = 'Saturday';
switch ($day) {
    case 'Saturday':
    case 'Sunday':
        echo "Weekend\n"; // both days fall through to here
        break;
    default:
        echo "Weekday\n";
}

// match — strict comparison, no fall-through, expression
$status = 1;
$label = match($status) {
    0     => 'inactive',
    1     => 'active',
    2     => 'suspended',
    default => 'unknown',
};
echo $label; // "active"

// match with multiple values per arm
$lang = 'en';
$greeting = match($lang) {
    'en', 'en-US', 'en-GB' => 'Hello',
    'fr', 'fr-FR'           => 'Bonjour',
    'de'                    => 'Hallo',
    'ro'                    => 'Bună',
    default                 => 'Hi',
};

// match throws on unhandled value
$code = 999;
try {
    $text = match($code) {
        200 => 'OK',
        404 => 'Not Found',
        500 => 'Server Error',
        // 999 not handled, no default → throws
    };
} catch (\UnhandledMatchError $e) {
    echo "No match for code $code\n";
}

// The string "0" gotcha in switch
switch ("0") {
    case false: echo "Matches false!\n"; break; // switch("0") == false → TRUE
    case "0":   echo "Matches string zero\n"; break;
}
// match("0") { false => ..., "0" => ... } would correctly match "0"