グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



PHP8で実装されたmatch式がとても便利だったのでまとめてみた

PHP7.4のセキュリティサポートが2022年11月28日に終了し、PHP8に移行していくなかで、新しく実装されたmatch式がとても便利で使いやすかったので勉強も兼ねてまとめてみようと思います。

match式とは

match 式は、値の一致をチェックした結果に基づいて評価結果を分岐します。 switch 文と似ていますが、 match 式は複数の候補と比較される制約式を持ちます。 switch 文とは異なり、 三項演算子のように値を評価します。 switch 文とは異なり、 弱い比較(==)ではなく、 型と値の一致チェック(===) に基づいて行われます。 match 式は PHP 8.0.0 以降で利用可能です。PHPドキュメント

match式の構文

基本的なmatch式の使い方から見ていきます。

// 例1 match 式の構造
<?php
$return_value = match (制約式) {
    単一の条件式 => 返却式,
    条件式1, 条件式2 => 返却式,
};
?>
// 例2 基本式な match 式の使い方
<?php
$food = 'cake';

$return_value = match ($food) {
    'apple' => 'This food is an apple',
    'bar' => 'This food is a bar',
    'cake' => 'This food is a cake',
};

var_dump($return_value); // string(19) "This food is a cake"
?>

制約式とマッチする条件式に対応する返却式が返り値としてreturnされます。

下記のように条件式には複数指定することも可能です。

<?php
$result = match ($x) {
    // この分岐は:
    $a, $b, $c => 5,
    // 以下の3つの分岐と等しい:
    $a => 5,
    $b => 5,
    $c => 5,
};
?>

すべてのパターンを条件式で網羅する必要がありますが、switch文のようにdefaultでどの条件式にもマッチしなかった場合の返り値を設定することも可能です。

<?php
$expressionResult = match ($condition) {
    1, 2 => foo(),
    3, 4 => bar(),
    default => baz(),
};
?>

switch文との違い

match式の比較は、 switch文が行う弱い比較ではなく、 厳密に値を比較(===) します。

switch文の式は緩い型比較(==)なので使い方によっては予期しないバグを引き起こす場合がありました。 match式は厳密な型比較なのでこれだけでもmatch式を使っていきたいポイントですね。

match式の分岐は、 switch文のように後の分岐に抜けたりはしません。

switch文ではマッチした条件式以下のコードを実行してしまいます。
下記の例では case 2: でマッチし、以下のコードも実行してしまうためdumpの結果が default になってしまいます。

<?php

$result = '';

switch (1+1) {
case 1: $result = 'case 1';
case 2: $result = 'case 2';
case 3: $result = 'case 3';
default: $result = 'default';
}

var_dump($result); // string(7) "default"
?>

match式で書き直したのが下記のコードになります。

<?php

$result = match(1+1) {
    1 => 'case 1',
    2 => 'case 2',
    3 => 'case 3',
};

var_dump($result); // string(6) "case 2"
?>

switch文でも各コード内で break; することで同じ結果を得ることが出来ますが、その分コード量が増えるためmatch式を使っていきたいですね!

match式は、全ての場合を網羅していなければいけません。

match式では条件式にすべての場合を条件式に記載しないと UnhandledMatchError がスローされます。

<?php

try {
$result = match(10) {
    1 => 'case 1',
    2 => 'case 2',
    3 => 'case 3',
};
} catch (\UnhandledMatchError $e) {
    var_dump($e);
    exit;
}

var_dump($result);
?>

上記の結果は下記のようになります。

object(UnhandledMatchError)#1 (7) {
  ["message":protected]=>
  string(33) "Unhandled match value of type int"
  ["string":"Error":private]=>
  string(0) ""
  ["code":protected]=>
  int(0)
  ["file":protected]=>
  string(41) "/home/suzuki/blog-cords/2022-09/match.php"
  ["line":protected]=>
  int(4)
  ["trace":"Error":private]=>
  array(0) {
  }
  ["previous":"Error":private]=>
  NULL
}

switch文だと条件式に当てはまらない場合はそのままスルーする動作になるため、入力値によるランタイムなエラーが補足できない可能性があります。

<?php

$result = '';

switch (10) {
case 1: $result = 'case 1';
case 2: $result = 'case 2';
case 3: $result = 'case 3';
}

var_dump($result); // string(0) ""
?>

どの条件式にもマッチしない場合はスルーさせたいという場合もあるので、場合によって使い分けていけると良いですね。

さいごに

筆者は可読性の点からif-ifelse文よりswitch文を好んで使ってきました。 しかし、緩い比較のため制約式を無理やり厳密な比較で記載し、逆に自分以外の可読性が落ちてしまう(=3か月後の自分も読みづらい)コードになってしまう場合がありました。

match式であれば、もともと厳密な比較であることと、条件式にマッチしない場合にエラーを返してくれてランタイムなエラーに気づけるため、PHP8以上の開発では積極的にmatch式を使っていこうと思いました。

この記事を書いた人

ばね
ばねソリューション事業部 システムエンジニア
東京で2年半エンジニアとしての経験を積み、浜松にUターンの後、アーティスへ入社。
ソリューション事業部のWebエンジニアとして、システムの設計・開発・保守・運用からインフラまで幅広く従事している。
フルスタックエンジニア目指して現在も勉強の日々。車が好き。
この記事のカテゴリ

FOLLOW US

最新の情報をお届けします