PHP8.1から、他のプログラム言語にはすでにある列挙型(Enum)が追加されました。
基本形は定数値を入れずに定数だけを定義するんですが、定数値を入れる型もあります。Backed Enumと言います。
基本形とあまり変わらないので難しくないです。
ここでは、列挙型の基本形は分かってる前提で進めます。基本形についてはこちらをどうぞ。
(基本形の列挙型のことを Pure Enum という。)
Backed Enum の定義
Pure Enum に対して、Backed Enumの定義はこうなります。
enum Color
{
case Red;
case Green;
case Blue;
case Yellow;
case White;
case Black;
}
enum Color: string
{
case Red = 'red';
case Green = 'green';
case Blue = 'blue';
case Yellow = 'yellow';
case White = 'white';
case Black = 'black';
}
定義上のちがいは定数値の型をenumクラスで指定するのと、定数値を代入することだけ。
ただ、たったこれだけのちがいで、Backed Enumにはいろいろな制限が加わります。
定数値の型
Backed Enum では、定数値の型を指定しなければなりません。指定しないとエラーになります。
enum Color
{
case Red = 'red';
}
PHP Fatal error: Case Red of non-backed enum Color must not have a value, try adding ": string" to the enum declaration in /home/vagrant/enum.php on line 4
定数値の型は1つだけ指定できます。そして、すべてのcaseオブジェクトの値はそれに従わないといけません。
enum Color: string
{
case Red = 'red';
case Green = 1;
}
PHP Fatal error: Enum case type int does not match enum backing type string in /home/vagrant/enum.php on line 5
指定できる定数値の型は "int" か "string" のみです。
enum Color: array
{
case Red = [1,2];
}
PHP Fatal error: Enum backing type must be int or string, array given in /home/vagrant/enum.php on line 2
いろんな型の定数値を混ぜると、それはenumクラスでグループ化する意味はないよね? ということらしい。
ゆるさがなく使い勝手が悪く感じる人がいるかもしれませんが、オブジェクト指向では1つの種類や役割をひとくくりにする仕様なので、そういうもんです。
Pure と Backed のミックスはできない
Backed Enum は定数値の型を明示しなければならないことから、基本形の列挙型(Pure Enum)とのミックスはできません。
enum Color
{
case Red;
case Green = 'green';
}
PHP Fatal error: Case Green of non-backed enum Color must not have a value, try adding ": string" to the enum declaration in /home/vagrant/enum.php on line 5
enum Color: string
{
case Red;
case Green = 'green';
}
PHP Fatal error: Case Red of backed enum Color must have a value in /home/vagrant/enum.php on line 4
定数に入れられる値、式
定数値はリテラルかリテラルの式です。リテラルとは変数や定数を使わない直書きの値。
もちろん、型を合わせないといけないので、string に 1 + 1 はダメだし、int に 1 . 1 は入れられません。
enum Color: string
{
case Red = 1 . 1; // OK
case Green = '1' . '2'; // OK
}
enum Color2: int
{
case Red = 1 + 1; // OK
}
enum Color: string
{
case Red = 1 + 1; // NG
}
enum Color2: int
{
case Red = 1 . 1; // NG
}
PHP Fatal error: Enum case type int does not match enum backing type string in /home/vagrant/enum.php on line 5
const TEST = 'test';
$test = 'test';
enum Color: string
{
case Red = TEST; // NG
case Green = $test; // NG
}
PHP Fatal error: Enum case value must be compile-time evaluatable in /home/vagrant/enum.php on line 7
もちろん関数もリテラルじゃないのでNGです。また、キャストもNGです。
enum Color: string
{
case Red = (string)3; // NG
}
PHP Fatal error: Enum case value must be compile-time evaluatable in /home/vagrant/enum.php on line 5
さらに値の重複もNG。caseオブジェクトの値は、enumクラス内でユニークである必要があります。
enum Color: string
{
case Red = 'color';
case Green = 'color';
}
PHP Fatal error: Duplicate value in enum Color for cases Red and Green in /home/vagrant/enum.php on line 6
Backed Enumのプロパティ(変数名と値)
Pure Enum では、caseオブジェクトにnameプロパティがあって変数名が取得できました。Backed Enum ではそれに加え、value で値も取得できます。
enum Color: string
{
case Red = 'red';
case Green = 'green';
case Blue = 'blue';
case Yellow = 'yellow';
case White = 'white';
case Black = 'black';
}
var_dump(Color::Red->name);
var_dump(Color::Red->value);
string(3) "Red"
string(3) "red"
valueは読み取り専用なので、リファレンス代入はできません。
$color = Color::Red;
$val = &$color->value;
PHP Fatal error: Uncaught Error: Cannot modify readonly property Color::$value in /home/vagrant/enum.php:14
Stack trace:
#0 {main}
thrown in /home/vagrant/enum.php on line 14
Backed Enum のメソッド
Pure Enum にはcases()がありましたが、Backed Enumにも実装されてます。
var_dump(Color::cases());
array(6) {
[0]=>
enum(Color::Red)
[1]=>
enum(Color::Green)
[2]=>
enum(Color::Blue)
[3]=>
enum(Color::Yellow)
[4]=>
enum(Color::White)
[5]=>
enum(Color::Black)
}
それに加え、Backed Enum には、定数値の有無チェックのメソッドが用意されてます。
var_dump(Color::from('red'));
var_dump(Color::tryFrom('red'));
enum(Color::Red)
enum(Color::Red)
どちらも定数値をパラメータで渡してチェックするんですが、結果の出し方が違います。
from() | ValueError例外のスロー。 |
tryFrom() | nullを返す。 |
また、両メソッドで指定するパラメータは数字の場合、キャストしてくれます。
enum Color: string
{
case Red = '1';
case Green = '2';
}
var_dump(Color::from(1));
var_dump(Color::tryFrom(1+1));
enum Color2: int
{
case Red = 1;
case Green = 2;
}
var_dump(Color2::from('1'));
var_dump(Color2::tryFrom('1'));
enum(Color::Red)
enum(Color::Green)
enum(Color2::Red)
enum(Color2::Red)
これらのメソッドは、Blocked Enum 専用のインターフェイス BackedEnum に定義されています。
ただし、オーバーライドはできません。
Blocked Enumもクラスの一種なので、自作のメソッド、インターフェイスの実装から、定数の定義までできます。traitの実装もできる。
PHP公式ドキュメント