
ブログをNext.js14(App Router)でリファクタリングしました
PHPの列挙型について解説します。PHPの列挙型はメソッドを持つことが出来たり、インターフェースの実装、トレイトの利用も可能で様々な表現ができます。本記事は公式ドキュメントをもとに解説しますが、要点のみを抑えて簡潔な表現を心がけたり、具体例を増やすことによって分かりやすさに重点を置いています。
目次
列挙型は、開発者が独自に定義した値のみを取る型です。enumで宣言します。
以下に具体例を挙げます。
<?php
enum Size
{
case Small;
case Medium;
case Large;
}
Sizeというenumを宣言しました。
このときSizeは、caseで指定した3つの値(Small, Medium, Large)のみを取ります。
caseは0個以上、好きなだけ定義することが可能です。
次のようにして::演算子でアクセスします。
$a = Size::Small;
$b = Size::Large;
これが一体何の役に立つかというと、例えば、関数の引数の値を限定できて型安全になります。
次のコードはSizeで定義された3つのcaseのみを受け取ることが保証されます。
function hoge (Size $size)
{
var_dump($size);
}
hoge(Size::Small); //OK
//hoge(3); //NG
概要を押さえたところで、PHPの列挙型について詳しく取り上げていきます。
(参考) 列挙型はPHP8.1からの新機能です。
PHP8.1には他にも多くの新機能があります。
列挙型はクラスに似ています。::演算子で参照されたcase値は、列挙型のインスタンスとなります。
$a = Size::Small;
var_dump($a instanceof Size); //bool(true)
また、このインスタンスはシングルトンです。
$a = Size::Small;
$b = Size::Small;
var_dump($a === $b); //bool(true)
それぞれのインスタンスは読み取り専用のnameプロパティを持ちます。caseで定義した名前そのものを返します。
$a = Size::Small;
$b = Size::Medium;
var_dump($a->name); //string(5) "Small"
var_dump($b->name); //string(6) "Medium"
列挙型にはPure EnumとBacked Enumがあります。
前章のように定義された列挙型をPure Enumと言います。
再掲します。
enum Size
{
case Small;
case Medium;
case Large;
}
もう一方で、次のように定義したEnumをBacked Enumと言います。
enum Size: string
{
case Small = "s";
case Medium = "m";
case Large = "l";
}
その特徴は、case式が右辺にスカラー値を持たせることが可能なことです。
int型かstring型のみを持つことができ、その型を列挙型名の後ろの:の後に記述します。
ただし、スカラー値はすべてユニークにする必要があります。
valueプロパティを使うことで、定義したスカラー値を参照することが可能です。
$a = Size::Small;
$b = Size::Medium;
var_dump($a->value); //string(1) "s"
var_dump($b->value); //string(1) "m"
また、fromメソッドを用いることでスカラー値からcase名を取得することも可能です。
これには2つのメソッドがあり、該当case名がない場合にエラーを投げる from()メソッドと、null値を返すtryFrom()があります。
$a = Size::from("s");
var_dump($a); //enum(Size::Small)
$b = Size::tryFrom("hoge") ?? Size::Large;
var_dump($b); //enum(Size::Large)
列挙型はメソッドを持つことが可能で、インスタンスからアクセスできます。
<?php
enum Size: string
{
case Small = "s";
case Medium = "m";
case Large = "l";
//$thisで自身のcaseを返す
public function thisCase(): Size
{
return $this;
}
//どのcaseでも一律に文字列を返す
public function hoge(): string
{
return "hoge";
}
}
$a = Size::Small;
$b = Size::Large;
var_dump($a->thisCase()); //enum(Size::Small)
var_dump($b->hoge()); //string(4) "hoge"
ここで、メソッドにpublic修飾子をつけていることからわかるように、アクセス修飾子の指定が可能です。
しかし、列挙型は継承できないため、protectedもprivateも同じものとなります。
メソッド内の$thisで自身のcaseを返すので、例えばmatch式と組み合わせてcaseによって条件分岐するメソッドの実装も可能です。
<?php
enum Size: string
{
case Small = "s";
case Medium = "m";
case Large = "l";
//$thisで自身のcaseを返す
public function price(): int
{
return match($this) {
Size::Small => 10,
Size::Medium => 100,
Size::Large => 1000
};
}
}
$a = Size::Small;
$b = Size::Large;
var_dump($a->price()); //int(10)
var_dump($b->price()); //int(1000)
上の例ではBacked Enumにメソッドを定義しましたが、Pure Enumでも同様のことが可能です。
列挙型はinterfaceを実装することも可能です。
<?php
interface ISize
{
public function price(): int;
}
enum Size: string implements ISize
{
case Small = "s";
case Medium = "m";
case Large = "l";
//$thisで自身のcaseを返す
public function price(): int
{
return match($this) {
Size::Small => 10,
Size::Medium => 100,
Size::Large => 1000
};
}
}
$a = Size::Small;
function hoge(ISize $size): int
{
return $size->price();
}
var_dump(hoge($a)); //int(10)
Sizeはインターフェースを実装しており、関数hogeではポリモーフィズムがきちんと働いています。
staticメソッドの定義もできます。
列挙型ではコンストラクタが使えないため、代用としてstaticメソッドが使えます、
<?php
enum Size: string
{
case Small = "s";
case Medium = "m";
case Large = "l";
public static function fromPrice(int $price): static
{
return match(true) {
$price < 10 => static::Small,
$price < 100 => static::Medium,
default => statoc.Large
};
}
}
var_dump(Size::fromPrice(50)); //enum(Size::Medium)
constで定数の定義も可能です。自身のcaseも代入可能です。
<?php
enum Size: string
{
case Small = "s";
case Medium = "m";
case Large = "l";
public const Hoge = "hoge";
public const Fuga = self::Small;
}
var_dump(Size::Hoge); //string(4) "hoge"
var_dump(Size::Fuga); //enum(Size::Small)
トレイトの利用も可能です。
ただし列挙型でuseされるトレイトは、プロパティを含めるとエラーが出るので注意が必要です。
<?php
interface ISize
{
public function price(): int;
}
trait SizeTrait
{
public function price(): int
{
return match($this) {
Size::Small => 10,
Size::Medium => 100,
default => 1000
};
}
}
enum Size: string
{
use SizeTrait;
case Small = "s";
case Medium = "m";
case Large = "l";
}
$a = Size::Small;
var_dump($a->price()); //int(10)
今まで何度か出てきたものを含めて、クラスとの違いについて改めてまとめます。
cases()メソッドでcaseのリストを取得できます。
enum Size: string
{
case Small = "s";
case Medium = "m";
case Large = "l";
}
var_dump(Size::cases());
/**
* array(3) {
* [0]=>
*enum(Size::Small)
* [1]=>
* enum(Size::Medium)
* [2]=>
* enum(Size::Large)
*}
*/
最後に、公式ドキュメントで紹介されている応用例を紹介します。
<?php
enum UserStatus: string
{
case Pending = 'P';
case Active = 'A';
case Suspended = 'S';
case CanceledByUser = 'C';
public function label(): string
{
return match($this) {
static::Pending => 'Pending',
static::Active => 'Active',
static::Suspended => 'Suspended',
static::CanceledByUser => 'Canceled by user',
};
}
}
foreach (UserStatus::cases() as $case) {
printf('<option value="%s">%s</option>\n', $case->value, $case->label());
}
/**
*<option value="P">Pending</option>
*<option value="A">Active</option>
*<option value="S">Suspended</option>
*<option value="C">Canceled by user</option>
*
*/
UserStatusは4つの状態のうち1つに限定されます。
またすべてのインスタンスはlabelメソッドを持つので、例のforeach文のようにループを回せます。
PHPにおける列挙型とその使い方について解説しました。基本的な解説に留めましたが、データベース操作などと組み合わせると役に立ちそうです。
関連記事
最新の記事
カテゴリー一覧
アーカイブ