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 文の最も重要な違いは比較方法にある。
緩い比較(==)を使う。0 == "foo" が true になるような暗黙の型変換が起きる。break を忘れるとフォールスルーする。文なので値を返せない。
厳密比較(===)を使う。型が異なれば一致しない。フォールスルーしない。式なので値を返せる。
// 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 がないほうが、新しい値の追加時にエラーで気づける。
match 式で条件式を使う
match の対象を true にすることで、elseif のような条件分岐を式として書ける。
$score = 82;
$grade = match (true) {
$score >= 90 => "A",
$score >= 70 => "B",
$score >= 50 => "C",
default => "D",
};
echo $grade; // Bmatch (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 がスローされる
- 最後のアームが実行される
いつ match を使うか
match 式が適しているのは「1 つの値に基づいて結果を選ぶ」場面である。
| 場面 | 推奨する構文 | 理由 |
|---|---|---|
| 値に応じた単純な分岐 | match | 簡潔で型安全 |
| 複数文の実行が必要 | switch | match は単一式のみ |
| 範囲や複合条件 | if-elseif | 柔軟な条件記述 |
match は switch の上位互換ではなく、それぞれに適した場面がある。値のマッピングや単純な変換なら match を、副作用を伴う複雑な処理なら switch や if-elseif を使うのが実用的な判断基準になる。PHP 8 以降のコードでは match を第一候補として検討し、表現力が足りないときに他の構文へ切り替えるとよい。
match 式は網羅性を保証するため、一致しない場合は UnhandledMatchError を発生させます。これは switch 文にはない安全機構です。