【PHP】match式とswitch文の違いと使い分け|PHP 8.0の新機能を実務レベルで理解する
PHP 8.0 で導入された match 式は、値の一致に応じて結果を返す構文です。本記事では match 式と従来の switch 文の違い、実務での使い分け、注意点を整理します。コード例は PHP 8.0 以降を前提とします。詳細は PHP 公式マニュアルの match の節 および switch の節 を参照してください。
はじめに
switch 文で分岐を書き続け、戻り値を代入するために break と一時変数を何度も繰り返していませんか。HTTP ステータスコードからメッセージを返す、注文ステータスから表示ラベルを決める——こうした「値に応じて結果を分岐する」処理は PHP コードのあちこちに現れます。従来は switch 文が定番でしたが、PHP 8.0 からは match 式も選択肢に入ります。
この記事で扱う範囲は次のとおりです。
switch文の基本と、実務で困りやすい挙動match式の構文とswitchとの違い- 比較表と使い分けの目安
- よくある失敗例と次に試すこと
式と文の違い(この記事の前提)
PHP では 式(expression)は評価すると値になる 一方、文(statement)は処理の単位 で、単体では値になりません。
- 式の例 —
match (...) { ... }は代入やreturnの右辺にそのまま書ける - 文の例 —
switch (...) { ... }はブロック内で代入やbreakを書く
// 式: 評価結果をそのまま代入できる
$message = match ($status) { 200 => 'OK', default => 'Unknown' };
// 文: ブロック内で処理を書く(単体では値にならない)
switch ($status) {
case 200:
$message = 'OK';
break;
default:
$message = 'Unknown';
break;
}
以降の比較は、この「値を返せるか」「比較やフォールスルーの挙動」に焦点を当てます。
背景:switch 文のおさらい
switch は文であり、制御式の値と各 case の値を比較して分岐します。PHP の switch は 緩い比較(==) で一致判定を行います(公式マニュアル)。
$status = 200;
switch ($status) {
case 200:
$message = 'OK';
break;
case 404:
$message = 'Not Found';
break;
default:
$message = 'Unknown';
break;
}
break を書き忘れると、マッチした case のあと 次の case へ処理が流れ続ける(フォールスルー)点に注意が必要です。
$type = 'error';
switch ($type) {
case 'error':
$icon = 'error-icon';
// break がないため fall-through
case 'warning':
$icon = 'warning-icon';
break;
default:
$icon = 'info-icon';
break;
}
// $type が 'error' のときも 'warning-icon' になる
意図的なフォールスルーもありますが、代入だけしたい場面ではバグの温床になりやすいです。レガシーの switch を保守するときは、break の有無をレビュー観点に含めるとよいでしょう。
match 式とは
match は式です。評価結果を返し、PHP 8.0.0 以降で利用できます(公式マニュアル)。
$status = 200;
$message = match ($status) {
200 => 'OK',
404 => 'Not Found',
default => 'Unknown',
};
match の主な特徴は次のとおりです。
| 観点 | switch 文 | match 式 |
|---|---|---|
| 種類 | 文 | 式(戻り値がある) |
| 比較 | 緩い比較(==) | 厳密な比較(===) |
| フォールスルー | あり(break で止める) | なし |
| 網羅性 | default は任意 | どの分岐にも当てはまらないと UnhandledMatchError |
| 複数条件の OR | case を並べる | カンマ区切り(例: 'a', 'b' => ...) |
実装例
ステータスコードからメッセージを返す
次の例は、比較表の「式として戻り値がある」点を示します。先ほどの switch 版(代入と break でおおよそ 12 行)に対し、match 版は return match の 1 式にまとまります。
function httpMessage(int $status): string
{
return match ($status) {
200 => 'OK',
201 => 'Created',
404 => 'Not Found',
500 => 'Internal Server Error',
default => 'Unknown',
};
}
default を省略すると、上記のいずれにも当てはまらない値(例: 418)で UnhandledMatchError がスローされ、未捕捉のままだと実行はそこで停止します。
try {
httpMessage(418);
} catch (UnhandledMatchError $e) {
// Unhandled match value of type int
}
想定外の値を早期に検知したい場合は default を付けない選択もあります。その場合は呼び出し側で例外を想定するか、上位で捕捉する設計にしてください。
複数の値を 1 つの分岐にまとめる
match では左辺にカンマ区切りで複数の条件式を書けます。論理 OR と同等です。
$role = 'editor';
$label = match ($role) {
'admin', 'superuser' => '管理者',
'editor', 'author' => '編集者',
default => 'ゲスト',
};
switch でも case 'admin': case 'superuser': のように並べられますが、match では 1 行で同じ右辺を共有できます。
default で例外を投げる
PHP 8.0 以降、throw は式として扱えるため、match の分岐の右辺に直接書けます(マニュアルの default で throw する例)。
function daysInMonth(string $month): int
{
$key = strtolower(substr($month, 0, 3));
return match ($key) {
'jan', 'mar', 'may', 'jul', 'aug', 'oct', 'dec' => 31,
'apr', 'jun', 'sep', 'nov' => 30,
'feb' => 28, // うるう年判定は省略
default => throw new InvalidArgumentException("不正な月: {$month}"),
};
}
厳密一致以外の条件:match (true)
通常の match は制御式と左辺の 値の一致 で分岐します。match (true) は、左辺の 条件式が真になるか で分岐する別パターンです。範囲判定などに使えます(公式マニュアル「厳密な一致チェックを行わずに match 式を使う」)。
$age = 20;
$group = match (true) {
$age >= 65 => 'シニア',
$age >= 25 => '成人',
$age >= 18 => '若年成人',
default => '未成年',
};
// $age が 20 のときは '成人'($age >= 25 が先に真になる)
条件は 上から順に試される ため、順序が結果を変えます。$age >= 18 を最上段に置くと 20 歳は「若年成人」になります。if / elseif の連鎖を式としてまとめたいときに向きます。分岐が 4〜5 以上になる、または各 arm が複数行になる場合は、別関数への切り出しも検討してください。
使い分けの目安
match を検討しやすい場面
- 分岐の結果を 変数や戻り値に直接代入 したい
- 型と値の両方 を区別したい(
===ベース) break忘れによるフォールスルーバグを避けたい- 想定外の値を
UnhandledMatchErrorやdefaultのthrowで検知したい
switch が残りやすい場面
- 各
caseで 複数行の副作用(ログ出力、複数の代入、早期returnなしの処理)をまとめて実行したい - 意図的なフォールスルー で段階的に処理を重ねたい(レガシーコードで見かけるパターン)
- PHP 7 系など 8.0 未満 の実行環境をサポートする必要がある
新規コードで「値から結果を返すだけ」の分岐なら、代入と break の繰り返しは減り、match ならより短く書ける場合が多いです。一方、手続き的に処理を積み上げる switch の書き方が読みやすいケースもあります。
実務での注意点
比較の違いは型の罠につながる
switch の緩い比較では、型が異なっても一致することがあります。
$value = '0';
switch ($value) {
case 0:
// '0' == 0 は true のため、ここに入る
$result = 'zero as int';
break;
default:
$result = 'other';
break;
}
match では '0' === 0 は false なので、上記の case 0 には入りません。文字列と数値が混在する API レスポンスなどでは、どちらを使うかで結果が変わる点に注意してください。
すべての分岐が評価されるわけではない
match は最初に一致した分岐だけを評価します。マニュアルにあるとおり、一致が確定するまで左辺の条件式が順に試され、マッチした時点で右辺が評価されます。一致しなかった分岐の右辺(関数呼び出しなど)は実行されません。
preg_match などはそのままでは使えない
preg_match の戻り値は bool ではなく、一致時 1・不一致時 0・エラー時 false です。match の左辺は === で比較するため、if (preg_match(...)) のような真偽値判定をそのまま移植できません。
$text = 'hello';
// そのままでは意図どおり動かない
// match (preg_match('/hello/', $text)) { 1 => 'hit', 0 => 'miss' };
$result = match (true) {
preg_match('/hello/', $text) === 1 => 'hit',
preg_match('/hello/', $text) === 0 => 'miss',
default => 'error',
};
match (true) と明示的な比較を組み合わせるか、事前に bool へ正規化してください。
次に試すこと
- 候補を探す — 各
caseが代入とbreakだけ、戻り値用の一時変数があるswitchを 1 件選ぶ。 matchに書き換える — 制御式とcaseの値を左辺、代入先の値を右辺へ移す。想定外の入力はdefault(文字列フォールバック)、throw、省略(UnhandledMatchError)のどれにするか決める。- 実行して確認する — 既存の PHPUnit テストや
php -rで、代表値と境界値(defaultに入る値)を実行する。 - 型の罠を確認する — 文字列
"0"と数値0など、==と===で挙動が変わる入力はないか見る。 - レビュー観点を足す —
match移行の PR では、網羅性(defaultの有無)、入力の型、match (true)の条件順序をチェックする。
まとめ
matchは PHP 8.0 以降の式で、分岐結果を直接返せる。switchは緩い比較・フォールスルーあり、matchは厳密比較・フォールスルーなし。- 値からラベルやメッセージを返す処理は
matchで短く書けることが多い。 - 型の違いで
switchとmatchの結果が変わるため、入力の型を意識する。




