PHP8以降の比較とmatch文について

便利だけど使う前に挙動をしっかり理解しておきたいので。
予習メモです。

# 環境構築
PHP8の環境を用意します。
開発環境を汚したくない場合は[Docker](https://docs.docker.com/get-docker/)がおすすめです。

簡単なスクリプトならDockerfileを作成する必要もありません。
rmオプションを使用することにより、スクリプト実行後にコンテナが削除されます。

```bash
$ mkdir php8_test
$ cd php8_test
$ vi mytest.php
$ docker run -it --rm --name my-running-script -v "$PWD":/usr/src/php8_test -w /usr/src/php8_test php:8.0-cli php mytest.php 
```

[公式ドキュメント](https://github.com/docker-library/docs/tree/master/php)にも紹介されています。

# 数値と文字列の比較
数値と文字列の比較について、``文字列が数値形式か否か``で変換の対象が変わります。

[公式サイト](https://www.php.net/releases/8.0/ja.php)より
>数値形式の文字列と比較する場合は、PHP 8 は数値として比較を行います。それ以外の場合は、数値を文字列に変換し、文字列として比較を行います。

## PHP7以前
``'foobar'``を数値に変換して比較します。
``(int)'foobar'``は``0``なので、結果がtrueになってしまいます。

[公式サイト](https://www.php.net/manual/ja/language.types.integer.php)より
>文字列が 数値形式の文字列 の場合は、対応する整数値に解決されます。 そうでない場合は、ゼロ (0) に変換されます。

```mytest.php
var_dump(0 == 'foobar');
//> bool(true)
var_dump(in_array(0, ['foobar']));
//> bool(true)
```

## PHP8以降
``'foobar'``は数値形式の文字列ではないので、``0``を文字列に変換して比較します。
``(string)0``は``'0'``なので、結果はfalseになります。

```mytest.php
var_dump(0 == 'foobar');
//> bool(false)
var_dump(in_array(0, ['foobar']));
//> bool(false)
```
# match式
PHP8から追加された``match式``を紹介します。
「厳密な比較ができるswitch文」として利用できそうです。

[公式サイト](https://www.php.net/manual/ja/control-structures.match.php)より
>match 式は、値の一致をチェックした結果に基づいて評価結果を分岐します。 switch 文と似ていますが、 match 式は複数の候補と比較される制約式を持ちます。 switch 文とは異なり、 三項演算子のように値を評価します。 switch 文とは異なり、 弱い比較(==)ではなく、 型と値の一致チェック(===) に基づいて行われます。

```php
$return_value = match (制約式) {
    単一の条件式 => 返却式,
    条件式1, 条件式2 => 返却式,
};
```

## PHP7以前
ご存知のとおり、``switch文``では``厳密な比較(===)``が行われません。

```mytest.php
$param = 1;

# switch文
switch ($param) {
    case '1':
        str_func();
        break;
    case 1:
        int_func();
        break;
    default:
        default_func();
}
//> Param is string!

function str_func()
{
    echo "Param is string!\n";
}

function int_func()
{
    echo "Param is integer!\n";
}

function default_func()
{
    echo "Default!\n";
}
```

そのため、条件分岐で厳密な比較を行う場合は``if文``や``三項演算子``を利用する必要がありました。

```mytest.php
# if文
if ($param === '1') {
    str_func();
} elseif ($param === 1) {
    int_func();
} else {
    default_func();
}
//> Param is integer!

# 三項演算子
$param === '1' ? str_func() : ($param === 1 ? int_func() : default_func());
//> Param is integer!
```

## match式の使いどころ

「本当はswich文を使いたいけれど厳密な比較が出来ないから使えない・・・ぐぬぬ・・・」のような``分岐が多い処理``で役に立ちます。
下記コードのように``$param``の種類ごとに処理が存在する場合は活躍できそうです。
また、記述できるのは一行の式だけですが``break;``が必要ないためコードがスッキリします。

```mytest.php
match ($param) {
    '1' => str_func(),
    1, => int_func(),
    default => default_func(),
};
//> Param is integer!
```

逆に分岐が少ない場合はさほどメリットが無いかもしれません。

## match式で厳密な比較を行わない方法
興味深い使い方もありました。

[公式サイト](https://www.php.net/manual/ja/control-structures.match.php)より
>制約式に true を指定することで、 厳密な一致チェックを行わずに match 式を使うことができます。

下記コードは制約式に``true``を指定しているため、条件式の``in_array($param, $str_arr)``の結果を元に条件分岐が行われます。
ですが``in_array``の第3引数にtrueが指定されていないので厳密な比較が実行されず、match式の結果にもそれが反映されてしまいます。

```mytest.php
$param = 1;

$str_arr = ['1', '2'];
$int_arr = [1, 2];

//  制約式にtrueを指定
match (true) {
    in_array($param, $str_arr) => str_func(),
    in_array($param, $int_arr) => int_func(),
    default => default_func(),
};
//> Param is string!
```

この場合は条件式で厳密な比較を行う必要があります。

```mytest.php
match (true) {
    in_array($param, $str_arr, true) => str_func(),
    in_array($param, $int_arr, true) => int_func(),
    default => default_func(),
};
//> Param is integer!
```

新しい機能は使いたくなるのが人の常ですが、使いどころは見極めていきたいです。