PHP 8.1 枚举是类型系统一等公民,非高级常量;涉及数据库、API、JSON时必须用backed enum(如enum Status: string),pure enum仅适用于内存内状态判断。

PHP 8.1 枚举不是“更高级的常量”,而是类型系统的一等公民——用错场景或调用方式,轻则报错,重则绕过类型安全、引入静默 bug。
什么时候该用 backed enum 而不是 pure enum
纯枚举(enum Status { case Pending; })只适合内存内状态判断,比如领域事件分发或服务间类型约束;一旦涉及数据库字段、API 输入/输出、JSON 序列化,就必须用背书枚举(enum Status: string)。
-
->value在 pure enum 中不存在,强行访问会报Fatal error: Uncaught Error: Cannot access value property on unit enum - 数据库字段是
VARCHAR或TINYINT?API 返回{"status": "active"}?前端传status=active?这些都要求双向转换能力,只有 backed enum 支持->value、::from()和::tryFrom() - Doctrine ORM 2.11+ 持久化时,
#[ORM\Column(type: 'string', enumType: Status::class)]仅对 backed enum 生效;pure enum 会触发MappingException
遍历所有枚举项必须用 ::cases(),别手写数组
Status::cases() 是唯一安全、自动同步、类型可推导的遍历方式。它返回 Status[] 数组,顺序与定义一致,且不依赖反射或硬编码。
- 错误写法:
[$status::Pending, $status::Active]—— 新增case Archived后必须手动补全,IDE 无法校验 - 正确写法:
foreach (Status::cases() as $case) { echo $case->name . ': ' . ($case->value ?? ''); } - 注意:
$case->value在 pure enum 中为''(空字符串),不是null;若需区分,应改用 backed enum 或显式判断isset($case->value)
从用户输入解析枚举,永远优先用 ::tryFrom()
::from() 在值不匹配时抛 ValueError,而真实请求里拼错、旧数据残留、第三方字段变更都是常态——不能靠 try-catch 拦截,得主动防御。
立即学习“PHP免费学习笔记(深入)”;
- HTTP 查询参数:
$status = Status::tryFrom($_GET['status'] ?? ''); if ($status === null) { throw new InvalidArgumentException('Invalid status'); } - JSON 请求体:
$data = json_decode(file_get_contents('php://input'), true); $status = Status::tryFrom($data['status'] ?? ''); - 别用
(string) $enum或$enum->value去“反向猜”枚举项——这等于放弃类型检查,和直接用字符串没区别
给枚举加行为逻辑,只能用静态方法或实例方法,不能有属性
枚举不允许定义普通属性(如 public string $label)或构造函数,但可以声明实例方法(public function isFinal(): bool)和静态方法(public static function fromLabel(string $label): self)。
- 常见错误:
public function getLabel(): string { return $this->label; }→ 报ParseError: Enum cases cannot have methods - 正确姿势:用
match($this)分支返回差异化结果,例如:public function label(): string { return match($this) { self::Draft => '草稿', self::Published => '已发布' }; } - 若逻辑复杂(如依赖外部服务),建议抽到独立 service 类,保持枚举轻量——它负责“是什么”,不负责“怎么做”
最易被忽略的一点:backed enum 的标量类型(: string 或 : int)必须显式声明,否则 ->value、::from() 全部不可用,且 IDE 不会提示缺失——这种错误直到运行时才暴露,且无明确报错上下文。



















