PHP 枚举(PHP 8.1+)定义了一个类型,它只有固定的几个可能取值,也就是它的 case:enum Status { case Active; case Banned; }。使用时写作 Status::Active,类型声明为 Status 的参数不接受其他任何值。backed 枚举(enum Status: string)还会给每个 case 一个值,你可以把它存起来,再用 Status::from() 转换回来。
<?php
enum Status: string
{
case Active = 'active';
case Pending = 'pending';
case Banned = 'banned';
}
function canLogIn(Status $status): bool
{
return $status === Status::Active;
}
$s = Status::Pending;
echo $s->name, ' = ', $s->value, "\n";
var_dump(canLogIn($s), canLogIn(Status::Active));
// From a value stored in a database or a form
$fromDb = Status::from('banned');
echo $fromDb->name, "\n";
在有枚举之前,同样的功能靠一组字符串常量实现,没有什么能阻止 'actve' 这样的拼写错误写进数据库。把 Status 用作参数类型后,只能传入这三个 case。
纯枚举和 backed 枚举
纯枚举的 case 只有名称。backed 枚举在名称后面声明一个类型,int 或 string,然后每个 case 都必须有一个该类型的唯一值。每个 case 都有 ->name;只有 backed case 才有 ->value。
<?php
enum Suit
{
case Hearts;
case Spades;
}
enum Priority: int
{
case Low = 1;
case Normal = 5;
case Urgent = 10;
}
$card = Suit::Hearts;
echo $card->name, "\n";
var_dump($card === Suit::Hearts, $card instanceof Suit);
$p = Priority::Urgent;
echo $p->name, ' = ', $p->value, "\n";
var_dump(Priority::Low->value < Priority::Urgent->value);
var_dump($p);
echo Role::Admin; 会抛出 Error("Object of class Role could not be converted to string"),而且枚举不能实现 __toString()。反过来读取时,JSON 给出的是普通字符串,所以要像上面那样用 from() 或 tryFrom() 转换。编码的更多内容见 json_encode。
使用枚举的常见错误
有三种错误反复出现:case 永远不等于它的值,case 不能作为数组键,new 不能用于枚举:
<?php
enum Status: string
{
case Active = 'active';
case Banned = 'banned';
}
// 1. A case is an object, not its value
var_dump(Status::Active == 'active'); // false
var_dump(Status::Active->value === 'active'); // true
// 2. Use ->value as an array key
$counts = [];
foreach ([Status::Active, Status::Banned, Status::Active] as $s) {
$counts[$s->value] = ($counts[$s->value] ?? 0) + 1;
}
print_r($counts);
try {
$bad = [Status::Active => 1];
} catch (TypeError $e) {
echo 'Array key: ', $e->getMessage(), "\n";
}
// 3. Cases are created by PHP, never with new
try {
$s = new Status();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
如果需要一个以 case 本身为键的映射,用 SplObjectStorage 或 WeakMap,它们接受对象作为键。
常见问题
PHP 中怎么获取枚举的值?
在 backed 枚举上读取 ->value:Status::Active->value 是 'active'。每个 case 还有 ->name,即字符串形式的 case 名('Active')。纯枚举(没有 : string 或 : int)只有 ->name。