PHP の match 式とは、値を厳密な比較(===)で照合し、一致した腕(アーム)の結果を「値として返す」条件分岐です。 PHP 8.0 で追加されました。switch と似ていますが、break が不要で、どの条件にも一致しないときは UnhandledMatchError という例外で知らせてくれる点が大きく違います。

この記事では、同じ条件分岐を if elseif・switch・match の3通りで書いて並べ、比較方法・break・戻り値・該当なし時の挙動を表で整理します。"0" と 0 を渡したときに switch と match で結果が変わる例、default を書かずに UnhandledMatchError が出る例は、PHP 8.4.21 で実際に動かした出力をそのまま載せています。match(true) での範囲判定、複数値のカンマ指定、enum との組み合わせ、複数行の処理を書きたいときの対処まで、コピーして動かせる形でまとめました。

if elseif・switch・match の違い早見表

比べる点 if / elseif switch match
種類 文 文 式(値を返す)
使える PHP すべて すべて 8.0 以降
比較方法 自分で書く(=== も == も >= も可) 緩い比較(==) 厳密な比較(===)
break 不要 必要(書かないと次の case へ進む) 不要(次の腕へは進まない)
戻り値 なし(変数に代入するか return する) なし(同左) あり(そのまま代入・return できる)
複数の値をまとめる || でつなぐ case を並べる カンマで区切る
どれにも一致しないとき 何も起きない(else を書けば実行) 何も起きない(default を書けば実行) UnhandledMatchError(default を書けば実行)
1つの分岐に書ける量 複数行 複数行 式を1つ
範囲や複雑な条件 得意 switch (true) で可能 match (true) で可能
向いている場面 条件がそれぞれ違う形のとき 1つの分岐で複数の処理をしたいとき 値に応じて値を決めたいとき

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「PHP match式の使い方|switchとの違いとif elseifの使い分け」

同じ条件分岐を3通りで書いて比べる

記事の状態(status)を表す文字列から、画面に出すラベルを決める処理を、3つの書き方で用意しました。そのまま three.php として保存して実行できます。

<?php
declare(strict_types=1);

function labelByIf(string $status): string
{
    if ($status === 'draft') {
        return '下書き';
    } elseif ($status === 'review' || $status === 'pending') {
        return '確認待ち';
    } elseif ($status === 'published') {
        return '公開中';
    } else {
        return '不明';
    }
}

function labelBySwitch(string $status): string
{
    switch ($status) {
        case 'draft':
            $label = '下書き';
            break;
        case 'review':
        case 'pending':
            $label = '確認待ち';
            break;
        case 'published':
            $label = '公開中';
            break;
        default:
            $label = '不明';
    }
    return $label;
}

function labelByMatch(string $status): string
{
    return match ($status) {
        'draft' => '下書き',
        'review', 'pending' => '確認待ち',
        'published' => '公開中',
        default => '不明',
    };
}

foreach (['draft', 'pending', 'published', 'archived'] as $s) {
    printf("%-10s if=%s switch=%s match=%s\n", $s, labelByIf($s), labelBySwitch($s), labelByMatch($s));
}
php three.php
draft      if=下書き switch=下書き match=下書き
pending    if=確認待ち switch=確認待ち match=確認待ち
published  if=公開中 switch=公開中 match=公開中
archived   if=不明 switch=不明 match=不明

結果は3つとも同じです。違いは書き方に出ています。

見るところ if / elseif switch match
関数本体の行数 9行 15行 6行
$status を書く回数 4回 1回 1回
結果の受け取り方 分岐ごとに return 変数に入れてから return 式の結果をそのまま return
複数の値 || case を2行 'review', 'pending'

「1つの値を見て、対応する値を決める」処理は、match がいちばん短く書けます。

match 式の基本の書き方

$結果 = match (調べる値) {
    条件1 => 返す値1,
    条件2, 条件3 => 返す値2,
    default => どれにも一致しないときの値,
};
部品 説明
match (調べる値) かっこの中の値を、上から順に各条件と === で比べる
条件 => 返す値 この1行を「腕(アーム)」と呼ぶ。一致したら => の右側の式を評価して返す
条件2, 条件3 カンマで区切ると「どれかに一致したら」という意味になる
default どの条件にも一致しないときに選ばれる。書けるのは1つだけ
末尾の ; match は式なので、文として終わらせるセミコロンが必要
最後の腕のあとの , 付けても付けなくてもよい

