banner
RandyChan

RandyChan

深漂 / Back-end developer
github

PHP 8.1 列挙型

列挙型(Enum)は、開発者がカスタム型を一連の可能な離散値の 1 つに定義できるようにします。 ドメインモデルを定義する際に非常に便利で、「無効な状態を表現できないようにする」(making invalid states unrepresentable)ことができます。

PHP では、列挙型は特別なタイプのオブジェクトです。Enum 自体はクラス(Class)であり、そのさまざまなエントリ(case)はこのクラスのシングルトンオブジェクトであり、有効なオブジェクトでもあります。つまり、型の検出が可能で、オブジェクトが必要な場所でも使用できます。

列挙型の定義#

列挙型はクラスに似ており、クラス、インターフェース、トレイトと同じ名前空間を共有します。 同じ方法で自動ロードも可能です。

列挙型を定義するには enum キーワードを使用し、case キーワードを使用して列挙の値を宣言します。たとえば、投稿の状態の列挙を定義する場合:

enum Status
{
    case DRAFT;
    case PUBLISHED;
    case ARCHIVED;
}

列挙型の利点は、定数値の集合を表すことですが、最も重要なのは、これらの値をこのように渡すことができることです:

class Post
{
    public function __construct(
        public Status $status, 
    ) {}
}

この例では、列挙型を作成し、それを Post に渡すと次のようになります:

$post = new Post(Status::DRAFT);

列挙型のメソッド#

列挙型は、クラスと同様にメソッドを定義できます。match演算子と組み合わせることができます。たとえば、列挙値に対応する色を取得する場合:

enum Status
{
    case DRAFT;
    case PUBLISHED;
    case ARCHIVED;
    
    public function color(): string
    {
        return match($this) 
        {
            Status::DRAFT => 'grey',   
            Status::PUBLISHED => 'green',   
            Status::ARCHIVED => 'red',   
        };
    }
}

// 列挙型のメソッドを呼び出す
$status = Status::ARCHIVED;
$status->color(); // 'red'

同時に静的メソッドの定義もサポートしています:

enum Status
{
    // …
    
    public static function getColor(Status $status): string
    {
        return $status->color();
    }
}

列挙型はselfキーワードを使用して現在の列挙型を表すこともサポートしています:

enum Status
{
    // …
    
    public function color(): string
    {
        return match($this) 
        {
            self::DRAFT => 'grey',   
            self::PUBLISHED => 'green',   
            self::ARCHIVED => 'red',   
        };
    }
}

列挙型の定数#

列挙型クラスには public、private、protected の定数を含めることができますが、継承をサポートしていないため、実際には private と protected の効果は同じです。

たとえば、デフォルトの状態の定数を定義する場合:

enum Status
{
    case DRAFT;
    case PUBLISHED;
    case ARCHIVED;

    public const DEFAULT = self::DRAFT;
}

列挙型インターフェース#

列挙型はクラスと同様にインターフェースを実装することもできます。

interface HasColor
{
    public function color(): string;
}
enum Status implements HasColor
{
    case DRAFT;
    case PUBLISHED;
    case ARCHIVED;
    
    public function color(): string { /* implement color */ }
}

列挙値(Backed enums)#

各列挙項目に値を割り当てることができ、列挙値は内部オブジェクトによって表されます。

enum Status: string
{
    case DRAFT = 'draft';
    case PUBLISHED = 'published';
    case ARCHIVED = 'archived';
}

列挙型定義の型宣言に注意してください。これは、すべての列挙値が指定された型であることを示します。intにすることもできます。列挙値として許可されるのはintstringのみです。

enum Status: int
{
    case DRAFT = 1;
    case PUBLISHED = 2;
    case ARCHIVED = 3;
}

値を持つ列挙型は「Backed enums」と呼ばれ、Backed enums はより良いシリアル化とデータベースへの書き込みを可能にします。列挙値を割り当てることを決定した場合、すべての列挙項目は値を持つ必要があり、混合してマッチさせることはできません。

Backed enums もインターフェースを実装できます。implementsは型の後に配置する必要があります。

enum Status: string implements HasColor
{
    case DRAFT = 'draft';
    case PUBLISHED = 'published';
    case ARCHIVED = 'archived';

    public function color(): string { /* implement color */ }
}

列挙型のシリアル化#

列挙値はシリアル化と逆シリアル化をサポートしています。各列挙値は、読み取り専用のvalueを介して値を取得できます。

$value = Status::DRAFT->value; // draft

また、Enum::from()メソッドを使用して値を列挙項目に戻すこともできます。from メソッドは列挙項目の値を受け取り、型が一致しない場合や値が存在しない場合はValueError例外をスローします。

$status = Status::from(1); // ValueError
$status = Status::from('unknown'); // ValueError
$status = Status::from('draft'); // Status::DRAFT

この列挙値が存在するかどうか不明で、存在しない場合にnullを返すことを期待する場合は、Enum::tryFrom()メソッドを使用できます。

$status = Status::tryFrom('unknown'); // null
$status = Status::from('draft'); // Status::DRAFT

列挙型のリスト#

Enum::cases()メソッドを使用して、すべての列挙項目を取得できます。

Status::cases();

/* [
    Status::DRAFT, 
    Status::PUBLISHED, 
    Status::ARCHIVED
] */

このメソッドが返すのは配列型で、各列挙オブジェクトが含まれています。このメソッドを使用して、すべての列挙をループ処理できます。

array_map(
    fn(Status $status) => $status->color(), 
    Status::cases()
); // ['grey', 'green', 'red']

列挙オブジェクト#

実際には、各列挙値はシングルトンオブジェクトです。つまり、次のように列挙値を比較できます:

$statusA = Status::PENDING;
$statusB = Status::PENDING;
$statusC = Status::ARCHIVED;

$statusA === $statusB; // true
$statusA === $statusC; // false
$statusC instanceof Status; // true

列挙値は実際にはオブジェクトであるため、現在のところそれらを配列のキーとして使用することはできません。以下はエラーを引き起こします。

$list = [
    Status::DRAFT => 'draft', // TypeError
    // …
];

トレイト#

列挙型もトレイトを使用でき、動作はクラスと同様です。列挙型でトレイトを使用する際には、プロパティを含めることはできません。メソッドや静的メソッドのみを含むことができます。プロパティを含むトレイトは致命的なエラーを引き起こします。

参考リンク#

謝辞#

ご覧いただきありがとうございます。共に知識を探求し、成長していきましょう。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。