はじめに
前回の記事では、switch が抱える「ゆるい比較による予期しないマッチ」と「break 忘れによるフォールスルー」という2つの有名な落とし穴を解説しました。今回取り上げる match は、PHP 8.0で満を持して導入された、これらの問題を根本から解消する新しい分岐構文です。
match は、switch と似た「1つの値を複数の候補と比較する」という目的を持ちながら、厳密な比較(===相当)を行い、break が不要で、**値を返す「式」**であるという、より安全でモダンな設計になっています。本記事では基本的な使い方から、switch との明確な違い、実践的な活用例まで詳しく解説します。
構文概要
| 項目 | 内容 |
|---|---|
| 構文名 | match |
| 種別 | 式(switchとは異なり、値を返す) |
| 基本構文 | match ($value) { 候補1, 候補2 => 結果1, default => 結果2 } |
| 比較方法 | 厳密な比較(===相当) |
| フォールスルー | なし(各アームは独立しており、breakは不要かつ書けない) |
| マッチしない場合 | default がなければ UnhandledMatchError がスローされる |
| 対応バージョン | PHP 8.0.0以降 |
switchとmatchの決定的な違い(イメージ図)
【switch - ゆるい比較・文】
switch ($status) {
case 0: ← "0"や""などもゆるくマッチしうる
$result = '無効';
break; ← 書き忘れるとフォールスルー
default:
$result = '不明';
}
【match - 厳密な比較・式】
$result = match ($status) {
0 => '無効', ← 型も含めて厳密に一致する場合のみマッチ
1, 2 => '有効', ← カンマで複数候補をOR条件のように指定
default => '不明',
};
★この記事の対象
(breakは不要。各アームは独立しており、代入もそのまま可能)
ポイントは、match が値を返す「式」であるため、直接変数への代入や、関数の戻り値、別の式の中への埋め込みができるという点です。これにより、switch では必要だった「各ケースで変数に代入してからbreakする」という冗長な書き方が不要になります。
実践サンプル7選
例1:基本的な使い方
<?php
function describeDayNumber(int $day): string
{
return match ($day) {
1 => '月曜日',
2 => '火曜日',
3 => '水曜日',
default => '不明',
};
}
echo describeDayNumber(2) . PHP_EOL; // "火曜日"
例2:カンマ区切りで複数の候補をまとめる
<?php
function isWeekend(string $day): bool
{
return match ($day) {
// カンマで区切ることで、switchの"caseの連続"と同じ効果が得られる
'Saturday', 'Sunday' => true,
default => false,
};
}
var_dump(isWeekend('Saturday')); // true
var_dump(isWeekend('Monday')); // false
例3:厳密な比較によってswitchの問題が解消されることを確認する
<?php
function safeMatch(mixed $value): string
{
return match ($value) {
// matchは型も含めて厳密に比較するため、
// switchで問題になっていた意図しないマッチが起こらない
0 => '整数の0です',
'0' => '文字列の"0"です',
false => '真偽値のfalseです',
default => 'その他',
};
}
echo safeMatch(0) . PHP_EOL; // "整数の0です"
echo safeMatch('0') . PHP_EOL; // "文字列の"0"です"(switchなら誤ってマッチしていた可能性)
echo safeMatch(false) . PHP_EOL; // "真偽値のfalseです"
例4:match(true)パターンによる範囲条件の表現
<?php
function gradeFromScore(int $score): string
{
/**
* switch(true)と同様、match(true)とすることで
* 各アームに任意の真偽式を書ける
*/
return match (true) {
$score >= 90 => 'A',
$score >= 70 => 'B',
$score >= 50 => 'C',
default => 'D',
};
}
echo gradeFromScore(75) . PHP_EOL; // "B"
例5:defaultなしでマッチしない場合に例外をスローさせる
<?php
function httpMethodToAction(string $method): string
{
// defaultを意図的に書かないことで、
// 想定外の値が来た場合に明示的にエラーとして検知する
return match ($method) {
'GET' => '取得',
'POST' => '作成',
'PUT' => '更新',
'DELETE' => '削除',
};
}
try {
echo httpMethodToAction('PATCH') . PHP_EOL;
} catch (\UnhandledMatchError $e) {
echo 'エラー: ' . $e->getMessage() . PHP_EOL;
}
例6:式の中への直接埋め込み
<?php
class Order
{
public function __construct(private string $status)
{
}
public function getStatusBadgeClass(): string
{
// match式は値を返すため、戻り値にそのまま使える
return 'badge-' . match ($this->status) {
'pending' => 'warning',
'completed' => 'success',
'cancelled' => 'danger',
default => 'secondary',
};
}
}
$order = new Order('completed');
echo $order->getStatusBadgeClass() . PHP_EOL; // "badge-success"
例7:switchからの書き換えで見るコード量の削減
<?php
// switch版: 変数への代入とbreakが必要で冗長になりがち
function priorityLabelWithSwitch(int $level): string
{
switch ($level) {
case 1:
$label = '低';
break;
case 2:
$label = '中';
break;
case 3:
$label = '高';
break;
default:
$label = '不明';
}
return $label;
}
// match版: breakなしで簡潔に、かつ直接returnできる
function priorityLabelWithMatch(int $level): string
{
return match ($level) {
1 => '低',
2 => '中',
3 => '高',
default => '不明',
};
}
echo priorityLabelWithMatch(2) . PHP_EOL;
関連機能との比較
| 機能 | 役割 | matchとの違い |
|---|---|---|
match | 値に基づいて式を返す、厳密な比較を行う分岐 | 本記事の対象。PHP 8.0以降。===相当の比較で、値を返す |
switch | 1つの値を複数のケースと比較する「文」 | ゆるい比較(==)を行い、フォールスルーのリスクがある(前回記事を参照) |
if / elseif | 任意の条件式を順番に評価する | match(true)と似た柔軟性を持つが、matchの方が「値を返す式」として簡潔に書ける場合がある |
三項演算子(?:) | 条件に応じて2つの値のどちらかを返す式 | 候補が2つだけのシンプルな場合に適しており、matchは3つ以上の候補がある場合に適する |
Null合体演算子(??) | 値がnullの場合にデフォルト値を使う | null判定に特化した、より限定的な式 |
よくある落とし穴(注意点)
- 厳密な比較(
===)であることを前提に値を揃えるmatchは型まで含めて厳密に比較するため、'1'(文字列)と1(整数)は別の値として扱われます。switchからの移行時、ゆるい比較に依存していたコードがあると、挙動が変わってしまう可能性があるため注意が必要です(例3を参照)。 defaultがない状態でマッチしない値が来ると例外になる これはswitchとの大きな違いです。switchではマッチするcaseがなくても単に何も実行されず終わりますが、matchではdefaultがない場合、マッチしないとUnhandledMatchErrorという例外がスローされます。これは意図的な「想定外の値を検知する」設計として活用できますが(例5を参照)、予期せず例外が発生するリスクとしても理解しておく必要があります。- アーム内に複数の文を書くことはできない
matchの各アーム(条件 => 結果)は、単一の式のみを受け付けます。複数行にわたる処理を書きたい場合は、無名関数やメソッド呼び出しに処理を切り出す必要があります。 breakを書こうとして構文エラーになるswitchに慣れていると、ついmatchの中でもbreak;を書いてしまいがちですが、matchにはbreakという概念自体がなく、書くと構文エラーになります。- 複雑な条件判定には
match(true)パターンの可読性を考慮するmatch(true)は強力なパターンですが、多用しすぎると「matchなのに値の比較をしていない」という点で、コードを読む人を混乱させる可能性があります。単純な範囲条件であれば問題ありませんが、複雑な論理式が並ぶ場合はif–elseifの方が読みやすいこともあります(例4を参照)。
まとめ
| 観点 | まとめ |
|---|---|
| 何をする構文か | 値を複数の候補と厳密に比較し、マッチした結果を返す「式」 |
| 主な用途 | 単一の変数に対する値の判定、ステータスに応じたラベルやクラス名の決定 |
| 比較方法 | 厳密な比較(===相当)。switchのゆるい比較の問題を解消 |
switchとの違い | break不要・フォールスルーなし・式として値を返せる・defaultなしで例外になる |
| 注意点 | switchからの移行時の型の違いによる挙動変化、UnhandledMatchErrorのリスク |
match は、switch が長年抱えてきた「ゆるい比較」と「フォールスルー」という2つの代表的な問題点を解消した、PHP 8.0以降のモダンな分岐構文です。単一の値に対する判定処理では、新規コードにおいて switch よりも match を優先して選ぶことが、より安全で簡潔なコードにつながります。