式なので、どこにでも書ける

match は値を返すので、代入の右辺だけでなく、return・関数の引数・文字列連結の途中にも直接書けます。

<?php
$code = 404;
echo 'HTTP ' . $code . ' ' . match ($code) {
    200, 201, 204 => 'OK系',
    301, 302 => 'リダイレクト',
    404 => 'Not Found',
    default => 'その他',
}, PHP_EOL;
HTTP 404 Not Found

上から順に評価し、最初に一致した腕だけが動く

条件側に関数呼び出しを書いた場合、一致した時点でそれより下は評価されません。

<?php
function check(string $name, bool $r): bool { echo "  {$name} を評価\n"; return $r; }

$r = match (true) {
    check('A', false) => 'A',
    check('B', true) => 'B',
    check('C', true) => 'C',
};
echo "結果: {$r}\n";
  A を評価
  B を評価
結果: B

C は評価されていません。条件は「先に書いたものが優先」なので、範囲判定では並べる順番が結果を左右します(後述)。

条件側には変数や式も書ける

<?php
$min = 1; $max = 5;
$n = 5;
echo match ($n) { $min => '最小', $max => '最大', default => '中間' }, PHP_EOL;
最大

switch の case と同じく、条件はリテラルに限られません。

switch と match の違い1:比較方法(== と ===)

switch は緩い比較(==)、match は厳密な比較(===)で照合します。型まで見るかどうかが違いです。

"0" と 0 で結果が変わる例

次のコードを zero.php として保存して実行します。case と腕の並びは同じです。

<?php
function bySwitch(mixed $v): string
{
    switch ($v) {
        case 0:
            return '数値の0';
        case '0':
            return '文字列の"0"';
        case null:
            return 'null';
        default:
            return 'その他';
    }
}

function byMatch(mixed $v): string
{
    return match ($v) {
        0 => '数値の0',
        '0' => '文字列の"0"',
        null => 'null',
        default => 'その他',
    };
}

$inputs = ['0', 0, '00', 0.0, '', null, false, 'abc', '1e1'];
foreach ($inputs as $v) {
    printf("%-8s switch=%-14s match=%s\n", var_export($v, true), bySwitch($v), byMatch($v));
}
'0'      switch=数値の0     match=文字列の"0"
0        switch=数値の0     match=数値の0
'00'     switch=数値の0     match=その他
0.0      switch=数値の0     match=その他
''       switch=null           match=その他
NULL     switch=数値の0     match=null
false    switch=数値の0     match=その他
'abc'    switch=その他      match=その他
'1e1'    switch=その他      match=その他
渡した値 switch の結果 match の結果 なぜ違うか
'0'(文字列) 数値の0 文字列の"0" '0' == 0 は true なので、switch は先に書いた case 0 に入る
0(整数) 数値の0 数値の0 どちらも一致
'00' 数値の0 その他 数値として読める文字列は数値に変換して比べられる
0.0(浮動小数点) 数値の0 その他 0.0 == 0 は true、0.0 === 0 は false
''(空文字) null その他 '' == null は true
null 数値の0 null null == 0 は true なので case 0 に入る
false 数値の0 その他 false == 0 は true
'abc' その他 その他 PHP 8 では数値でない文字列と 0 は等しくない

switch では null や false を渡しても「数値の0」と判定され、case '0' や case null まで届きません。 match は型も含めて一致したものだけを選ぶので、書いたとおりの腕に入ります。

文字列同士でも switch は数値として比べることがある

両方が数値として読める文字列の場合、switch は数値に変換して比べます。

<?php
function sw(string $v): string { switch ($v) { case '1.0': return "case '1.0'"; case '1': return "case '1'"; default: return 'default'; } }
function ma(string $v): string { return match ($v) { '1.0' => "arm '1.0'", '1' => "arm '1'", default => 'default' }; }

