枚举序列化
概述
枚举的序列化涉及将枚举实例转换为可存储或传输的格式(如 JSON、数据库值),以及从这些格式还原为枚举实例。PHP 8.1+ 的枚举提供了内置的序列化支持,包括 serialize()/unserialize() 和 json_encode()/json_decode() 的自定义行为。
版本要求
枚举序列化是 PHP 8.1+ 专属特性。
基础概念
枚举序列化的两种场景
| 场景 | 方法 | 用途 |
|---|---|---|
| PHP 原生序列化 | serialize() / unserialize() | 缓存、Session 存储 |
| JSON 序列化 | json_encode() / json_decode() | API 响应、前端交互 |
| 数据库存储 | value 属性 | 持久化到数据库 |
语法与代码
serialize 与 unserialize
php
<?php
declare(strict_types=1);
enum Status
{
case Pending;
case Approved;
case Rejected;
}
$status = Status::Approved;
// 序列化
$serialized = serialize($status);
// E:5:"Status":1:{s:4:"name";s:8:"Approved";}
// 反序列化
$restored = unserialize($serialized);
var_dump($restored === Status::Approved); // bool(true)
var_dump($restored->name); // string(8) "Approved"JSON 编码枚举
php
<?php
declare(strict_types=1);
enum Role
{
case Admin;
case Editor;
case Viewer;
}
$role = Role::Admin;
// 默认 JSON 编码 - 输出枚举的 name 属性
echo json_encode($role);
// "Admin"
// 编码枚举示例数组
$roles = [Role::Admin, Role::Viewer];
echo json_encode($roles);
// ["Admin","Viewer"]Backed Enum 的 JSON 编码
php
<?php
declare(strict_types=1);
enum Priority: int
{
case Low = 1;
case Medium = 2;
case High = 3;
case Critical = 4;
}
$priority = Priority::High;
// Backed Enum 的 JSON 编码输出 value 属性
echo json_encode($priority);
// 3
// 编码数组
$priorities = [Priority::Low, Priority::Critical];
echo json_encode($priorities);
// [1,4]JSON 解码与枚举还原
php
<?php
declare(strict_types=1);
enum Color: string
{
case Red = 'red';
case Green = 'green';
case Blue = 'blue';
}
// json_decode 返回的是字符串/整数,需要手动转换为枚举
$json = '"green"';
$value = json_decode($json);
$color = Color::from($value); // Color::Green
// 使用 tryFrom 安全处理
$json = '"purple"';
$value = json_decode($json);
$color = Color::tryFrom($value); // nullJsonSerializable 接口自定义 JSON 输出
php
<?php
declare(strict_types=1);
enum OrderStatus: string implements \JsonSerializable
{
case Created = 'created';
case Paid = 'paid';
case Shipped = 'shipped';
case Delivered = 'delivered';
case Cancelled = 'cancelled';
public function jsonSerialize(): mixed
{
return [
'value' => $this->value,
'label' => $this->label(),
];
}
public function label(): string
{
return match ($this) {
self::Created => '已创建',
self::Paid => '已支付',
self::Shipped => '已发货',
self::Delivered => '已送达',
self::Cancelled => '已取消',
};
}
}
$status = OrderStatus::Paid;
echo json_encode($status);
// {"value":"paid","label":"已支付"}存储枚举值到数据库
php
<?php
declare(strict_types=1);
enum UserStatus: string
{
case Active = 'active';
case Inactive = 'inactive';
case Banned = 'banned';
}
class UserRepository
{
public function save(int $id, UserStatus $status): void
{
// 存储枚举的标量值
$sql = "UPDATE users SET status = ? WHERE id = ?";
// execute($sql, [$status->value, $id]);
}
public function findStatus(int $id): ?UserStatus
{
// 从数据库获取原始值
$rawValue = 'active'; // 从查询结果获取
return UserStatus::tryFrom($rawValue);
}
}详细说明
序列化格式说明
PHP 内部序列化枚举的格式:
纯枚举: E:枚举名长度:"枚举名":case数量:{s:4:"name";s:case名长度:"case名";}
Backed Enum: E:枚举名长度:"枚举名":case数量:{s:4:"name";s:case名长度:"case名";s:5:"value";s/i:值;}json_encode 的行为
| 枚举类型 | json_encode 输出 | 说明 |
|---|---|---|
| 纯枚举 | "CaseName" (字符串) | 输出 name 属性 |
| string Backed Enum | "value" (字符串) | 输出 value 属性 |
| int Backed Enum | value (整数) | 输出 value 属性 |
json_decode 不会自动还原枚举
php
<?php
declare(strict_types=1);
enum Status: string
{
case Active = 'active';
case Inactive = 'inactive';
}
// json_decode 返回原始类型,不是枚举
$json = '"active"';
$decoded = json_decode($json);
var_dump(gettype($decoded)); // string
// 需要手动转换
$enum = Status::from($decoded);
var_dump($enum instanceof Status); // trueSession 中的枚举
php
<?php
declare(strict_types=1);
enum Theme: string
{
case Light = 'light';
case Dark = 'dark';
case Auto = 'auto';
}
session_start();
// 存储到 Session
$_SESSION['theme'] = Theme::Dark;
// 从 Session 读取
$theme = $_SESSION['theme'];
var_dump($theme instanceof Theme); // true
var_dump($theme->value); // "dark"实战示例
场景一:API 响应中的枚举序列化
php
<?php
declare(strict_types=1);
enum TaskStatus: string implements \JsonSerializable
{
case Todo = 'todo';
case InProgress = 'in_progress';
case Done = 'done';
public function jsonSerialize(): mixed
{
return [
'key' => $this->value,
'label' => $this->label(),
'color' => $this->color(),
];
}
public function label(): string
{
return match ($this) {
self::Todo => '待办',
self::InProgress => '进行中',
self::Done => '已完成',
};
}
public function color(): string
{
return match ($this) {
self::Todo => '#6B7280',
self::InProgress => '#3B82F6',
self::Done => '#10B981',
};
}
}
class TaskResponse
{
public function __construct(
public readonly string $title,
public readonly TaskStatus $status
) {}
public function toArray(): array
{
return [
'title' => $this->title,
'status' => $this->status,
];
}
}
$task = new TaskResponse('完成文档', TaskStatus::InProgress);
echo json_encode($task->toArray(), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);输出:
json
{
"title": "完成文档",
"status": {
"key": "in_progress",
"label": "进行中",
"color": "#3B82F6"
}
}场景二:配置文件中的枚举持久化
php
<?php
declare(strict_types=1);
enum LogDriver: string
{
case File = 'file';
case Syslog = 'syslog';
case Stdout = 'stdout';
public static function fromConfig(string $configPath): self
{
if (!file_exists($configPath)) {
return self::File;
}
$config = json_decode(file_get_contents($configPath), true);
return self::tryFrom($config['log']['driver'] ?? '')
?? self::File;
}
public function saveConfig(string $configPath, array $otherConfig = []): void
{
$config = array_merge($otherConfig, [
'log' => [
'driver' => $this->value,
],
]);
file_put_contents(
$configPath,
json_encode($config, JSON_PRETTY_PRINT)
);
}
}注意事项
注意事项
json_decode()不会自动将值还原为枚举,需要手动调用from()或tryFrom()- 枚举的序列化格式是 PHP 内部格式,不适用于跨语言传输
- 实现
JsonSerializable接口可以自定义 JSON 输出格式 - 在 Session 中存储枚举时,PHP 会自动处理序列化和反序列化
小贴士
- 对于 API 响应,实现
JsonSerializable自定义输出格式 - 对于数据库存储,使用 Backed Enum 的
value属性 - 对于缓存,PHP 原生序列化即可
最佳实践
1. 为 Backed Enum 统一 JSON 格式
php
<?php
declare(strict_types=1);
enum ErrorCode: int implements \JsonSerializable
{
case Success = 0;
case NotFound = 1004;
public function jsonSerialize(): mixed
{
return ['code' => $this->value, 'message' => $this->message()];
}
public function message(): string { /* ... */ }
}2. 使用 tryFrom 处理外部数据
php
<?php
declare(strict_types=1);
// 始终使用 tryFrom 处理来自 JSON、数据库、用户输入的值
$status = Status::tryFrom($request->get('status'));
if ($status === null) {
throw new ValidationException('Invalid status');
}