match 式 - PHP 8 の新しい分岐

PHP 8.0 で導入された match 式は、switch 文の問題点を解消するために設計された新しい分岐構文である。「式」であるため値を返すことができ、厳密比較がデフォルトで、break の書き忘れによるフォールスルーも起きない。

match 式の基本構文

match は対象の値を複数の条件と照合し、一致した条件に対応する値を返す。

$status = 2;

$message = match ($status) {
    0 => "下書き",
    1 => "公開中",
    2 => "非公開",
    3 => "削除済み",
};

echo $message; // 非公開

match は式なので、結果を変数に代入したり return で直接返したりできる。各アーム(条件 => 値)はカンマで区切り、最後のアームの後にもカンマを置ける。セミコロンは match 式全体の末尾に 1 つだけ必要だ。

switch 文では各 case の後に break を書く必要があったが、match ではアームが 1 つ実行された時点で自動的に終了する。フォールスルーという概念自体が存在しない。

switch 文との違い

match 式と switch 文の最も重要な違いは比較方法にある。

switch 文

緩い比較(==)を使う。0 == "foo" が true になるような暗黙の型変換が起きる。break を忘れるとフォールスルーする。文なので値を返せない。

match 式

厳密比較(===)を使う。型が異なれば一致しない。フォールスルーしない。式なので値を返せる。

// switch の危険な挙動
$input = 0;

switch ($input) {
    case "draft":
        echo "下書き"; // PHP 7 ではここに入ってしまう
        break;
    case 0:
        echo "ゼロ";
        break;
}

switch は緩い比較を行うため、0 == "draft" が PHP 7 以前では true になり、意図しない case に入る。match ではこの問題が起きない。

$input = 0;

$result = match ($input) {
    "draft" => "下書き",
    0       => "ゼロ",   // 厳密比較なのでここに入る
};

echo $result; // ゼロ

複数条件をまとめる

1 つのアームに複数の値をカンマ区切りで列挙できる。

$extension = "jpg";

$type = match ($extension) {
    "jpg", "jpeg", "png", "gif" => "画像",
    "mp4", "avi", "mov"        => "動画",
    "pdf"                       => "PDF",
    "csv", "xlsx"               => "表計算",
    default                     => "不明",
};

echo $type; // 画像

switch 文で同じことをするには case を並べて break なしでフォールスルーさせる必要があった。match ではカンマで並べるだけなので意図が明確になる。

// switch で同じことをする場合
switch ($extension) {
    case "jpg":
    case "jpeg":
    case "png":
    case "gif":
        $type = "画像";
        break;
    case "mp4":
    case "avi":
    case "mov":
        $type = "動画";
        break;
    // ...
}

記述量の差は歴然としている。match のほうが簡潔で見通しがよい。

default アームと UnhandledMatchError

match 式でどのアームにも一致しなかった場合、default アームがあればそこが実行される。default がなければ UnhandledMatchError がスローされる。

$color = "purple";

// default なし → エラー
$hex = match ($color) {
    "red"   => "#ff0000",
    "green" => "#00ff00",
    "blue"  => "#0000ff",
};
// UnhandledMatchError: Unhandled match case

これは switch 文にはない安全機構である。switch では一致する case がなくても何も起きずに通過してしまうが、match は明示的にエラーを発生させる。

default を書くべきとき

入力値の範囲が不定な場合や、外部入力を扱う場合。エラーメッセージを返したり例外を投げたりする。

default を書かないほうがよいとき

列挙型など値の範囲が確定している場合。default がないほうが、新しい値の追加時にエラーで気づける。

match 式で条件式を使う

match の対象を true にすることで、elseif のような条件分岐を式として書ける。

$score = 82;

$grade = match (true) {
    $score >= 90 => "A",
    $score >= 70 => "B",
    $score >= 50 => "C",
    default      => "D",
};

echo $grade; // B

match (true) は各アームの条件式を上から順に評価し、最初に true になったものを返す。elseif の連鎖を式として書き直したい場面で便利だ。

ただし match (true) は通常の match と異なり「値の一致」ではなく「条件の評価」を行っているため、初見では意図が読みにくいこともある。単純な範囲判定なら問題ないが、複雑な条件を詰め込みすぎると可読性が落ちる。

match 式は「式」である

match が式であることの利点は、他の式の中に組み込める点にある。

function getStatusBadge(int $status): string {
    return match ($status) {
        0 => '<span class="gray">下書き</span>',
        1 => '<span class="green">公開</span>',
        2 => '<span class="red">非公開</span>',
        default => '<span class="black">不明</span>',
    };
}

return に直接書けるため、一時変数が不要になる。配列の値としても使える。

$data = [
    "label"  => match ($status) {
        0 => "draft",
        1 => "published",
        default => "unknown",
    },
    "active" => $status === 1,
];

switch 文ではこのような書き方はできない。switch は文であり、値を返す能力を持たないためだ。

match 式の制限

match は便利だが万能ではない。各アームで実行できるのは単一の式だけであり、複数の文を書くことはできない。

// これはできない
$result = match ($action) {
    "save" => {
        validate($data);
        save($data);
        "保存しました";
    },
};

複数の処理を行いたい場合は、関数に切り出すか switch 文を使う。

// 関数に切り出す
$result = match ($action) {
    "save"   => processSave($data),
    "delete" => processDelete($data),
    default  => "不明な操作",
};

match 式でどの条件にも一致せず default も定義されていない場合、何が起きますか?

  • null が返る
  • 何も起きずスキップされる
  • UnhandledMatchError がスローされる
  • 最後のアームが実行される
__RESULT__

match 式は網羅性を保証するため、一致しない場合は UnhandledMatchError を発生させます。これは switch 文にはない安全機構です。

いつ match を使うか

match 式が適しているのは「1 つの値に基づいて結果を選ぶ」場面である。

場面推奨する構文理由
値に応じた単純な分岐match簡潔で型安全
複数文の実行が必要switchmatch は単一式のみ
範囲や複合条件if-elseif柔軟な条件記述

match は switch の上位互換ではなく、それぞれに適した場面がある。値のマッピングや単純な変換なら match を、副作用を伴う複雑な処理なら switch や if-elseif を使うのが実用的な判断基準になる。PHP 8 以降のコードでは match を第一候補として検討し、表現力が足りないときに他の構文へ切り替えるとよい。