当前位置:首页 > 文章列表 > 文章 > php教程 > PHP backed enum 怎样在 JSON 中输出稳定值

PHP backed enum 怎样在 JSON 中输出稳定值

来源:17golang原创 2026-10-15 03:20:59 0浏览 收藏

我在给 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,“已支付”是给人看的文案。三层不应该混成一个字符串,否则改代码命名或改产品文案都会意外影响调用方。

PHP backed enum 的 case、backed value、json_encode 与 JSON 字段之间的静态关系说明图
图1:PHP backed enum 从 case 到 JSON 标量值的静态关系示意图;这是原创说明图,不是运行截图。

用最小代码让接口返回稳定标量

如果接口只需要状态 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() 编码的值,因此可以返回数组,但要把字段当成版本化契约管理。

PHP OrderStatus 通过 JsonSerializable 输出稳定 code、展示 label 与 DTO 客户端契约的关系说明图
图2:JsonSerializable 覆盖枚举默认 JSON 表示时的契约边界示意图;这是原创说明图,不是实际接口截图。
 '待支付',
            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 清楚声明新形状,并把新增值、改名和标量转对象都当成接口兼容问题处理。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
AI 工作负载为何推动可观测性标准扩展AI 工作负载为何推动可观测性标准扩展
上一篇
AI 工作负载为何推动可观测性标准扩展
PHP ReflectionClass 怎样按参数动态实例化对象
下一篇
PHP ReflectionClass 怎样按参数动态实例化对象
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    474次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    533次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    553次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    506次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    326次使用