enum(列挙型)
| 対応: | PHP 8.1(2021) |
|---|
固定された値の集合を型安全に扱うには『PHP』8.1 で追加された『enum』(列挙型)が便利です。従来は定数や文字列でやり取りしていた「状態」「種別」などの値を、専用の型として定義できるためコードの意図が明確になり、誤った値の代入をコンパイル時に防げます。
構文
// Pure enum(値を持たない列挙型)
enum 列挙型名 {
case ケース名;
}
// Backed enum(intまたはstringの値を持つ列挙型)
enum 列挙型名: int {
case ケース名 = 値;
}
enum 列挙型名: string {
case ケース名 = '値';
}
// enumのケースにアクセスします
列挙型名::ケース名
// Backed enumで値からケースを取得します
列挙型名::from(値) // 一致しない場合は例外
列挙型名::tryFrom(値) // 一致しない場合はnull
Pure enumとBacked enumの違い
| 種類 | 構文 | 概要 |
|---|---|---|
| Pure enum | enum 名前 { case ... } | 数値や文字列などのスカラー値を持ちません。ケース自体がオブジェクトとして扱われます。 |
| Backed enum(int) | enum 名前: int { case ... = 値 } | 各ケースに整数値を割り当てます。データベースへの保存などに便利です。 |
| Backed enum(string) | enum 名前: string { case ... = '値' } | 各ケースに文字列値を割り当てます。JSONや外部APIとのやり取りに便利です。 |
enumのプロパティとメソッド
| 名前 | 使用可能な種類 | 概要 |
|---|---|---|
| $case->name | Pure enum / Backed enum | ケース名を文字列で返します。例:『Suit::Hearts->name』は『"Hearts"』を返します。 |
| $case->value | Backed enum のみ | ケースに割り当てられたスカラー値を返します。Pure enum では使用できません。 |
| ::from($value) | Backed enum のみ | スカラー値からケースを取得します。一致するケースがない場合は『ValueError』をスローします。 |
| ::tryFrom($value) | Backed enum のみ | スカラー値からケースを取得します。一致するケースがない場合は『null』を返します。 |
| ::cases() | Pure enum / Backed enum | 全ケースの配列を返します。 |
サンプルコード
Pure enum の基本
enum_pure.php
<?php
// KOFのキャラクター属性をPure enumで定義します
// Pure enumはスカラー値を持たず、ケース自体がオブジェクトです
enum Element {
case Fire; // 炎
case Ice; // 氷
case Thunder; // 雷
case Wind; // 風
}
// ケースにはクラス定数のようにアクセスします
$kyo = Element::Fire;
$iori = Element::Fire;
$kula = Element::Ice;
$benimaru = Element::Thunder;
// 同じケース同士は厳密比較(===)で一致します
var_dump($kyo === $iori); // 同じケースのため true
var_dump($kyo === $kula); // 異なるケースのため false
// ->name でケース名を文字列として取得できます
echo $kyo->name . "\n"; // Fire
echo $kula->name . "\n"; // Ice
// match式と組み合わせてケースごとの処理を記述できます
function getElementDescription(Element $element): string
{
return match($element) {
Element::Fire => "炎の使い手です。",
Element::Ice => "氷の使い手です。",
Element::Thunder => "雷の使い手です。",
Element::Wind => "風の使い手です。",
};
}
echo "草薙京: " . getElementDescription($kyo) . "\n";
echo "クーラ: " . getElementDescription($kula) . "\n";
echo "八神庵: " . getElementDescription($iori) . "\n";
echo "二階堂紅丸: " . getElementDescription($benimaru) . "\n";
実行すると次のように出力されます。
php enum_pure.php bool(true) bool(false) Fire Ice 草薙京: 炎の使い手です。 クーラ: 氷の使い手です。 八神庵: 炎の使い手です。 二階堂紅丸: 雷の使い手です。
Backed enum(int)
enum_backed_int.php
<?php
// KOFのキャラクターランクをint型のBacked enumで定義します
// 各ケースに整数値を割り当てることで、データベース保存などに利用できます
enum Rank: int {
case D = 1;
case C = 2;
case B = 3;
case A = 4;
case S = 5;
}
// ->value でケースに割り当てた整数値を取得します
echo Rank::A->value . "\n"; // 4
echo Rank::S->name . "\n"; // S
// ::from() で整数値からケースを取得します
// 一致するケースがない場合はValueErrorが発生します
$rank = Rank::from(3);
echo $rank->name . "\n"; // B
// ::tryFrom() はケースが見つからない場合にnullを返します(例外なし)
$unknownRank = Rank::tryFrom(99);
var_dump($unknownRank); // NULL
// キャラクターとランクを組み合わせた例です
$fighters = [
["name" => "草薙京", "rank" => Rank::S],
["name" => "八神庵", "rank" => Rank::S],
["name" => "テリー・ボガード", "rank" => Rank::A],
["name" => "ラルフ・ジョーンズ", "rank" => Rank::B],
];
foreach ($fighters as $fighter) {
// ->value で整数値、->name でケース名を参照できます
echo $fighter['name'] . ": ランク " . $fighter['rank']->name
. "(スコア: " . $fighter['rank']->value . ")\n";
}
実行すると次のように出力されます。
php enum_backed_int.php 4 S B NULL 草薙京: ランク S(スコア: 5) 八神庵: ランク S(スコア: 5) テリー・ボガード: ランク A(スコア: 4) ラルフ・ジョーンズ: ランク B(スコア: 3)
Backed enum(string)
enum_backed_string.php
<?php
// KOFのチームをstring型のBacked enumで定義します
// 外部APIやJSONとのやり取りで文字列値が必要な場合に便利です
enum Team: string {
case HeroesTeam = 'heroes';
case RivalsTeam = 'rivals';
case FatalFury = 'fatal_fury';
case IkariTeam = 'ikari';
}
// ->value で文字列値を取得します
echo Team::HeroesTeam->value . "\n"; // heroes
echo Team::FatalFury->value . "\n"; // fatal_fury
// ::from() で文字列値からケースを復元します(JSONから読み込んだ値の復元など)
$teamFromJson = Team::from('ikari');
echo $teamFromJson->name . "\n"; // IkariTeam
// ::cases() で全ケースの配列を取得できます
echo "--- 全チーム一覧 ---\n";
foreach (Team::cases() as $team) {
echo $team->name . ": " . $team->value . "\n";
}
// キャラクターとチームの対応例です
$members = [
["name" => "草薙京", "team" => Team::HeroesTeam],
["name" => "八神庵", "team" => Team::RivalsTeam],
["name" => "テリー・ボガード", "team" => Team::FatalFury],
["name" => "ラルフ・ジョーンズ", "team" => Team::IkariTeam],
];
echo "\n--- チーム所属 ---\n";
foreach ($members as $member) {
// JSON送信やDB保存では ->value を使用します
echo $member['name'] . ": " . $member['team']->value . "\n";
}
実行すると次のように出力されます。
php enum_backed_string.php heroes fatal_fury IkariTeam --- 全チーム一覧 --- HeroesTeam: heroes RivalsTeam: rivals FatalFury: fatal_fury IkariTeam: ikari --- チーム所属 --- 草薙京: heroes 八神庵: rivals テリー・ボガード: fatal_fury ラルフ・ジョーンズ: ikari
enumのメソッド定義とインターフェース実装
enum_method_interface.php
<?php
// enumにもメソッドを定義できます
// また、インターフェースを実装することもできます
// ラベル取得のインターフェースを定義します
interface HasLabel {
public function label(): string;
}
// KOFの戦闘スタイルをstring型Backed enumで定義し、インターフェースを実装します
enum FightingStyle: string implements HasLabel {
case Karate = 'karate';
case Muay_Thai = 'muay_thai';
case Wrestling = 'wrestling';
case Martial_Art = 'martial_art';
// インターフェースのメソッドを実装します
public function label(): string
{
return match($this) {
FightingStyle::Karate => "空手",
FightingStyle::Muay_Thai => "ムエタイ",
FightingStyle::Wrestling => "レスリング",
FightingStyle::Martial_Art => "拳法",
};
}
// enumに独自メソッドを追加できます
public function description(): string
{
return match($this) {
FightingStyle::Karate => "草薙流空手。炎を操る攻撃が特徴です。",
FightingStyle::Muay_Thai => "ムエタイの使い手。蹴り技の威力が高いです。",
FightingStyle::Wrestling => "レスリングベースの投げ技が強力です。",
FightingStyle::Martial_Art => "中国拳法。機動力と多彩な技が特徴です。",
};
}
}
// インターフェース経由でメソッドを呼び出します
function printStyle(HasLabel $style): void
{
echo $style->label() . "\n";
}
$style = FightingStyle::Karate;
printStyle($style); // 空手
// 各メソッドを呼び出します
$fighters = [
["name" => "草薙京", "style" => FightingStyle::Karate],
["name" => "キム・カッファン", "style" => FightingStyle::Muay_Thai],
["name" => "クラーク・スティル", "style" => FightingStyle::Wrestling],
["name" => "レオナ・ハイデルン", "style" => FightingStyle::Martial_Art],
];
foreach ($fighters as $fighter) {
echo $fighter['name'] . "(" . $fighter['style']->label() . "): "
. $fighter['style']->description() . "\n";
}
// value から復元して使うことも可能です
$fromValue = FightingStyle::from('muay_thai');
echo "\n復元したスタイル: " . $fromValue->label() . "\n";
実行すると次のように出力されます。
php enum_method_interface.php 空手 草薙京(空手): 草薙流空手。炎を操る攻撃が特徴です。 キム・カッファン(ムエタイ): ムエタイの使い手。蹴り技の威力が高いです。 クラーク・スティル(レスリング): レスリングベースの投げ技が強力です。 レオナ・ハイデルン(拳法): 中国拳法。機動力と多彩な技が特徴です。 復元したスタイル: ムエタイ
よくあるミス
Pure enum で ->value にアクセスしようとする
Pure enum はスカラー値を持たないため、->value にアクセスしようとすると致命的なエラーになります。->value は Backed enum 専用です。
ng_enum_value.php
<?php
enum Element {
case Fire;
case Ice;
}
$kyo = Element::Fire;
echo $kyo->value . "\n"; // Pure enum には value がないためエラー
実行すると次のように出力されます。
php ng_enum_value.php Fatal error: Uncaught Error: Cannot access the value of a non-backed enum in ng_enum_value.php:8
スカラー値が必要な場合は Backed enum を使います。値が不要な場合は ->name でケース名の文字列を取得できます。
ok_enum_value.php
<?php
// Pure enum: ->name でケース名を取得する
enum Element {
case Fire;
case Ice;
}
$kyo = Element::Fire;
echo $kyo->name . "\n"; // "Fire"
// Backed enum: ->value でスカラー値を取得する
enum ElementBacked: string {
case Fire = 'fire';
case Ice = 'ice';
}
$iori = ElementBacked::Fire;
echo $iori->value . "\n"; // "fire"
実行すると次のように出力されます。
php ok_enum_value.php Fire fire
::from() で存在しない値を渡す
::from() は一致するケースがない場合に ValueError をスローします。外部から受け取った値を変換する際に未知の値が混入すると、例外処理がなければスクリプトが停止します。
ng_enum_from.php
<?php
enum Rank: int {
case D = 1;
case C = 2;
case B = 3;
case A = 4;
case S = 5;
}
// 存在しない値 99 を from() に渡すと ValueError が発生する
$rank = Rank::from(99);
echo $rank->name . "\n";
実行すると次のように出力されます。
php ng_enum_from.php Fatal error: Uncaught ValueError: 99 is not a valid backing value for enum "Rank" in ng_enum_from.php:13
外部入力などで値が正しいか不明な場合は、null を返す ::tryFrom() を使って null チェックを挟みます。
ok_enum_from.php
<?php
enum Rank: int {
case D = 1;
case C = 2;
case B = 3;
case A = 4;
case S = 5;
}
$fighters = [
["name" => "草薙京", "rankValue" => 5],
["name" => "テリー・ボガード", "rankValue" => 4],
["name" => "不明なファイター", "rankValue" => 99],
];
foreach ($fighters as $fighter) {
$rank = Rank::tryFrom($fighter['rankValue']);
if ($rank !== null) {
echo $fighter['name'] . ": ランク " . $rank->name . "\n";
} else {
echo $fighter['name'] . ": ランク不明\n";
}
}
実行すると次のように出力されます。
php ok_enum_from.php 草薙京: ランク S テリー・ボガード: ランク A 不明なファイター: ランク不明
概要
『enum』はPHP 8.1 で追加された構文で、固定された値の集合を型として定義します。従来は定数や文字列でやり取りしていた「状態」「種別」などの値を専用の型として扱えるため、型ヒントと組み合わせることで誤った値の代入をコンパイル時に防止でき、IDEの補完も効くようになります。
スカラー値を持たない『Pure enum』と、int または string の値を持つ『Backed enum』の2種類があります。Backed enum では『::from()』で値からケースを復元できるため、データベースや外部 API とのやり取りに適しています。一致しない値が渡される可能性がある場合は、例外をスローする『::from()』ではなく『null』を返す『::tryFrom()』を使用してください。
『enum』にはメソッドを定義でき、インターフェースを実装することも可能です。これにより『match』式でケースごとの処理を書く代わりに、ケース自身にロジックを持たせる設計ができます。『match』式については『match式』、インターフェースについては『extends / implements』を参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。