编程 PHP 8 match:严格比较、不贯穿,以及漏掉分支时的 UnhandledMatchError

2026-09-14 00:03:52

PHP 8 match:严格比较、不贯穿,以及漏掉分支时的 UnhandledMatchError

match 表达式基于恒等检查(identity check)对分支求值。它和 switch 一样有 subject 表达式去跟多个候选项比较,但和 switch 不同:它像三目表达式一样求出一个值,并且比较用的是恒等检查 === 而不是弱相等 ==。match 自 PHP 8.0.0 起可用。

$return_value = match (subject_expression) {
    single_conditional_expression => return_expression,
    conditional_expression1, conditional_expression2 => return_expression,
};

与 switch 的关键区别

  • match 分支用严格比较 ===,不是弱相等。
  • match 是表达式,会返回值。
  • match 分支不会像 switch 那样 fall through(贯穿到后面的 case)。
  • match 表达式必须穷尽(exhaustive)。

执行模型

逐分支执行,开始时什么都不执行。只有当之前所有条件表达式都没匹配 subject 时,才去求值当前条件表达式;只有匹配分支的 return 表达式会被求值。也就是说,写在前面的分支一旦命中,后面的条件表达式和返回值都不会被碰:

$result = match ($x) {
    foo() => 'value',
    $this->bar() => 'value', // 若 foo() === $x,则 $this->bar() 不会被调用
    $this->baz => beep(),    // 除非 $x === $this->baz,否则 beep() 不会被调用
};

一个分支可以含多个以逗号分隔的表达式,表示逻辑 OR:

match ($x) { $a, $b, $c => 5, }

这等价于三个分支 $a => 5$b => 5$c => 5

default 匹配前面所有没匹配到的值。写多个 default 会触发 E_FATAL_ERROR

必须穷尽

如果 subject 没有被任何分支处理,抛出 UnhandledMatchError

$condition = 5;
try {
    match ($condition) {
        1, 2 => foo(),
        3, 4 => bar(),
    };
} catch (\UnhandledMatchError $e) {
    var_dump($e); // message: "Unhandled match case 5"
}

PHPStan 提供 match.unhandled 错误标识来静态发现这种未覆盖,比如枚举漏了一个 case。

用 true 作 subject 做非恒等判断

想写条件判断而不是值匹配,就把 true 当 subject:

$result = match (true) {
    $age >= 65 => 'senior',
    $age >= 25 => 'adult',
    $age >= 18 => 'young adult',
    default    => 'kid',
};

注意:这种方式下,条件表达式成功时必须真的返回 true。很多人习惯依赖真值/假值判断,例如 preg_match 返回 int 0/1,这在 match 里行不通——01 都不等于 true

$s = 'abc123';

// 不会命中第一支:preg_match 返回 int(1),1 !== true
$r = match (true) {
    preg_match('/\d/', $s) => 'has digit',
    default                => 'no digit',
};

// 需要显式比较或强转
$r = match (true) {
    preg_match('/\d/', $s) === 1     => 'has digit',
    (bool) preg_match('/\d/', $s)    => 'has digit',
    default                          => 'no digit',
};

其他细节

match 表达式作为独立语句时必须用分号结尾。

目前不支持代码块。需要多语句时,用数组返回或者 IIFE 包裹:

$r = match ($x) {
    1 => (function () { /* ... */ })(),
    default => 'other',
};

返回值类型不会被 match 强制。可以用一层闭包包起来强制返回类型,返回不符会抛 TypeError

$r = (fn (): string => match ($x) {
    1       => 100,
    default => 'x',
})(); // TypeError

continue/goto 跳转 match 相关位置有编译期限制:continue 指向 match 会报 Fatal error;不允许 goto 进入 match。

设计动机

RFC 归纳了 switch 的四个长期问题:类型强制转换、无返回值、fallthrough、不穷尽(inexhaustiveness)。match 正是为解决这四点而设计的。

复制全文 生成海报 pattern matching PHP match 表达式

推荐文章

程序员茄子在线接单