値に依存した列挙型(Backed Enum)
デフォルトでは、列挙型はスカラー値の情報を持っていません。 単なるシングルトンオブジェクトです。 しかし、列挙型の case をデータベースや、 類似のデータストアで読み書きする必要があるケースが多くあります。 よって、ビルトインの (シリアライズ可能であることが自明な) スカラー値を持つ case があると、本質的に役に立ちます。
列挙型にスカラー値を定義するには、以下のようにします:
<?php
enum Suit: string
{
case Hearts = 'H';
case Diamonds = 'D';
case Clubs = 'C';
case Spades = 'S';
}
?>
スカラー値を持つ case を、"Backed Case" と呼びます。 なぜなら、オブジェクトよりもシンプルな値に依存して(Backed)いるからです。 全ての case が Backed Case である列挙型を "Backed Enum" と呼びます。 Backed Enum には Backed Case のみを含めることができます。 Pure Enum には Pure Case だけを含めることができます。
Backed Enum は、整数、または文字列の値を持つことができます。
そして、単一の列挙型が一度に持つことの出来る型はひとつだけです
(つまり、int|string
のような union
型はサポートしていないということです)。
列挙型がスカラー情報を持つとマークすると、
全ての case はユニークなスカラーの値を明示的に定義しなければいけません。
自動生成されるスカラー値 (例: 整数の連番) は存在しません。
Backed Enum の case の値は、全てユニークでなければいけません。
つまり、ふたつの Backed Enum の case は、
同じスカラー値を持ってはいけないということです。
しかし、定数は case を参照していても構わないので、
別名を作成することはできます。
列挙型と定数
も参照ください。
スカラー値は、リテラルか、リテラルを表す式でなければいけません。
定数や定数式はサポートしていません。
つまり、1 + 1
は許されますが、
1 + SOME_CONST
は許されません。
Backed Enum の case は、追加の読み取り専用のプロパティ
value
を持っています。
これは、Backed Enum の定義で指定された値です。
<?php
print Suit::Clubs->value;
// "C" と表示
?>
value
プロパティを強制的に読み取り専用にするため、
そのリファレンスを変数には代入できません。
つまり、以下のようなコードはエラーになります:
<?php
$suit = Suit::Clubs;
$ref = &$suit->value;
// Error: Cannot acquire reference to property Suit::$value
?>
Backed Enum は内部的に BackedEnum インターフェイスを実装しています。 このメソッドは、以下の2つのメソッドを公開しています:
-
from(int|string): self
スカラー値を受け取り、対応する Enum の case を返します。 対応する case がない場合は、 ValueError がスローされます。 このメソッドは、入力のスカラー値が信頼でき、 存在しない enum の値はアプリケーションを停止すべきエラーとみなせる場合に役立ちます。 -
tryFrom(int|string): ?self
スカラー値を受け取り、対応する Enum の case を返します。 対応する case がない場合は、null
を返します。 このメソッドは、入力のスカラー値が信頼できない場合で、 呼び出し側が独自のエラーハンドリングや、 デフォルト値のロジックを実装したい場合に役立ちます。
from()
と tryFrom()
メソッドは、標準の 弱い/強い 型付けのルールに従います。
弱い型付けのルールでは、整数または文字列を受け入れ、
システムは値をそれに従って自動変換します。
厳密な型付けモードの場合、文字列型の Backed Enum に整数値を渡す
(またはその逆をする) と、TypeError が発生します。
float についても、全ての場合に同じ型付けのルールに従います。
それ以外の型については、どちらのモードでも TypeError が発生します。
<?php
$record = get_stuff_from_database($id);
print $record['suit'];
$suit = Suit::from($record['suit']);
// 不正なデータを渡すと、次のようなエラーが発生 -> ValueError: "X" is not a valid scalar value for enum "Suit"
print $suit->value;
$suit = Suit::tryFrom('A') ?? Suit::Spades;
// 不正なデータに対しては null を返すので、Suit::Spades が代わりに使われます。
print $suit->value;
?>
Backed Enum において、手動で from()
や
tryFrom()
メソッドを定義すると、
致命的なエラーが発生します。