PHPを書いて早4年。何度も何度もお世話になった switch 文。
そのswitch文の進化系のような式が、PHPバージョン8から新たに追加されたんです!
それが match 式!
個人的にはこれが結構感動したもんで、簡単なカート計算ロジックを例に、使用感(?)をお伝えしようと思います。
概要

これ何がすごいって、match は「文」じゃなくて「式」だってことなんですよね。
式。
そう、つまり、値を返せるのです。
実際にswitch文と比較してみましょう。
switch ($rank) {
case 'gold':
$rate = 0.20;
break;
case 'silver':
$rate = 0.10;
break;
default:
$rate = 0.0;
}
親の顔より見たなぁ…。
現状のままでも十分わかりやすいですが、やや縦長なのが気になりますよね。
match式で書いてみるとどうでしょうか。
$rate = match ($rank) {
'gold' => 0.20,
'silver' => 0.10,
default => 0.0,
};
10行が5行になってる!!!!
半分!!!!!
半分ですよ!!!!???
break はいらないし、$rate = も毎回書かなくていいし、なにより「 $rank が〇〇ならこの値ね!」ってのがわかりやすい。
初学者であればこっちの方が馴染むんじゃないだろうか。
それだけじゃないんです。
match式の本領はこんなもんじゃないんですよ。
実装例
せっかくなので、ECサイトあるあるの「割引ルール」をカート計算で実装してみます。
今回はアパレルの通販をイメージし、ルールを3つ用意しました。
商品ごとに「どのルールを適用するか?」を分岐させる必要があります。
これ、switchで書こうとすると、各 case の中で計算して変数に代入して break して…とやることになります。
分岐が「値を返す」構造じゃないので、どうしても縦に長くなってしまうわけです。
ここでmatchの出番です。
カートの定義
まずはカートに何が入っているか定義しましょう。
$cart = [
['name' => 'Tシャツ', 'price' => 2000, 'qty' => 3, 'rule' => 'buy3_10off'],
['name' => '靴下', 'price' => 500, 'qty' => 6, 'rule' => 'buy5_get1'],
['name' => '帽子', 'price' => 3000, 'qty' => 1, 'rule' => 'none'],
]
ここでは、商品名・価格・個数・その商品の割引ルールを持たせています。
ルールの関数
Tシャツと靴下のルールは関数に切り出しておきました。
Tシャツ
function buyThreeOffPrice($price, $qty) {
// 割引率
$discount_rate = 0.1;
// しきい値
$threshold = 3;
$subtotal = $qty * $price;
if($qty >= $threshold) {
$subtotal *= 1 - $discount_rate;
}
return (int) round($subtotal);
}
靴下
function buyFiveGetOnePrice($price, $qty) {
// しきい値
$threshold = 6;
// 何個分サービスできるか
$free = intdiv($qty, $threshold);
$subtotal = $price * ($qty - $free);
return (int) $subtotal;
}
集計の関数
あとはカートの集計ロジックを作りましょう!
match式が活躍してくれます。
match式の場合
function applyDiscountRule($item) {
$price = $item['price'];
$qty = $item['qty'];
return match ($item['rule']) {
'buy3_10off' => buyThreeOffPrice($price, $qty),
'buy5_get1' => buyFiveGetOnePrice($price, $qty),
'none' => $qty * $price,
// どのルールにも当てはまらなかったら割引なし
default => $price * $qty,
};
}
switch文の場合
function applyDiscountRule($item) {
$price = $item['price'];
$qty = $item['qty'];
switch ($item['rule']) {
case 'buy3_10off':
$result = buyThreeOffPrice($price, $qty);
break;
case 'buy5_get1':
$result = buyFiveGetOnePrice($price, $qty);
break;
case 'none':
$result = $price * $qty;
break;
default:
// どのルールにも当てはまらなかったら割引なし
$result = $price * $qty;
}
return $result;
}
ほらもう全然違う!!!!!
関数をそのまま渡せるの、超便利じゃないですか??
ルールが増えても、行を1つ足すだけ。
読む側も「どのルールがどの計算か」わかりやすい。
やっぱりswitch文だと break がいちいち面倒なんですよね…。
これもこれで好きなんだけどさ…。
計上
最後に、各関数を使って計上していきたいと思います。
$total = 0;
foreach ($cart as $c) {
$item_subtotal = applyDiscountRule($c);
$total += $item_subtotal;
echo $c['name'] . " " . $c['qty'] . "点 小計: " . $item_subtotal . "円" . "\n";
}
echo "合計 " . $total . "円" . "\n";
実行結果
Tシャツ 3点 小計: 5400円
靴下 6点 小計: 2500円
帽子 1点 小計: 3000円
合計 10900円
match式のすごいとこ
こうした分岐のロジックって業務でも結構登場しますよね。
PHPのバージョンさえよけりゃ、かなりコードの改善に役立つのではないでしょうか?
- 式だからそのまま
returnできちゃう- 計算結果をいったん変数に入れて、最後に返して…みたいな回り道は不要。
- 「そのまま返せる」感じが楽でいいです。
breakを書き忘れる事故がない- 私のようなおとぼけエンジニアはまぁやります。
- match はフォールスルー(次の分岐に流れる挙動)がそもそも無いので、break という概念自体が存在しません。書き忘れようがない。平和。
- 実は比較が厳密(===)
- switch は処理の中で緩い比較(==)をするので、0 == “gold” みたいな意図しないマッチが起きることがあります(マジ?)。
- その点 match は厳密比較(===)なので、型まで含めてきっちり比べてくれるらしい。
- 網羅されてないと、ちゃんと怒ってくれる
- これが地味~に助かるんです。
- match は「どの分岐にも当てはまらず、default も無い」場合、UnhandledMatchError という例外を投げてくれます。
- switch だと黙ってスルーされて「なんかどっかで何かが処理から漏れてる…?? んだが…??」となりがちなところを、match は即座にアピールしてくれるわけです。
- タイポや考慮漏れに早く気づけるので、むしろ default を敢えて書かないままテストしてみるのも手らしい。なるほど。
// default を書かないと…
$rate = match ($rank) {
'gold' => 0.20,
'silver' => 0.10,
// default => 0.0,
};
// → $rank が 'bronze' のとき UnhandledMatchError
おまけ:カンマで複数値をまとめる
条件に合致する値がたくさんあるなら、カンマ区切りでいけます。
$shipping = match ($prefecture) {
'北海道', '沖縄' => 1500, // 離島料金
'東京', '神奈川', '千葉' => 0, // 送料無料エリア
default => 800,
};
実際のところ
こういうのってだいたいDBでしきい値を持ってることが常なので、記事のように数字がべた書きされることはなくって、きっと変数になります。
キャンペーン時の割引率とか、ころころ変わりますもんね。
とはいえあれだけ長かったswitch文がここまでコンパクトに、可読性も高く書けちゃうなら、match式を採用する手は大いにありだと思います。
レガシーな環境で開発してると、PHP8に出会うことが中々ないので…使えるタイミングが来たら絶対に使おう!と、今から意気込んでおきます。
ではまた次回!



