Menu

PHPの列挙型enum:Backed Enum、from()、tryFrom()、cases()

PHPのenum(8.1以降)は値の決まった一覧を持つ型です:enum Status: string { case Active = 'active'; }。Pure EnumとBacked Enum、->valueと->name、from()とtryFrom()での変換、cases()でのケースの一覧、メソッド、定数、インターフェース、matchでのenumを学びます。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

PHPのenum(PHP 8.1以降)は、取りうる値(ケース)の決まった集合を持つ型を定義します:enum Status { case Active; case Banned; }。ケースはStatus::Activeのように使い、Statusで型宣言した引数はそれ以外を受け付けません。Backed Enum(enum Status: string)は、各ケースに保存してStatus::from()で戻せる値も与えます。

enumができる前は、同じことを文字列の定数の一覧で書いていて、'actve'のようなタイプミスがデータベースまで届くのを止めるものはありませんでした。引数の型をStatusにすれば、渡せるのは3つのケースだけです。

Pure EnumとBacked Enum

Pure Enumのケースは名前だけです。Backed Enumは名前のあとにintかstringの型を宣言し、すべてのケースがその型の一意な値を持つ必要があります。どのケースにも->nameがあり、->valueがあるのはBackedのケースだけです。

データベースのカラム、URL、フォームの項目、JSONのように、値がプログラムの外に出るならBacked Enumを選びましょう。パーサーの状態のように、コードの中にしか存在しない値ならPure Enumを選びます。

from()とtryFrom()で値を変換する

Backed Enumには、保存した値をケースに戻す2つの静的メソッドがあります。from()は未知の値にValueErrorを投げ、tryFrom()はnullを返すので、既定値のための??と相性がよいです。

ユーザーから送られたものにはtryFrom()を、自分のコードが書いた値にはfrom()を使います。後者では未知の値はバグを意味し、知らせてほしいからです。引数は普通の引数の型ルールに従います。intのenumでは、デフォルトのモードなら'5'が先に5に変換されるのでPriority::from('5')はPriority::Normalを返し、Priority::from('5x')はTypeErrorで、declare(strict_types=1)の下ではどんな文字列もTypeErrorです。tryFrom()も同じで、Priority::tryFrom('abc')はnullを返さずに例外を投げるので、intのenumに対するフォームの入力は先にfilter_var($raw, FILTER_VALIDATE_INT)で確認しましょう。

cases()ですべてのケースを一覧にする

Enum::cases()は、宣言順にすべてのケースの配列を返します。ドロップダウンを作ったり、リストを検証したり、array_columnですべての値を取得したりする方法です。

実行してプランを選び、Chooseを押すと、$_POST['plan']に値が入った状態で同じスクリプトがもう一度実行され、tryFrom()が送信された文字列をPlanのケースに戻します。plan=goldのような偽造された値はnullになり、どのオプションも選択されません。

enumのメソッド、定数、インターフェース

enumはメソッド、静的メソッド、定数を持て、インターフェースを実装できます。メソッドの中では$thisが現在のケースなので、各ケースにデータを結びつけるにはmatch ($this)が自然な方法です。enumが持てないのはプロパティで、ケースごとのデータはメソッドから得ます。

matchでのenum

matchは===で比較し、enumのケースは自分自身とだけ同一なので、matchとenumは相性がよいです。ケースを書き忘れ、そのケースがmatchに届くと、PHPは黙って何も返さない代わりにUnhandledMatchErrorを投げます。

matchとswitchの違いについてはmatchを参照してください。

JSONと文字列でのenum

json_encode()はBackedのケースをその値として書き出します。Pure Enumはエンコードできず、enumは文字列ではなくオブジェクトなので、どのケースもechoでは失敗します。代わりに->valueか->nameを出力しましょう。

echo Role::Admin;はError(「Object of class Role could not be converted to string」)を投げ、enumは__toString()を実装できません。戻すときはJSONから普通の文字列が来るので、示したとおりfrom()かtryFrom()で変換します。エンコードについて詳しくはjson_encodeを参照してください。

enumのよくあるミス

何度も出てくるエラーが3つあります。ケースは決してその値と等しくならない、ケースは配列のキーにできない、enumにnewは使えない、の3つです。

ケースそのものをキーにしたマップが必要なら、オブジェクトをキーとして受け付けるSplObjectStorageかWeakMapを使います。

よくある質問

PHPでenumの値を取得するには?

Backed Enumなら->valueを読みます:Status::Active->valueは'active'です。どのケースにも、ケース名を文字列で表す->name('Active')があります。Pure Enum(: stringや: intなし)には->nameしかありません。

PHPのenumのfrom()とtryFrom()の違いは何ですか?

どちらもBacked Enumの値をケースに変換します。Status::from('active')はケースを返し、その値を持つケースがなければValueErrorを投げます。Status::tryFrom('nope')は代わりにnullを返します。ユーザーの入力にはtryFrom()を、不正な値がバグであるときはfrom()を使います。

PHPでenumのすべての値を取得するには?

Status::cases()は宣言順にすべてのケースを返します。Backed Enumの値にはarray_column(Status::cases(), 'value')を、名前にはarray_column(Status::cases(), 'name')を使います。

Pure EnumとBacked Enumの違いは何ですか?

Pure Enum(enum Suit { case Hearts; })のケースは名前だけです。Backed Enum(enum Suit: string { case Hearts = 'H'; })は各ケースに一意なintかstringの値を与え、データベースへの保存、フォームやJSONへの出力、from()やtryFrom()での変換に必要です。

PHPのenumはメソッドを持てますか?

持てます。enumはメソッド、静的メソッド、定数を持て、インターフェースを実装できます。メソッドの中では$thisが現在のケースなので、return match ($this) { self::Active => 'green', self::Banned => 'red' };がよく使われるパターンです。enumはプロパティを持てません。

Coddyのプログラミング言語のイラスト

Coddyでコードを学ぼう

始める