echo sw('1'), ' / ', ma('1'), PHP_EOL;
echo sw('01'), ' / ', ma('01'), PHP_EOL;
case '1.0' / arm '1'
case '1.0' / default

'1' を渡すと、switch は '1' == '1.0'(数値として等しい)で最初の case に入りました。郵便番号や会員番号のように「数字だけの文字列」を扱うときは、match か === を使った if にすると意図どおりに分かれます。

厳密な比較ならではの気をつけどころ

match は型が違うと一致しません。フォームや URL から届く値は文字列なので、整数の腕とは一致しない点に気をつけます。

<?php
$id = '5';
echo match ($id) { 5 => 'int', '5' => 'string' }, PHP_EOL;
var_dump(match (1.0) { 1 => 'int 1', default => 'default' });
string
string(7) "default"
入力 対処
$_GET['page'] などの数字の文字列 (int) で変換してから match に渡す、または腕を文字列で書く
浮動小数点の 1.0 整数の 1 とは一致しない。腕を 1.0 と書くか、match (true) で範囲として判定する
大文字小文字が混ざる文字列 match (strtolower(trim($v))) のように整えてから渡す

switch と match の違い2:break とフォールスルー

switch は、break を書かないと次の case の処理まで続けて実行します(フォールスルー)。match は一致した腕だけを実行します。

<?php
$rank = 'gold';

switch ($rank) {
    case 'gold':
        echo "送料無料\n";
    case 'silver':
        echo "ポイント2倍\n";
        break;
    default:
        echo "特典なし\n";
}

echo match ($rank) {
    'gold' => "送料無料\n",
    'silver' => "ポイント2倍\n",
    default => "特典なし\n",
};
送料無料
ポイント2倍
送料無料

switch では case 'gold' に break がないため、「ポイント2倍」まで出力されました。意図してそう書くこともありますが、break の書き忘れと見分けにくいのが switch の特徴です。match には break もフォールスルーもないので、この種の取り違えが起きません。

「gold と silver で同じ処理をしたい」ときは、match ではカンマで並べます。

$benefit = match ($rank) {
    'gold', 'silver' => '送料無料',
    default => '特典なし',
};

なお、ループの中の switch で continue を書くと break と同じ動きになり、PHP が警告を出します。

PHP Warning:  "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in switch_continue.php on line 5

switch と match の違い3:戻り値

switch は文なので、結果を使うには変数に代入するか、各 case で return します。match は式なので、結果をそのまま受け取れます。

書き方 switch match
変数に入れる 各 case で $label = ...; break; $label = match (...) { ... };
関数から返す 各 case で return ...; return match (...) { ... };
代入し忘れたとき 変数が未定義のまま進む 構文上、必ず値が決まる

switch で default を書かず、どの case にも一致しなかった場合を実行してみます。

<?php
$status = 'archived';
switch ($status) {
    case 'draft': $label = '下書き'; break;
    case 'published': $label = '公開中'; break;
}
var_dump($label ?? null);
echo $label;
NULL
PHP Warning:  Undefined variable $label in nodupe.php on line 9

switch 自体は何も知らせず、あとで変数を使ったところで初めて警告が出ます。match では次の節のとおり、その場で例外になります。

switch と match の違い4:該当なし時の挙動(UnhandledMatchError)

default を書かずに一致しない値を渡す

<?php
$status = 'archived';

$label = match ($status) {
    'draft' => '下書き',
    'published' => '公開中',
};

echo $label, PHP_EOL;
PHP Fatal error:  Uncaught UnhandledMatchError: Unhandled match case of type string in unhandled.php:4
Stack trace:
#0 {main}
  thrown in unhandled.php on line 4

どの腕にも一致せず default もないとき、match は UnhandledMatchError を投げて処理を止めます。 行番号つきで知らせてくれるので、条件の書きもれにすぐ気づけます。

エラーメッセージは php.ini の設定で変わる

上の出力では値が of type string と型だけの表示になっています。これは検証環境の php.ini が zend.exception_ignore_args = On(本番向け php.ini の値)だったためです。設定を変えて同じコードを動かすと、メッセージが変わりました。

