PHP 枚举序列化JSON 输出枚举值时为什么格式不一致怎样完成一个可复用的最小示例
来源:17golang原创
时间:2026-11-10 03:53:47 447浏览 收藏
接口里有一个状态字段时,PHP 枚举经常会出现“同样是 case,JSON 输出却不一样”的现象。原因不是 json_encode() 随机变化,而是纯枚举和 Backed Enum 的类型契约不同:纯枚举没有标量值,直接 JSON 序列化会报错;Backed Enum 默认输出它的 string 或 int 值。如果客户端还需要稳定拿到 case 名称,就应当显式实现 JsonSerializable。
官方手册:https://www.php.net/manual/en/language.enumerations.serialization.php
先确认接口要传“存储值”还是“可读名称”,再决定直接使用 Backed Enum 的默认输出,还是把 name 和 value 组装成固定对象。不要把 PHP 的内部 serialize() 结果当成对外 JSON 契约。
先分清两种枚举的默认输出
我排查这类问题时,会先看枚举声明有没有冒号和标量类型。只有 enum Status { ... } 的是纯枚举;写成 enum Status: string { ... } 或 enum Status: int { ... } 的是 Backed Enum。前者的 case 只有名称,后者还绑定了一个可用于传输或存储的标量值。
PHP 手册规定,Backed Enum 进入 json_encode() 时默认按其标量值表示;纯枚举直接进入 JSON 序列化则会抛出错误。两者都可以通过实现 JsonSerializable 改写这一默认行为。

用最小示例看见 JSON 输出边界
下面的示例故意把两个枚举放在同一个数组里,重点不是做完整接口,而是把默认契约摆在眼前。OrderState::Paid 是 Backed Enum,因此可以直接得到字符串值;Visibility::Public 是纯枚举,不能依赖默认 JSON 转换。
OrderState::Paid,
'visibility' => Visibility::Public,
];
// 对外接口不要把 serialize() 的内部格式当作 JSON 契约。
$json = json_encode($payload, JSON_THROW_ON_ERROR);
echo $json, PHP_EOL;
如果暂时移除 visibility,这个数组可以自然得到类似 {"state":"paid"} 的 JSON。加入纯枚举后,默认行为就不再是“自动输出 case 名称”,而是报错。这个差异解释了为什么某些接口只传出 paid,另一些接口却在序列化阶段失败。
用 JsonSerializable 固定接口对象
当客户端既要展示 Paid,又要提交稳定的 paid 时,最稳妥的做法不是让客户端猜测字段,而是让枚举明确返回对象。下面让 Backed Enum 自己实现 JsonSerializable,同时保留默认的 value 语义。
$this->name,
'value' => $this->value,
];
}
}
// 外层业务对象保持普通数组,枚举负责自己的序列化契约。
$payload = ['status' => OrderStatus::Paid];
$json = json_encode($payload, JSON_THROW_ON_ERROR);
echo $json, PHP_EOL;
此时接口对象的形状可以稳定为 {"status":{"name":"Paid","value":"paid"}}。如果接口只需要数据库值,就不必为了展示名称而改变契约;可以在资源层显式使用 $status->value。如果还需要支持纯枚举,也可以让纯枚举实现同样的 JsonSerializable,返回只包含 name 的对象。

按接口契约选择字段并排查格式不一致
把选择落到项目里,可以按下面三种情况判断:
- 数据库和接口都只认固定编码:使用 Backed Enum 的
value,保持字符串或整数类型不变。 - 前端还要展示人类可读状态:实现
JsonSerializable,固定返回name与value,不要让每个调用方自行拼装。 - 枚举没有外部编码:不要假设纯枚举会自动变成名称;明确返回
$this->name或一个带语义的对象。
遇到输出异常时,先检查枚举声明是否带 backed type,再查调用点是否把整个 case 传给 json_encode()。接着确认有没有实现 JsonSerializable,以及返回数组里的字段类型是否和旧客户端约定一致。最后区分 serialize() 与 json_encode():前者服务于 PHP 的序列化恢复,后者才是常见接口响应的 JSON 表示。
相关问题
为什么 Backed Enum 默认只输出 value?
因为 backed value 是枚举声明中明确绑定的标量,适合保持接口和存储字段的稳定类型。case 名称是否展示,应由接口契约显式决定。
纯枚举可以直接输出 case 名称吗?
不能依赖默认行为。纯枚举应实现 JsonSerializable,在 jsonSerialize() 中返回 $this->name 或结构化数组。
JsonSerializable 会影响 PHP 的 serialize() 吗?
它主要定义 JSON 序列化时返回的值,不等同于 PHP 内部的 serialize() 规则。两种序列化用途不同,应分别设计和测试。
-
332 收藏
-
329 收藏
-
377 收藏
-
489 收藏
-
141 收藏
-
381 收藏
-
178 收藏
-
331 收藏
-
384 收藏
-
399 收藏
-
378 收藏
-
219 收藏
-
204 收藏
-
140 收藏
-
109 收藏
-
231 收藏
-
394 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习