ツイート
シェア
LINEで送る
B! はてぶでブックマーク
Pocketでブックマーク
RSSフィード

PHP8.1, 定数値を入れる列挙型(Enum)。Backed Enum と言うらしい。

php
イラストダウンロードサイト【イラストAC】
の画像をもとに加工しています。

PHP8.1から、他のプログラム言語にはすでにある列挙型(Enum)が追加されました。

基本形は定数値を入れずに定数だけを定義するんですが、定数値を入れる型もあります。Backed Enumと言います。

基本形とあまり変わらないので難しくないです。

ここでは、列挙型の基本形は分かってる前提で進めます。基本形についてはこちらをどうぞ。

(基本形の列挙型のことを Pure Enum という。)

Backed Enum の定義

Pure Enum に対して、Backed Enumの定義はこうなります。

Pure: 定数値なし
enum Color
{
    case Red;
    case Green;
    case Blue;
    case Yellow;
    case White;
    case Black;
}
Backed: 定数値あり
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)とのミックスはできません。

Pure Enum に Backedを混ぜる
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
Backed Enum に Pure を混ぜる
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を返す。
チェックNG時の戻り値

また、両メソッドで指定するパラメータは数字の場合、キャストしてくれます。

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の実装もできる。

前の投稿
PHP8.1, Enum(列挙型)の追加。定数専用のクラス。
PHP8.1, 列挙型(Enum)にメソッド・インターフェイスが追加できる。継承はできない。
次の投稿
コメントを残す

*