PHP backed enum 怎样在 JSON 中输出稳定值
我在给 PHP JSON 接口整理状态字段时,最先想确认的不是“枚举能不能编码”,而是客户端最终拿到的值是否稳定。结论很明确:backed enum 通过 json_encode() 默认输出它的 string 或 int backed value;纯枚举没有标量值,不能直接按普通 JSON 值编码。需要输出对象结构时,再实现 JsonSerializable,把接口契约写成显式的 payload。
官方资料:https://www.php.net/enumerations.serialization
数据库和 API 要长期稳定时,让 backed value 负责机器可读的 code;不要把 case 名或展示文案偷偷变成客户端契约。只有确实需要多个字段时,才用 JsonSerializable 覆盖默认标量输出。
backed enum 的默认 JSON 表示是什么
PHP 官方文档把枚举分成 pure enum 和 backed enum。backed enum 只能使用一种标量类型:string 或 int,每个 case 都要显式声明唯一 value。这个 value 正是序列化到 JSON 时最适合作为接口 code 的部分。
可以把一个订单状态拆成三层:Paid 是代码里的 case 名,paid 是稳定的 backed value,“已支付”是给人看的文案。三层不应该混成一个字符串,否则改代码命名或改产品文案都会意外影响调用方。

用最小代码让接口返回稳定标量
如果接口只需要状态 code,直接让 backed enum 进入响应数组即可。下面的例子把 OrderStatus::Paid 放进响应对象,重点是注释中的边界:value 是公开契约,name 不是。
1001,
// 对外只暴露稳定的 backed value,不暴露 case 名 Paid。
'status' => OrderStatus::Paid,
];
try {
// 让编码失败直接抛出异常,避免接口静默返回 false。
echo json_encode($payload, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
} catch (JsonException $exception) {
// 生产代码应记录上下文,并交给统一错误处理中间件。
error_log($exception->getMessage());
http_response_code(500);
}
这里的 status 会按 backed value 输出为 "paid"。如果使用 int backed enum,输出则保持数字类型。不要为了“看起来更直观”改成 $status->name;case 名更像源码内部标识,重命名时不应迫使客户端迁移。
纯枚举为什么不能直接当 JSON 值
pure enum 没有 value,因此没有一个 PHP 可以自动放进 JSON 的标量。把它直接放到 json_encode() 中时,编码会失败。这个失败是有益的:它提醒我们必须明确选择“返回 case 名”“返回业务 code”,还是返回一个完整对象,而不是让序列化器替我们猜。
Visibility::Public], JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
// 这里应改为显式映射,而不是吞掉异常后返回空字段。
error_log('枚举 JSON 编码失败:' . $exception->getMessage());
}
如果领域上确实需要一个公开 code,就把它设计成 backed enum;如果纯枚举只服务于内部逻辑,可以在 DTO 映射时显式写出 visibility => 'public'。这两种选择都比依赖默认对象序列化更容易维护。
什么时候用 JsonSerializable 覆盖默认输出
backed value 足够表达状态时,不必增加自定义序列化层。只有在 API 需要同时返回稳定 code、展示 label 或能力标记时,JsonSerializable 才值得引入。官方接口允许 jsonSerialize() 返回能被 json_encode() 编码的值,因此可以返回数组,但要把字段当成版本化契约管理。

