値によって処理を分けたいとき、if を並べるか switch を使うのが定番でした。ただ switch には、break を書き忘れると次の case まで処理が流れてしまう、比較が緩くて意図しない一致が起きる、といった扱いにくさがあります。
PHP 8.0 で追加された match 式は、こうした点を解消した条件分岐です。break は不要で、結果をそのまま変数に代入でき、比較は型まで見る厳密比較になります。
この記事では、match の基本的な書き方、switch との違い、条件式として使う match(true) の書き方、そして一致しなかったときのエラーの扱いまでを解説します。
目次
match は値を返す「式」
まずは switch で書いたコードと、それを match に置き換えたコードを並べて見てみます。
<?php
switch ( $status ) {
case 'publish':
$label = '公開済み';
break;
case 'draft':
$label = '下書き';
break;
case 'pending':
case 'future':
$label = '公開待ち';
break;
default:
$label = '不明';
}
<?php
$label = match ( $status ) {
'publish' => '公開済み',
'draft' => '下書き',
'pending', 'future' => '公開待ち',
default => '不明',
};
行数が半分以下になりました。変わったのは見た目だけではありません。match は式なので、全体が1つの値になります。だから $label = の右側にそのまま書けますし、関数の引数や return にも直接渡せます。
文法上の注意点として、式なので最後にセミコロンが必要です。switch の閉じ波かっこの感覚で書くと、構文エラーになります。
複数の値をまとめたいときは、カンマ区切りで並べます。switch のように case を空で続ける(フォールスルー)書き方はありません。
switch との違い
2つの違いを整理すると次のようになります。
| 項目 | switch | match |
|---|---|---|
| 文法上の扱い | 文(値を返さない) | 式(値を返す) |
| 比較方法 | 緩い比較(==) | 厳密比較(===) |
break | 必要。忘れると次に流れる | 不要。一致したものだけ評価される |
| 一致するものが無いとき | default が無ければ何もしない | default が無ければ UnhandledMatchError |
| 分岐に書ける内容 | 複数の文 | 1つの式のみ |
| 使える PHP のバージョン | すべて | 8.0 以降 |
比較が厳密になる
実務で効いてくるのが比較方法の違いです。switch は == で比較するため、型が違っても一致することがあります。
<?php
// フォームから来た値は文字列
$input = '1';
// switch は == なので、数値の 1 と一致してしまう
switch ( $input ) {
case 1:
echo '数値の1として扱われた';
break;
}
// 出力: 数値の1として扱われた
// match は === なので一致しない
$result = match ( $input ) {
1 => '数値の1',
'1' => '文字列の1',
default => 'その他',
};
echo $result;
// 出力: 文字列の1
フォームや REST API から受け取った値は、たいてい文字列です。switch ではその違いが吸収されてしまい、動いているように見えて実は意図とずれている、ということが起こります。match なら型の違いがそのまま結果に出るので、問題に気づけます。
逆に「文字列でも数値でも同じ扱いにしたい」場合は、match に渡す前に (int) でキャストするか、両方の値をカンマ区切りで並べます。どちらにせよ、変換していることがコード上で明示されます。
一致しないときは例外になる
match でどの条件にも一致せず、default も書かれていない場合、UnhandledMatchError という例外が投げられます。switch のように黙って素通りすることはありません。
<?php
$status = 'trash';
$label = match ( $status ) {
'publish' => '公開済み',
'draft' => '下書き',
};
// PHP Fatal error: Uncaught UnhandledMatchError:
// Unhandled match case 'trash'
これは欠点ではなく、むしろ利点です。「想定していない値が来ていること」がその場で分かるからです。switch だと $label が未定義のまま処理が進み、表示が空になって初めて気づく、ということになりがちです。
取りうる値がすべて分かっている場合は default を書かず、あえて例外に任せる書き方もあります。新しい値が増えたときに修正漏れを検知できるためです。逆に、想定外の値が来てもよい場面では default を書きます。
<?php
// default で自分の例外を投げることもできる
$label = match ( $status ) {
'publish' => '公開済み',
'draft' => '下書き',
default => throw new InvalidArgumentException( "未知のステータス: {$status}" ),
};
PHP 8.0 からは throw も式になったので、このように => の右側に直接書けます。
match(true) で範囲や条件を判定する
match は値の一致しか見ませんが、比較対象に true を渡すと、条件式による分岐として使えます。各分岐に書いた条件式のうち、最初に true になったものが選ばれるという仕組みです。
<?php
function get_shipping_fee( int $price ): int {
return match ( true ) {
$price >= 10000 => 0,
$price >= 5000 => 300,
$price > 0 => 600,
default => throw new InvalidArgumentException( '金額が不正です' ),
};
}
echo get_shipping_fee( 12000 ); // 0
echo get_shipping_fee( 6000 ); // 300
echo get_shipping_fee( 980 ); // 600
if と elseif を並べた場合と同じ結果になりますが、「1つの値を決めるための分岐」であることが見た目から伝わります。上から順に評価されるので、条件の順番は重要です。範囲が広いものを先に書くと、あとの条件に到達しなくなります。
書き換えるときに引っかかるところ
分岐の中に複数の処理を書けない
=> の右側に書けるのは1つの式だけです。「ログを出してからメールを送る」のように複数の処理をしたい場合、match には収まりません。
この場合は、処理をメソッドや関数に切り出して、その呼び出しを書くのが定石です。それでも収まらないなら、無理に書き換えず switch や if のままにしておくほうが読みやすくなります。match が向いているのは「入力に対して1つの値を決める」処理です。
<?php
// 処理を関数にまとめて、その呼び出しを式として書く
match ( $event ) {
'order_created' => notify_order_created( $order ),
'order_canceled' => notify_order_canceled( $order ),
default => null,
};
戻り値を使わない場合でも、match 自体は式なので、最後のセミコロンを忘れないでください。
PHP 7 系の環境では使えない
match は PHP 8.0 で追加された構文です。PHP 7.4 以前では syntax error, unexpected 'match' のようなパースエラーになります。レンタルサーバーなど、実行環境のバージョンが選べない場所に配布するコードでは注意してください。
なお、構文エラーはファイルの読み込み時点で発生するため、if ( PHP_VERSION_ID >= 80000 ) のような実行時の分岐では回避できません。バージョンによって処理を分けたい場合は、ファイル自体を分けて読み込む必要があります。
比較対象に式を書くときの読みにくさ
match ( true ) は便利ですが、多用すると「何を基準に分岐しているのか」が一目で分からなくなります。条件が3つ4つと増え、それぞれが別々の変数を見ているようなときは、素直に if で書いたほうが読みやすいことも多いです。判断の軸が1つであるかどうかを目安にしてください。
まとめ
match は PHP 8.0 で追加された、値を返す条件分岐です。switch と比べて break が不要、結果をそのまま代入できる、比較が厳密比較になる、という3点が主な違いになります。
どの条件にも一致しないと UnhandledMatchError が投げられるのも特徴です。想定外の値を見逃さずに済むので、取りうる値が決まっている分岐ではあえて default を書かない、という選択もできます。
ステータスコードをラベルに変換する、種別ごとに設定値を返すといった「値の対応表」のような処理は、match で書くとぐっと短くなります。一方で、1つの分岐の中で複数の処理をしたい場合は収まらないので、そこは if や switch と使い分けてください。