PHP 8.1 Enums を使ってみる

## 列挙型(Enumerations)

PHP8.1で実装されたEnumの使い方を軽くご紹介します。

## 環境

```shell-session
$ php -v
PHP 8.1.1 (cli) (built: Dec 17 2021 23:49:52) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.1, Copyright (c) Zend Technologies
    with Zend OPcache v8.1.1, Copyright (c), by Zend Technologies
```

## 使い方

```php
<?php declare(strict_types=1);

enum AccountStatus
{
    case Active;
    case Inactive;
    case Leave;
}

var_dump(AccountStatus::Active); // enum(AccountStatus::Active)
var_dump(AccountStatus::Active->name); // string(6) "Active"
var_dump(AccountStatus::cases());
// array(3) {
//   [0]=>
//   enum(AccountStatus::Active)
//   [1]=>
//   enum(AccountStatus::Inactive)
//   [2]=>
//   enum(AccountStatus::Leave)
// }
```

`enum` と宣言して列挙型を宣言する。
`case` を使って列挙していく。大文字も小文字も使える

`AccountStatus::Active` とするとenumのインスタンスができる。
インスタンスの`->name`プロパティへアクセスするとcaseの値を取得できる。
`cases()` でenum値の一覧を取得できます。

```php
<?php declare(strict_types=1);

enum AccountStatus: string
{
    case ACTIVE = 'active';
    case INACTIVE = 'inactive';
    case LEAVE = 'leave';

    /**
     * @return string
     */
    public function description(): string
    {
        return match($this) {
            self::ACTIVE => '有効',
            self::INACTIVE => '無効',
            self::LEAVE => '退会済み',
        };
    }
}

var_dump(AccountStatus::ACTIVE); // enum(AccountStatus::ACTIVE)
var_dump(AccountStatus::ACTIVE->name); // string(6) "ACTIVE"
var_dump(AccountStatus::ACTIVE->value); // string(6) "active"
var_dump(AccountStatus::from('active')); // enum(AccountStatus::ACTIVE)
// var_dump(AccountStatus::from('undefined status')); // PHP Fatal error:  Uncaught ValueError
var_dump(AccountStatus::tryFrom('active')); // enum(AccountStatus::ACTIVE)
var_dump(AccountStatus::tryFrom('undefined status')); // NULL
var_dump(AccountStatus::ACTIVE->description()); // string(6) "有効"
var_dump(AccountStatus::LEAVE->description()); // string(12) "退会済み"
var_dump(AccountStatus::cases());
// array(3) {
//   [0]=>
//   enum(AccountStatus::Active)
//   [1]=>
//   enum(AccountStatus::Inactive)
//   [2]=>
//   enum(AccountStatus::Leave)
// }
```

列挙型はスカラー値情報をデフォルトでは持ってませんが、スカラー値を持つcase(Backed Case)を定義できます。 
intまたはstringのスカラー値をサポートしてます。

Backed Enumは内部的に[Backed Enumインターフェース](https://www.php.net/manual/ja/class.backedenum.php)を実装しています。
`from($value)` や `tryFrom($value)` でスカラー値からEnumのcaseを返します。

```php
<?php declare(strict_types=1);

final class Account
{
    public function __construct(
        private AccountStatus $accountStatus,
    ) {
    }
}

$account = new Account(AccountStatus::Active);
$account = new Account('undefined status');
// Fatal error: Uncaught TypeError: Account::__construct(): ...
```

他のクラスのコンストラクタで列挙型を型宣言して受け取れる。
未定義の値を渡すと型エラーが発生するので不正な値が入ることを防いでくれる。安心。

## Enumとクラスの違い

- 状態を持つことが禁止
- コンストラクタやデストラクタは禁止
- 継承は禁止
- プロパティを持つことは禁止
- public, private, protected メソッド は利用可能
- public, private, protected な static メソッド は利用可能
- public, private, protected な定数 は利用可能
- インターフェースの実装 は利用可能
- マジックメソッド `__call`, `__callStatic`, `__invoke` は利用可能。これ以外は禁止
- マジック定数 `__CLASS__` と `__FUNCTION__` は利用可能。これ以外は禁止

## 感想

思ったより扱いやすそう！早く実務で使ってみたい。
ただ、インターフェース使ったEnumはいまいち実務で使うイメージができなかった。
今までは[BenSampo/laravel-enum](https://github.com/BenSampo/laravel-enum)パッケージを使ってたけど乗り換えて良さそう。

## 参考

- https://www.php.net/manual/ja/language.enumerations.overview.php