Skip to content

Latest commit

 

History

History
133 lines (96 loc) · 4.1 KB

File metadata and controls

133 lines (96 loc) · 4.1 KB

Match Expression

The match expression, added in PHP 8.0, is a value-returning, strictly-compared alternative to switch.

Why match over switch

match fixes several long-standing footguns in switch:

  • It returns a value, so it can be assigned directly.
  • It uses strict === comparison, avoiding type-juggling surprises.
  • There is no fallthrough, so no break statements are needed.
  • An unmatched value with no default throws UnhandledMatchError instead of silently doing nothing.
<?php
$status = 404;

$message = match ($status) {
    200, 201, 204 => 'Success',   // multiple conditions per arm
    301, 302      => 'Redirect',
    404           => 'Not Found',
    500           => 'Server Error',
    default       => 'Unknown',
};

echo $message;   // Not Found

match vs switch

Aspect match switch
Comparison strict === loose ==
Returns a value yes no
Fallthrough none yes (needs break)
Body per branch single expression statements block
No match, no default throws UnhandledMatchError silently continues
Multiple values per arm 1, 2, 3 => stacked case labels

Strict comparison matters

Because match uses ===, "1" (string) does not match 1 (int). This prevents the type-confusion bugs common in switch.

<?php
$input = '1';   // string from $_GET

// switch: loose == means '1' matches case 1
switch ($input) {
    case 1:  echo "switch: matched int 1\n"; break;
    default: echo "switch: default\n";
}

// match: strict === means '1' does NOT match int 1
echo match ($input) {
    1       => "match: matched int 1\n",
    '1'     => "match: matched string '1'\n",
    default => "match: default\n",
};

Expected output:

switch: matched int 1
match: matched string '1'

Conditional arms with match (true)

Matching against true lets each arm hold a boolean expression, giving you if/elseif chains that return a value.

<?php
$score = 82;

$grade = match (true) {
    $score >= 90 => 'A',
    $score >= 80 => 'B',
    $score >= 70 => 'C',
    default      => 'F',
};

echo $grade;   // B

Handling the unmatched case

Never leave match without a default when the input can be attacker-controlled; an unexpected value would throw UnhandledMatchError. Either supply a default or catch the error to fail safely.

<?php
try {
    $role = match ($_GET['role'] ?? '') {
        'admin' => 'admin',
        'user'  => 'user',
    };
} catch (\UnhandledMatchError) {
    $role = 'guest';   // deny by default
}

Security Considerations

  • Strict === comparison removes the type-juggling foothold attackers exploit against loose switch (==), where request strings like '1', '0e123', or 'true' can collide with integers or booleans.
  • Always handle the unmatched case for attacker-controlled input — supply a default or catch UnhandledMatchError — and make that fallback deny by default (e.g. guest), never grant.
  • Because each arm is a single expression, avoid smuggling side-effectful or state-changing calls into arms; keep authorization decisions pure and auditable.

Summary

match is PHP 8.0's safer replacement for switch: it returns a value, compares with strict ===, has no fallthrough, and throws UnhandledMatchError on an unmatched value instead of failing silently. The match (true) form gives value-returning if/elseif chains. For security-sensitive dispatch (roles, statuses, input from $_GET/$_POST), prefer match and always provide a fail-safe default.


Related