列挙型(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
にすることもできます。列挙値として許可されるのはint
とstring
のみです。
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
// …
];
トレイト#
列挙型もトレイトを使用でき、動作はクラスと同様です。列挙型でトレイトを使用する際には、プロパティを含めることはできません。メソッドや静的メソッドのみを含むことができます。プロパティを含むトレイトは致命的なエラーを引き起こします。
参考リンク#
謝辞#
ご覧いただきありがとうございます。共に知識を探求し、成長していきましょう。