実行方法 文字列 'archived' を渡したときのメッセージ
検証環境の php.ini のまま(zend.exception_ignore_args=On) Unhandled match case of type string
php -d zend.exception_ignore_args=0(文字列の表示長は php.ini の 0 のまま) Unhandled match case '...'
php -n(php.ini を読まず、PHP の既定値で実行) Unhandled match case 'archived'

php -n で、いろいろな型の値を渡した結果です。

UnhandledMatchError: Unhandled match case 'archived'
UnhandledMatchError: Unhandled match case 5
UnhandledMatchError: Unhandled match case 3.5
UnhandledMatchError: Unhandled match case NULL
UnhandledMatchError: Unhandled match case true
UnhandledMatchError: Unhandled match case of type array
UnhandledMatchError: Unhandled match case of type stdClass
UnhandledMatchError: Unhandled match case 'xxxxxxxxxxxxxxx...'

30文字の文字列は15文字で切られて表示されました。メッセージに値が出ないときは、php.ini の zend.exception_ignore_args を確認すると原因がわかります。本番向けの設定では、例外メッセージに入力値を出さないようになっています。

UnhandledMatchError は Error の仲間

var_dump((new UnhandledMatchError) instanceof Error, (new UnhandledMatchError) instanceof Exception);
bool(true)
bool(false)

UnhandledMatchError は Exception ではなく Error を継承しています。catch (Exception $e) では受け取れないので、捕まえるなら catch (\UnhandledMatchError $e) か catch (\Throwable $e) と書きます。

3つの対処と選び方

対処 書き方 向いている場面
default で既定値を返す default => '不明', 想定外の値でも画面を出し続けたい
default で意味のある例外を投げる default => throw new InvalidArgumentException(...) 想定外の値は呼び出し側の誤りとして知らせたい
あえて default を書かない (何も書かない) enum のように取りうる値が決まっていて、書きもれを見つけたい

2つ目の例です。throw は PHP 8.0 から式として書けるので、腕にそのまま置けます。

<?php
declare(strict_types=1);

function parsePriority(string $p): int
{
    return match (strtolower(trim($p))) {
        'high', 'h' => 3,
        'middle', 'm' => 2,
        'low', 'l' => 1,
        default => throw new InvalidArgumentException("優先度が不明です: {$p}"),
    };
}

echo parsePriority(' High '), PHP_EOL;
try {
    parsePriority('urgent');
} catch (InvalidArgumentException $e) {
    echo $e->getMessage(), PHP_EOL;
}
3
優先度が不明です: urgent

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

match は === で照合するため、>= のような範囲の条件はそのままでは書けません。そこで 調べる値を true にし、各腕に条件式を書く方法を使います。「最初に true になった腕」が選ばれます。

<?php
declare(strict_types=1);

function shippingFee(int $total): int
{
    return match (true) {
        $total >= 10000 => 0,
        $total >= 5000 => 300,
        $total >= 1 => 600,
        default => throw new InvalidArgumentException("合計金額が不正です: {$total}"),
    };
}

foreach ([12000, 10000, 9999, 5000, 800] as $t) {
    printf("%6d円 -> 送料 %d円\n", $t, shippingFee($t));
}
try {
    shippingFee(0);
} catch (InvalidArgumentException $e) {
    echo $e->getMessage(), PHP_EOL;
}
 12000円 -> 送料 0円
 10000円 -> 送料 0円
  9999円 -> 送料 300円
  5000円 -> 送料 300円
   800円 -> 送料 600円
合計金額が不正です: 0

範囲判定で押さえる2つのポイント

ポイント 理由
条件は厳しいものから順に書く 上から評価されるため。$total >= 1 を先頭に書くと、すべてそこに入る
各腕の条件は bool を返す式にする true と === で比べるため。1 や "yes" は一致しない

2つ目は、bool 以外を返す関数でつまずきやすいところです。preg_match() は一致したとき整数の 1 を返すので、そのままでは true と一致しません。

<?php
$text = 'order-123';

