枚举基础
概述
PHP 8.1 引入的 enum 关键字用于定义枚举类型。枚举通过 case 关键字定义各个可能的值。纯枚举(Pure Enum)不携带标量值,仅作为类型安全的标识符使用。每个枚举 case 都是该枚举类的单例实例,自动实现 UnitEnum 接口。
基础概念
enum 关键字
enum 用于声明枚举类型,语法类似于类声明,但有以下限制:
- 枚举隐式为
final,不能被继承 - 枚举不能使用
new实例化 - 枚举只能包含
case、方法、常量和 trait
UnitEnum 接口
所有纯枚举自动实现 UnitEnum 接口,该接口提供:
cases(): array— 返回所有 case 实例的数组name: string— 只读属性,返回 case 的名称
语法与代码
定义纯枚举
php
<?php
declare(strict_types=1);
enum Suit
{
case Hearts;
case Diamonds;
case Clubs;
case Spades;
}
// 使用枚举
$card = Suit::Hearts;
echo $card->name; // "Hearts"
// 遍历所有 case
foreach (Suit::cases() as $suit) {
echo $suit->name . PHP_EOL;
}枚举实例是单例
php
<?php
declare(strict_types=1);
enum Season
{
case Spring;
case Summer;
case Autumn;
case Winter;
}
$a = Season::Spring;
$b = Season::Spring;
$c = Season::Summer;
var_dump($a === $b); // bool(true) - 同一个 case
var_dump($a === $c); // bool(false) - 不同的 case
var_dump($a instanceof Season); // bool(true)在方法参数中使用枚举
php
<?php
declare(strict_types=1);
enum LogLevel
{
case Debug;
case Info;
case Warning;
case Error;
case Critical;
}
class Logger
{
public function log(LogLevel $level, string $message): void
{
$prefix = match ($level) {
LogLevel::Debug => '[DEBUG]',
LogLevel::Info => '[INFO]',
LogLevel::Warning => '[WARNING]',
LogLevel::Error => '[ERROR]',
LogLevel::Critical => '[CRITICAL]',
};
echo "{$prefix} {$message}" . PHP_EOL;
}
}
$logger = new Logger();
$logger->log(LogLevel::Info, 'System started');
$logger->log(LogLevel::Error, 'Database connection failed');在枚举中定义方法
php
<?php
declare(strict_types=1);
enum HttpStatus
{
case Ok;
case NotFound;
caseServerError;
case Unauthorized;
public function code(): int
{
return match ($this) {
self::Ok => 200,
self::NotFound => 404,
self::ServerError => 500,
self::Unauthorized => 401,
};
}
public function label(): string
{
return match ($this) {
self::Ok => '成功',
self::NotFound => '未找到',
self::ServerError => '服务器错误',
self::Unauthorized => '未授权',
};
}
public function isSuccess(): bool
{
return $this === self::Ok;
}
}
$status = HttpStatus::NotFound;
echo $status->code(); // 404
echo $status->label(); // 未找到
echo $status->isSuccess(); // false静态方法
php
<?php
declare(strict_types=1);
enum Weekday
{
case Monday;
case Tuesday;
case Wednesday;
case Thursday;
case Friday;
case Saturday;
case Sunday;
public static function today(): self
{
$day = (int) date('N'); // 1 (Monday) to 7 (Sunday)
return self::cases()[$day - 1];
}
public static function isWeekend(self $day): bool
{
return in_array($day, [self::Saturday, self::Sunday], true);
}
public function isWeekday(): bool
{
return !self::isWeekend($this);
}
}
$today = Weekday::today();
echo "Today is {$today->name}";
echo $today->isWeekday() ? ' (工作日)' : ' (周末)';枚举常量
php
<?php
declare(strict_types=1);
enum MediaType
{
case Image;
case Video;
case Audio;
case Document;
final public const DEFAULT = self::Document;
public function maxSizeKb(): int
{
return match ($this) {
self::Image => 5120,
self::Video => 102400,
self::Audio => 25600,
self::Document => 10240,
};
}
}
$default = MediaType::DEFAULT;
echo $default->name; // "Document"详细说明
枚举中的属性限制
php
<?php
declare(strict_types=1);
enum BrokenEnum
{
case A;
case B;
// 以下都是非法的:
// public string $value; // Fatal error: Enums may not include properties
// public static int $count = 0; // Fatal error
// private int $id; // Fatal error
// 但常量是允许的
public const VERSION = '1.0';
// 方法是允许的
public function description(): string
{
return $this->name;
}
}重要限制
枚举不能包含属性(properties)。如果需要在 case 之间共享数据,只能通过方法返回值或构造函数参数(Backed Enum 的 from 方法)实现。
枚举构造函数
php
<?php
declare(strict_types=1);
enum Color
{
case Red;
case Green;
case Blue;
// 枚举可以有构造函数,但不能有参数
// 用于初始化每个 case 的内部状态(如有 trait 需要的话)
// 构造函数必须是 public 或 private,不能是 protected
}实现接口
php
<?php
declare(strict_types=1);
interface HasLabel
{
public function label(): string;
public function description(): string;
}
enum PaymentStatus implements HasLabel
{
case Pending;
case Completed;
case Failed;
case Refunded;
public function label(): string
{
return match ($this) {
self::Pending => '待支付',
self::Completed => '已完成',
self::Failed => '失败',
self::Refunded => '已退款',
};
}
public function description(): string
{
return "支付状态: {$this->label()}";
}
}实战示例
场景一:HTTP 请求方法枚举
php
<?php
declare(strict_types=1);
enum RequestMethod
{
case Get;
case Post;
case Put;
case Patch;
case Delete;
case Options;
case Head;
public function isSafe(): bool
{
return in_array($this, [self::Get, self::Head, self::Options], true);
}
public function hasBody(): bool
{
return in_array($this, [self::Post, self::Put, self::Patch], true);
}
public function isIdempotent(): bool
{
return in_array($this, [self::Get, self::Put, self::Delete, self::Head, self::Options], true);
}
}场景二:访问控制级别
php
<?php
declare(strict_types=1);
enum Permission
{
case Read;
case Write;
case Delete;
case Admin;
public function rank(): int
{
return match ($this) {
self::Read => 1,
self::Write => 2,
self::Delete => 4,
self::Admin => 8,
};
}
public function includes(Permission $other): bool
{
return $this->rank() >= $other->rank();
}
}
$admin = Permission::Admin;
$user = Permission::Read;
var_dump($admin->includes($user)); // true - Admin 包含 Read
var_dump($user->includes($admin)); // false - Read 不包含 Admin注意事项
注意事项
- 枚举不能包含属性(properties),只能包含 case、方法、常量和 trait
- 枚举构造函数不能有参数(每个 case 无需传参)
- 纯枚举没有
value属性 - 枚举的
name属性是只读的,不能修改
小贴士
- 使用
match表达式处理枚举,确保穷尽所有 case - 纯枚举适用于不需要序列化值的场景(如状态标识)
- 如果需要存储到数据库,使用 Backed Enum(带值枚举)
最佳实践
1. 为枚举添加描述性方法
php
<?php
declare(strict_types=1);
enum Color
{
case Red;
case Green;
case Blue;
public function hex(): string
{
return match ($this) {
self::Red => '#FF0000',
self::Green => '#00FF00',
self::Blue => '#0000FF',
};
}
}2. 保持 case 命名使用大驼峰
php
<?php
declare(strict_types=1);
// 推荐 - PascalCase
enum OrderStatus
{
case PendingReview;
case Approved;
case Rejected;
}