1. ホーム
  2. PHP

【PHP】match 式の使い方|switch との違いと厳密比較で書く条件分岐

Share

値によって処理を分けたいとき、if を並べるか switch を使うのが定番でした。ただ switch には、break を書き忘れると次の case まで処理が流れてしまう、比較が緩くて意図しない一致が起きる、といった扱いにくさがあります。

PHP 8.0 で追加された match 式は、こうした点を解消した条件分岐です。break は不要で、結果をそのまま変数に代入でき、比較は型まで見る厳密比較になります。

この記事では、match の基本的な書き方、switch との違い、条件式として使う match(true) の書き方、そして一致しなかったときのエラーの扱いまでを解説します。

match は値を返す「式」

まずは switch で書いたコードと、それを match に置き換えたコードを並べて見てみます。

switch で書いた場合
<?php
switch ( $status ) {
    case 'publish':
        $label = '公開済み';
        break;
    case 'draft':
        $label = '下書き';
        break;
    case 'pending':
    case 'future':
        $label = '公開待ち';
        break;
    default:
        $label = '不明';
}
match で書いた場合
<?php
$label = match ( $status ) {
    'publish'           => '公開済み',
    'draft'             => '下書き',
    'pending', 'future' => '公開待ち',
    default             => '不明',
};

行数が半分以下になりました。変わったのは見た目だけではありません。match は式なので、全体が1つの値になります。だから $label = の右側にそのまま書けますし、関数の引数や return にも直接渡せます。

文法上の注意点として、式なので最後にセミコロンが必要です。switch の閉じ波かっこの感覚で書くと、構文エラーになります。

複数の値をまとめたいときは、カンマ区切りで並べます。switch のように case を空で続ける(フォールスルー)書き方はありません。

switch との違い

2つの違いを整理すると次のようになります。

項目switchmatch
文法上の扱い文(値を返さない)式(値を返す)
比較方法緩い比較(==)厳密比較(===)
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 のように黙って素通りすることはありません。

example.php
<?php
$status = 'trash';

$label = match ( $status ) {
    'publish' => '公開済み',
    'draft'   => '下書き',
};
// PHP Fatal error: Uncaught UnhandledMatchError:
// Unhandled match case 'trash'

これは欠点ではなく、むしろ利点です。「想定していない値が来ていること」がその場で分かるからです。switch だと $label が未定義のまま処理が進み、表示が空になって初めて気づく、ということになりがちです。

取りうる値がすべて分かっている場合は default を書かず、あえて例外に任せる書き方もあります。新しい値が増えたときに修正漏れを検知できるためです。逆に、想定外の値が来てもよい場面では default を書きます。

example.php
<?php
// default で自分の例外を投げることもできる
$label = match ( $status ) {
    'publish' => '公開済み',
    'draft'   => '下書き',
    default   => throw new InvalidArgumentException( "未知のステータス: {$status}" ),
};

PHP 8.0 からは throw も式になったので、このように => の右側に直接書けます。

match(true) で範囲や条件を判定する

match は値の一致しか見ませんが、比較対象に true を渡すと、条件式による分岐として使えます。各分岐に書いた条件式のうち、最初に true になったものが選ばれるという仕組みです。

example.php
<?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つの値を決める」処理です。

example.php
<?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 と使い分けてください。

参考ページ