try {
    echo match (true) {
        preg_match('/^order-\d+$/', $text) => "注文番号\n",
        str_starts_with($text, 'user-') => "会員番号\n",
    };
} catch (\UnhandledMatchError $e) {
    echo get_class($e), ': ', $e->getMessage(), PHP_EOL;
}

// 対処: === 1 で bool にする
echo match (true) {
    preg_match('/^order-\d+$/', $text) === 1 => "注文番号\n",
    str_starts_with($text, 'user-') => "会員番号\n",
    default => "不明\n",
};
UnhandledMatchError: Unhandled match case of type bool
注文番号
bool 以外を返す関数の例 戻り値 match(true) での書き方
preg_match() 1 / 0 / false preg_match(...) === 1
strpos() 位置(int)/ false strpos(...) !== false、または str_contains() を使う
count() 件数(int) count($a) > 0
in_array()、str_contains()、is_int() など bool そのまま書ける

型ごとに分岐する

match (true) は、is_int() や instanceof と組み合わせると型ごとの分岐もすっきり書けます。

<?php
declare(strict_types=1);

function describe(mixed $v): string
{
    return match (true) {
        is_int($v) => "整数 {$v}",
        is_string($v) => "文字列 '{$v}'",
        $v instanceof DateTimeInterface => '日時 ' . $v->format('Y-m-d'),
        is_array($v) => '配列 ' . count($v) . '件',
        default => get_debug_type($v),
    };
}

foreach ([42, 'abc', new DateTimeImmutable('2026-10-03'), [1, 2, 3], 1.5, null] as $v) {
    echo describe($v), PHP_EOL;
}
整数 42
文字列 'abc'
日時 2026-10-03
配列 3件
float
null

enum と match を組み合わせる

PHP 8.1 で追加された enum(列挙型)は、取りうる値があらかじめ決まっているため、match との相性がとてもよい組み合わせです。

<?php
declare(strict_types=1);

enum TaskStatus: string
{
    case Todo = 'todo';
    case Doing = 'doing';
    case Done = 'done';

    public function label(): string
    {
        return match ($this) {
            self::Todo => '未着手',
            self::Doing => '進行中',
            self::Done => '完了',
        };
    }

    public function isOpen(): bool
    {
        return match ($this) {
            self::Todo, self::Doing => true,
            self::Done => false,
        };
    }
}

foreach (TaskStatus::cases() as $s) {
    printf("%-5s %s open=%s\n", $s->value, $s->label(), var_export($s->isOpen(), true));
}

$fromDb = TaskStatus::from('doing');
echo $fromDb->label(), PHP_EOL;
var_dump(TaskStatus::tryFrom('archived'));
todo  未着手 open=true
doing 進行中 open=true
done  完了 open=false
進行中
NULL

enum では default を書かないのがおすすめ

すべてのケースを腕に書き、default をあえて省くと、あとで enum にケースを足したときに、対応していない match が UnhandledMatchError で見つかります。Archived を追加し、label 側を直し忘れた状態を再現しました。

<?php
enum TaskStatus: string
{
    case Todo = 'todo';
    case Doing = 'doing';
    case Done = 'done';
    case Archived = 'archived';   // 後から追加したケース
}

function label(TaskStatus $s): string
{
    return match ($s) {
        TaskStatus::Todo => '未着手',
        TaskStatus::Doing => '進行中',
        TaskStatus::Done => '完了',
    };
}

echo label(TaskStatus::Archived), PHP_EOL;
PHP Fatal error:  Uncaught UnhandledMatchError: Unhandled match case of type TaskStatus in enum_missing.php:15
Stack trace:
#0 enum_missing.php(19): label()
#1 {main}
  thrown in enum_missing.php on line 15

zend.exception_ignore_args=0 で実行すると、メッセージは Unhandled match case TaskStatus::Archived となり、どのケースが足りないかまで表示されました。default を書いていると「不明」などの既定値で静かに通ってしまうので、ケースが決まっている enum では default なしのほうが書きもれに気づけます。テストで全ケースを一度ずつ通しておくと、さらに確実です。

外から来た文字列は tryFrom で enum にしてから

