[PMMP] Enum(列挙型)を使おう！

※この記事はPocketMine-API 4.0.0が対象です

# PMMPではEnumがPHP8.1じゃなくても使える！
PMMPには、[EnumTrait](https://github.com/pmmp/PocketMine-MP/blob/4.0.0/src/utils/EnumTrait.php)というTrait(機能)が実装されているので、これを使うことで実装することができます。

# Enumを使う理由
Enumを使用しない場合、例えば以下のようなコードになると思います。

```php
class Trump
{
    public const SUIT_CLUB = 0;
    public const SUIT_DIAMOND = 1;
    public const SUIT_HEART = 2;
    public const SUIT_SPADE = 3;

    private int $suit;

    public function __construct(int $suit)
    {
        switch ($suit) {
            case self::SUIT_CLUB:
            case self::SUIT_DIAMOND:
            case self::SUIT_HEART:
            case self::SUIT_SPADE:
                break;
            default:
                throw new InvalidArgumentException("Invalid suit");
        }
        $this->suit = $suit;
    }
}

$trump = new Trump(Trump::SUIT_CLUB);
```

しかし、この方法には

- 型がintのため任意の値を渡すことができ、渡された値が不正でないか検証する必要がある
- 使う側が、何の値を入れれば良いのか直感的にわかりづらい

といった問題があります。
そこで列挙型を使うと次のようになり、問題を解決することができます。

```php
class Trump
{
    private Suit $suit;

    public function __construct(Suit $suit)
    {
        $this->suit = $suit;
    }
}

$trump = new Trump(Suit::CLUB());
```

※Suitクラスはこれから説明するため省略

引数が整数ではなくクラスであるため値の検証がいらず、渡す側もタイプヒンティングによって分かりやすくなります。

# Enumクラスの作り方
Enumを使用するためには、`pocketmine\utils\EnumTrait`をuseしたクラスを定義する必要があります。
※クラス外に置いているよく使うuseとは意味が違うので注意！
(この記事で作成する)Enumクラスは継承されることを想定していないので、クラスにfinalと指定しておきましょう。

`setup()`メソッドの中で、Enumの要素を全て登録します。
ここでは`registerAll()`メソッドを使用していますが、`register()`メソッドを使用することもできます。[コード](https://github.com/pmmp/PocketMine-MP/blob/4.0.0/src/utils/EnumTrait.php#L33-L40)

クラス定義の上に`/** ～ */`で囲まれたDocブロックがありますが、これは主に静的解析ツールによる解析を補助するため(それによって生成されるドキュメント含む)であり、動作には影響ありませんが可読性のためにも書いておくことを推奨します。

この例では、トランプのマーク(Suit)のEnumを実装しています。

```php:Suit.php
use pocketmine\utils\EnumTrait;

/**
 * @method static Suit CLUB()
 * @method static Suit DIAMOND()
 * @method static Suit HEART()
 * @method static Suit SPADE()
 */
final class Suit
{
    use EnumTrait;

    protected static function setup(): void
    {
        self::registerAll(
            new self("club"),
            new self("diamond"),
            new self("heart"),
            new self("spade"),
        );
    }
}
```

<!--
# Enumクラス用のドキュメントを作成する
これは静的解析ツールが解析しやすくするためのもので動作には影響しませんが、可読性のためにも作成することをおすすめします。

## 生成ツールを使用する
PMMPには標準でこれの生成ツールが実装されていて、[`build/generate-registry-annotations.php`](https://github.com/pmmp/PocketMine-MP/blob/4.0.0/build/generate-registry-annotations.php)を使用することで自動で作成することができます。

あらかじめ[pmmp/PocketMine-MP](https://github.com/pmmp/PocketMine-MP/)をcloneしておくなどで、`build/generate-registry-annotations.php`を使えるようにしておいてください。

まず、クラス定義の上に`@generate-registry-docblock`を含むDocブロックを作成します。


以下のコマンドをコマンドプロンプトやシェルで実行すると、自動で作成されます。
phpのパスが通っていない場合はパスを通すか、phpまでのパスを指定します。(`./bin/php/php.exe`や`./bin/php7/bin/php`など)

```shell
php [build/generate-registry-annotations.phpまでのパス] [Enumクラスの.phpファイルがあるディレクトリのパス]
```

※この処理は再帰的に行ってくれるので、プラグインの場合は`src/`などのルートパスを指定しても良いです。

`Writing changed file ファイル名`と表示されたら完了です。

## 手動で作成する
クラス定義の上に`/** ～ */`で囲まれたこのようなDocブロックを追加しましょう。

```php:Suit.php
use pocketmine\utils\EnumTrait;


final class Suit
{
    use EnumTrait;

    protected static function setup(): void
    {
        self::registerAll(
            new self("club"),
            new self("diamond"),
            new self("heart"),
            new self("spade"),
        );
    }
}
```
-->

# 使い方

## Enumを生成する

```php
$suit = Suit::DIAMOND();//これはSuitクラスのインスタンス
```

## Enumの名前を取得する

```php
$suit = Suit::DIAMOND();
$suit->name();//string "diamond"
```

## Enumの数値IDを取得する

これは配列のインデックスなどに使用します。この値はサーバーを起動するたびに変わるので、Configに保存するなどの用途には使用できません。

```php
$suit = Suit::DIAMOND();
$suit->id();//int 0
```

## EnumがBであるか

```php
$suit = Suit::DIAMOND();
$suitB = Suit::HEART();

$suit->equals($suitB);//bool false
```