PHP8のmatch式を使ってみる!switchとは違うの?

こんにちは、さるまりんです🐵

今回は、PHP 8から導入された新しい分岐構文「match式」についてです。
switch構文に似ているようで、実はまったく違う書き方とメリットがあるみたい。
Dockerを使ってでPHP 7と8の環境を切り替えながら、実際に動かして比較してみました!

これまでのswitch文とその課題

PHPで条件分岐といえば、思い浮かぶのがswitch
例えば以下のように使います。

<?php
$fruit = 'apple';

switch ($fruit) {
    case 'apple':
        echo "It's an apple\n";
        break;
    case 'banana':
        echo "It's a banana\n";
        break;
    default:
        echo "Unknown fruit\n";
}

でも、ちょっと使いづらいかも…

breakを書き忘れるとおかしなことになりますし、値を返したい時に冗長になってしまいます。
また、switchの構文は式としては使えなくて、値をそのまま代入することができません。

match式でコードがスッキリ!

PHP 8では、switchに代わるスマートな書き方としてmatchが登場しました。

<?php
$fruit = 'banana';

echo match ($fruit) {
    'apple' => "It's an apple\n",
    'banana' => "It's a banana\n",
    default => "Unknown fruit\n",
};

matchのメリットは?

=>で結果を返す式ベースになっています。
そして、switchと違い、breakが不要です。
条件に一致するか否かの比較は===厳密に比較されます。
また、全ケースを網羅しないと例外となり、バグを見逃しにくくなっています。

値を返す使い方(matchの真価)

以下、値を返す例です。HTTPレスポンスのステータスに応じて、文言を返すようになっています。

<?php
$status = 302;

$message = match ($status) {
    200 => 'OK',
    301, 302 => 'Redirect',
    404 => 'Not Found',
    500 => 'Internal Server Error',
    default => 'Unknown status',
};

echo $message . PHP_EOL;

複数の値に対して同じ結果を返す書き方がシンプルになっていますね。
で、上の例ではmatchからそのまま変数$messageに代入しています。

注意:ケースが不足するとエラーになる

defaultは必須ではないけど、全てのケースをカバーできてないと例外を投げます。

<?php
$fruit = 'grape';

echo match ($fruit) {
    'apple' => 'Apple',
    'banana' => 'Banana',
    // default を忘れている!
};

実行結果:

Fatal error: Uncaught UnhandledMatchError

なので、全パターンを網羅するように設計する必要があるし、「その他」「デフォルト」はdeaultとして入れるようにするのがいいかと思いました。

もう少し複雑な使い方

例:得点による評価を返す

match(true)を使うと、こんなこともできます。

<?php
$score = 82;

$rank = match (true) {
    $score >= 90 => 'A',
    $score >= 70 => 'B',
    $score >= 50 => 'C',
    default => 'D',
};

echo "Score: $score, Rank: $rank\n";

どうですか?
こんなふうに条件評価の分岐に使うこともできます。

例:フォームの値によってアクションを分ける

<?php
$action = $_POST['action'] ?? 'none';

$result = match ($action) {
    'save' => '保存しました',
    'delete' => '削除しました',
    'update' => '更新しました',
    default => '不明なアクション',
};

echo $result;

HTMLフォームから送られてきた値によって動作を分けています。

Dockerを使ってPHP 7/8 の切り替え

ローカルに複数バージョンのPHPをインストールすると何か変なことが起きたりしそうなので、Dockerで独立した環境を作って試してみました。

# docker-compose.yml
services:
  php7:
    image: php:7.4-cli
    volumes:
      - ./src:/app
    working_dir: /app
    command: php switch_test.php

  php8:
    image: php:8.3-cli
    volumes:
      - ./src:/app
    working_dir: /app
    command: php match_test.php
# 実行
docker-compose run --rm php7
docker-compose run --rm php8

これでphp7switch_test.phpphp8match_test.phpを実行して試すことができます。

値に応じた数値からラベルへの変換処理、フォームの分岐処理、REST APIのステータス表示やテストデータの出力、いろんな使い道がありそうです。
switchで長くちょっと読みにくいコードを書いていたところにこのmatchを使えないか考えながら新しいものに使いこなせるようになっていきたいと思います。

読んでくださってありがとうございました。
それではまた!