メソッド 一致しない値を渡したとき 使いどころ
TaskStatus::from('x') ValueError を投げる 必ず正しい値が入っている前提のとき(DB の値など)
TaskStatus::tryFrom('x') null を返す フォームや URL の値など、想定外がありうるとき

tryFrom() で enum に変換し、null なら入力エラーとして扱い、以降は enum だけを match する、という流れにすると分岐が整理されます。

複数行の処理を書きたいときの対処

match の腕に書けるのは式を1つだけです。波かっこでブロックを書くと構文エラーになります。

<?php
$x = match (1) {
    1 => { echo 'a'; return 1; },
};
PHP Parse error:  syntax error, unexpected token "{" in err_block.php on line 3

複数行の処理が必要なときは、次の3つの方法があります。

<?php
declare(strict_types=1);

function notifyDone(string $title): string
{
    $message = "「{$title}」が完了しました";
    $message .= '(' . date('Y') . '年)';   // 複数行の処理は関数にまとめる
    return $message;
}

$status = 'done';
$title = '棚卸し';

// 1. 関数・メソッドに切り出す
$result = match ($status) {
    'todo' => '未着手です',
    'done' => notifyDone($title),
    default => '状態不明',
};
echo $result, PHP_EOL;

// 2. その場で無名関数を呼ぶ
$result = match ($status) {
    'done' => (function () use ($title) {
        $upper = mb_strtoupper($title);
        return "完了: {$upper}";
    })(),
    default => '状態不明',
};
echo $result, PHP_EOL;

// 3. 関数を返して後で呼ぶ
$handler = match ($status) {
    'todo' => fn(string $t) => "{$t} を開始します",
    'done' => fn(string $t) => "{$t} を記録しました",
    default => fn(string $t) => "{$t} は対象外です",
};
echo $handler($title), PHP_EOL;
「棚卸し」が完了しました(2026年)
完了: 棚卸し
棚卸し を記録しました
方法 読みやすさ 向いている場面
1. 関数・メソッドに切り出す 高い 基本はこれ。処理に名前が付き、テストもしやすい
2. その場で無名関数を呼ぶ 中 数行だけで、ほかから呼ばない処理
3. 関数を返して後で呼ぶ 中 「どの処理をするか」を先に決め、実行は後にしたい
switch や if に戻す 高い 分岐ごとに行う処理の形がばらばらなとき

各分岐で何行も処理をするなら、無理に match へ押し込まず switch や if elseif を選ぶのも立派な判断です。match は「値を決める」場面に絞ると、コードが読みやすくまとまります。

戻り値を使わない match

match を文のように使い、結果を捨てることもできます。この場合も末尾のセミコロンは必要です。

<?php
$log = [];
$action = 'save';
match ($action) {
    'save' => $log[] = '保存しました',
    'delete' => $log[] = '削除しました',
    default => $log[] = '何もしません',
};
print_r($log);
Array
(
    [0] => 保存しました
)

よく見るエラーと直し方

エラーメッセージ(PHP 8.4.21 で再現) 原因 直し方
Uncaught UnhandledMatchError: Unhandled match case ... どの腕にも一致せず default がない 腕を足す/default を書く/渡す値の型を確認
Parse error: syntax error, unexpected token "{" 腕にブロックを書いた 関数に切り出す
Parse error: syntax error, unexpected token "echo"(match の次の行) match の閉じかっこのあとの ; がない }; で終える
Fatal error: Match expressions may only contain one default arm default を2つ書いた 1つにまとめる
Unhandled match case of type bool(match (true) で) 腕の条件が bool 以外(1 など)を返している === 1 や !== false で bool にする

セミコロンを忘れたときの出力です。エラーの行番号は match の次の行を指すので、1つ上の } を確認します。

PHP Parse error:  syntax error, unexpected token "echo" in err_semi.php on line 5

直す前と後:数字の文字列を渡して UnhandledMatchError

// 直す前:$_GET の値は文字列なので、整数の腕と一致しない
$page = $_GET['page'] ?? '1';
$title = match ($page) {
    1 => '1ページ目',
    2 => '2ページ目',
};
// 直した後:整数に変換し、想定外の値は default で受ける
$page = (int) ($_GET['page'] ?? 1);
$title = match ($page) {
    1 => '1ページ目',
    2 => '2ページ目',
    default => 'ページが見つかりません',
};