'待支付',
self::Paid => '已支付',
self::Cancelled => '已取消',
};
}
public function jsonSerialize(): array
{
// 输出结构必须稳定;不要把调试字段混入公共响应。
return [
'code' => $this->value,
'label' => $this->label(),
];
}
}
// 调用方拿到对象结构,仍以 code 作为机器判断依据。
echo json_encode(['status' => OrderStatus::Paid], JSON_THROW_ON_ERROR);
使用这个方案后,返回值不再是单纯的 "paid",而是包含 code 与 label 的对象。换句话说,实现 JsonSerializable 是一次接口形状选择。已经有客户端依赖标量输出时,不能只为方便展示文案就原地改成对象,应该新开字段或走版本化迁移。
数组和 DTO 中怎样保持边界清楚
枚举经常不是单独编码,而是嵌在列表、分页对象或 DTO 中。backed enum 的默认行为可以自然地递归到数组里;自定义 JsonSerializable 则会在它所在的位置输出对象。团队需要先决定整个响应是“code 优先”还是“对象优先”,不要同一个资源的不同字段各自选择。
$this->id,
'status' => [
// code 给程序判断,label 给界面展示,两者职责分开。
'code' => $this->status->value,
'label' => $this->status->label(),
],
];
}
}
$orders = [new OrderView(1001, OrderStatus::Paid)];
// 列表里的 DTO 仍然使用同一套 JSON_THROW_ON_ERROR 错误策略。
echo json_encode($orders, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
我更倾向于让 DTO 负责响应形状,让 enum 负责状态本身的合法值和文案方法。这样一个 enum 可以服务数据库、消息和多个接口,而不同接口是否需要 label、权限提示或链接字段,由各自 DTO 做决定。
值演进时怎样避免旧客户端被打坏
稳定输出不等于永远不能新增状态。真正需要控制的是旧客户端遇到新 code 时的行为,以及后端是否会把旧 code 直接改名。新增 backed value 前,先确认读取方能够把未知值当作“未知状态”处理;修改已有 value 则应视为数据和接口协议迁移,而不是普通重构。
- 新增 case:服务端先保证旧客户端能容忍未知 code,再开始写入新值。
- 修改 value:保留旧值读取兼容,完成数据迁移后再停止写旧值。
- 修改 label:通常不影响机器判断,但要考虑缓存和多语言客户端。
- 从标量改对象:不要覆盖原字段的形状,优先增加新字段或升级 API 版本。
接口测试至少覆盖三件事:每个合法 case 的 code 是否稳定、JSON 类型是否保持 string/int、未知输入是否按约定报错或降级。测试的是公开契约,不是 enum 的内部 case 排列。
常见问题
backed enum 会在 JSON 中输出 name 还是 value?
默认输出 backed value,也就是显式声明的 string 或 int。case 名是源码标识,不应默认当成 API code。
pure enum 能不能直接 json_encode?
不能把它当作普通 JSON 标量直接编码。请改用 backed enum,或在 DTO 中显式映射为公开字符串。
实现 JsonSerializable 后还能得到原来的字符串吗?
可以,但要在 jsonSerialize() 中返回 $this->value。一旦返回数组,接口形状就变成对象,必须按兼容性变更管理。
接口只返回 code 还是 code 加 label?
跨系统协议优先返回稳定 code;同一接口明确需要展示文案时再返回对象。label 是展示辅助信息,不能替代机器判断字段。
小结
PHP backed enum 的 JSON 默认输出很适合做稳定接口 code:让 json_encode() 输出 backed value,使用 JSON_THROW_ON_ERROR 把失败显式暴露出来;pure enum 则在 DTO 边界显式映射。需要 code、label 等多字段时,用 JsonSerializable 清楚声明新形状,并把新增值、改名和标量转对象都当成接口兼容问题处理。
AI 工作负载为何推动可观测性标准扩展
- 上一篇
- AI 工作负载为何推动可观测性标准扩展
- 下一篇
- PHP ReflectionClass 怎样按参数动态实例化对象
-
- 文章 · php教程 | 41分钟前 | 性能优化 · PHP · php Composer 自动加载器 Classmap dump-autoload
- Composer 怎样优化生产环境的自动加载器
- 118浏览 收藏
-
- 文章 · php教程 | 46分钟前 |
- PHP NumberFormatter 怎样按地区格式化金额
- 346浏览 收藏
-
- 文章 · php教程 | 46分钟前 |
- PHP stream filter 怎样串联字符转换
- 157浏览 收藏
-
- 文章 · php教程 | 47分钟前 | PHP · utf-8 · 字符串处理 · php mbstring mb_strwidth 终端显示宽度
- PHP mb_strwidth 怎样计算终端显示宽度
- 241浏览 收藏
-
- 文章 · php教程 | 48分钟前 | php 文件上传 php.ini upload_max_filesize post_max_size
- PHP 上传失败怎样区分两个大小限制
- 211浏览 收藏
-
- 文章 · php教程 | 53分钟前 |
- PHP PDO 怎样统一数字字段的返回类型
- 270浏览 收藏
-
- 文章 · php教程 | 54分钟前 |
- PHP PDO 模拟预处理怎样影响参数行为
- 308浏览 收藏
-
- 文章 · php教程 | 55分钟前 | 协程 · 异常处理 · php教程 · php Fiber Fiber::suspend Fiber::throw 协程异常处理
- PHP Fiber 怎样向挂起协程注入异常
- 166浏览 收藏
-
- 文章 · php教程 | 1小时前 |
- PHP enum 怎样用 match 覆盖未知分支
- 383浏览 收藏
-
- 文章 · php教程 | 1小时前 | 反射 · php教程 · 语言特性 · php 反射 ReflectionClass newInstanceArgs 对象工厂
- PHP ReflectionClass 怎样按参数动态实例化对象
- 399浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 474次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 533次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 553次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 506次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 326次使用
-
- 接口返回 200 但前端仍报错怎么办:从响应格式到跨域一步步排查
- 2026-06-14 332浏览
-
- golang生成JSON以及解析JSON
- 2023-01-17 329浏览
-
- Go如何实现json字符串与各类struct相互转换
- 2023-01-07 377浏览
-
- Go项目怎么使用枚举
- 2023-01-07 489浏览
-
- Go中使用gjson来操作JSON数据的实现
- 2023-01-07 141浏览