(この2つは Web サーバー経由の $_GET を使うため未実行です。型が違うと一致しないこと自体は、前の節の $id = '5' の例で確認しています。)

if elseif・switch・match の使い分け

こんなとき おすすめ 理由
1つの値に応じて、返す値を決めたい match 短く書け、書きもれは例外でわかる
enum のケースごとに値を決めたい match(default なし) ケース追加時に未対応の箇所が見つかる
金額や点数などの範囲で値を決めたい match (true) 条件と結果が表のように並ぶ
条件ごとに見る変数が違う、条件が長い if / elseif 自由に条件を書ける
分岐ごとに複数の処理をしたい switch または if / elseif ブロックに何行でも書ける
上の case の処理に続けて下の case も実行したい switch フォールスルーを使える
緩い比較(==)で分けたい switch または if match は常に ===
キーと値の単純な対応だけ 配列 $map[$key] ?? '既定値' で済む
PHP 7.4 以前でも動かす必要がある switch / if match は 8.0 以降

最後の「配列」は、条件も結果も固定の値のときに使える書き方です。

<?php
declare(strict_types=1);

const LABELS = ['draft' => '下書き', 'published' => '公開中'];
echo LABELS['archived'] ?? '不明', PHP_EOL;
不明

switch を match に書き換えるときのチェックリスト

確認すること 理由
渡す値と腕の型がそろっているか == から === に変わるため、'1' と 1 は別物になる
break を省いてフォールスルーさせている case がないか match では次の腕へ進まない。カンマでまとめるか処理を分ける
default がない switch ではないか match では UnhandledMatchError になる。default を足すか、全パターンを腕に書く
各 case が複数行の処理をしていないか 腕は式1つ。関数へ切り出すか、switch のままにする
case の中で return・continue を使っていないか 腕には書けない。return match (...) の形に組み替える

よくある質問

Q. match 式はどの PHP から使えますか? A. PHP 8.0 以降で使えます。この記事のコードは PHP 8.4.21 で動かして確認しました。enum と組み合わせる例は、enum が追加された PHP 8.1 以降が必要です。

Q. switch はもう使わないほうがよいのでしょうか? A. いいえ、switch にも向いている場面があります。分岐ごとに複数行の処理をしたいときや、フォールスルーを使いたいときは switch が書きやすい選択です。「値を決めるなら match、処理を並べるなら switch や if」と役割で分けると選びやすくなります。

Q. match で「10以上20未満」のような範囲を判定できますか? A. match (true) を使うと判定できます。各腕に $n >= 10 && $n < 20 => ... のように bool を返す条件式を書きます。上から順に評価されるので、厳しい条件を先に並べるのがポイントです。

Q. UnhandledMatchError を try-catch で捕まえるべきですか? A. 多くの場合は、捕まえるよりも default を書くか、腕を足して起きないようにするのがおすすめです。UnhandledMatchError は「条件の書きもれ」を知らせる合図なので、想定外の入力がありうるなら default で既定値や分かりやすい例外を用意しておくと、原因を追いやすくなります。

まとめ

  • match は値を返す式。=== で照合し、break は不要
  • switch は == で比べるため、'0'・null・false がすべて case 0 に入る。match は型まで一致した腕だけを選ぶ
  • どの腕にも一致せず default もないと UnhandledMatchError。書きもれにその場で気づける
  • 範囲判定は match (true)。条件は厳しい順に並べ、bool を返す式にする
  • 複数の値はカンマで並べる。enum は default を省くとケース追加時の対応もれが見つかる
  • 腕に書けるのは式1つ。複数行の処理は関数に切り出すか、switch・if を選ぶ
  • 値を決めるなら match、条件が自由な形なら if elseif、処理を並べるなら switch

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「PHP match式の使い方|switchとの違いとif elseifの使い分け」

株式会社RJCでは、match 式をはじめとした PHP の条件分岐の書き方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?