滚动新闻 →
美AI无人载具新创携台厂布局平价化战略 霜降养生指南:羊肉汤与柿子的食补智慧 日本将实施新免税政策:11、1起先付含税价再退税 支持USB-C充电的笔记本无法充电,功率不足也可能造成什么现象 郭德纲红歌改编事件持续发酵:太原演出遭追溯举报 台东巨浪冲击国际地标 机车被冲倒海巡紧急救援 Windows 11 本地账户和微软账户有什么区别?选择之前需要了解这些 五中全会前夕政治局提前召开会议 人事调整引发关注 重庆四人聚餐后舌苔异常发黑 原因待查 MySQL 排序规则 collation 怎么理解?查询中文数据时需要注意什么 宋词为什么与唐诗有不同的魅力 娃哈哈集团总经理许思敏辞任 任职不足一年 黄仁勋柏林GTC演讲突发插曲 抗议与机器鸭成焦点 核桃在中国传统饮食中的位置 台风蔷琵暴风圈全面笼罩台湾 今明降雨持续 书法练习为什么不能只追求速度 让孩子学会为别人的成功感到高兴 存储卡速度对视频拍摄有什么影响 加拿大经销商指控徐工集团设备造假 涉案金额达6500万加元 九旬老人被女儿赶出家门 楼道生活近一年 胡春华突缺席高层会议 引发出事传闻与政局猜测 蒙古骑兵为什么能够改变中世纪战争方式 中国网红在台违规推广代孕遭重罚并被驱逐出境 不重新装修如何让老房子看起来更整洁 北京更大政治风暴 传多人被带走 蔡奇之子涉案 加拿大军事基地榴弹发射器靶场发生火灾致4死 面食中的手工技艺为什么值得关注 美国中期选举进入白热化 七州州长选战引关注 中央公园马车运营将终结 纽约市禁令引发争议 台风蔷琵减弱,暴风圈今晨四点接触台湾陆地,21县市停班停课 弗吉尼亚州民主党参议员涉毒洗钱被拘 川普力排众议破年龄壁垒 莱维特成最年轻白宫发言人 法国确认首例人类感染沙蒙达病毒病例 患者不幸去世 加拿大圣安妮峡谷的秋色,从山林一直延伸到河谷 俄乌和谈重启遇阻 停火方案分歧未解 数字战场再升级 乌军精准打击俄AI枢纽 西藏那曲巴青县7.0级地震致16人遇难 民众惊慌逃生影像曝光 AI投资成本高企 美股科技股集体下挫 发动机故障灯亮了首先应该做什么 法国首现人类感染沙蒙达病毒病例

PHP 枚举 Enum 怎么使用?用更清晰的方式管理固定状态和类型

发布时间: 2026-10-02 14:24:01    最后更新: 2026-10-20 00:09:44    阅读:69  约15 分钟阅读     

PHP 枚举 Enum 怎么使用?用更清晰的方式管理固定状态和类型
图片说明:示意图   图片来源:Public Domain(公有领域)
在企业软件、SaaS、后台系统和API开发中,有一类数据看起来只是几个固定值,实际上却直接参与业务逻辑。例如用户可能处于active、inactive、disabled几种状态,订单可能处于pending、paid、cancelled等阶段,权限可能分为read、write、admin。传统PHP项目经常使用整数常量、字符串常量或者数据库字段直接保存这些值,代码能够运行,但随着项目规模扩大,状态值会逐渐散落在控制器、Model、Service和数据库查询中。 PHP 8.1引入Enum以后,这类固定集合可以被建模成真正的类型。枚举不仅可以表达“有哪些合法状态”,还可以让函数参数、返回值、match分支和业务方法围绕这个类型工作,从而减少字符串和整数在系统各处自由流动的问题。 PHP Enum从哪个版本开始支持 原稿中“PHP 7.1引入Enum”这一点是错误的。PHP原生枚举是在PHP 8.1正式加入的,因此使用enum语法的项目最低需要PHP 8.1。 最基本的枚举可以这样定义: enum UserStatus { case Active; case Inactive; case Disabled; } 这里的Active、Inactive和Disabled不是普通字符串,也不是整数常量,而是UserStatus类型的枚举Case。 使用时: $status = UserStatus::Active; 此时$status是一个UserStatus对象实例,而不是字符串"active"。 因此下面的类型声明是有意义的: function updateUserStatus(UserStatus $status): void { // ... } 调用者必须传入UserStatus的Case。如果错误地传入一个普通字符串或整数,在严格的类型检查环境下就会暴露类型问题。 这也是Enum与传统常量最大的区别之一:传统常量主要提供一个名字和值,而Enum首先建立的是一个独立的类型。 Backed Enum适合数据库中的状态字段 实际SaaS系统通常需要把状态保存到MySQL、PostgreSQL等数据库中,因此单纯的Unit Enum还不够方便。 PHP提供了Backed Enum,可以让每个Case关联一个字符串或整数值。 例如: enum UserStatus: string { case Active = 'active'; case Inactive = 'inactive'; case Disabled = 'disabled'; } 现在: $status = UserStatus::Active; echo $status->value; 结果是: active 这里需要区分两个概念。 UserStatus::Active是Enum Case,也就是一个UserStatus对象;$status->value才是与数据库、API或者其他外部系统交互时使用的底层字符串。 因此数据库字段可以保存active,PHP业务层则可以使用: UserStatus::Active 这样可以把数据库中的原始值和业务代码中的类型区分开。 from和tryFrom解决外部值转换问题 Backed Enum还有两个非常实用的方法:from()和tryFrom()。 如果确定外部值一定合法,可以使用: $status = UserStatus::from('active'); 它会得到: UserStatus::Active 如果传入不存在的值,例如: $status = UserStatus::from('unknown'); 则会抛出ValueError。 对于来自用户输入、数据库旧数据或者第三方API的数据,通常更适合: $status = UserStatus::tryFrom('unknown'); 找不到对应Case时,tryFrom()返回null,而不是直接抛出异常。 例如: $status = UserStatus::tryFrom($input); if ($status === null) { // 非法状态值 } 这在API参数验证和旧系统数据迁移中尤其有用。 Enum与match组合后业务逻辑会更加清晰 Enum最适合与PHP 8.0加入的match表达式结合。 正确写法应该是: $message = match ($status) { UserStatus::Active => '用户已激活', UserStatus::Inactive => '用户未激活', UserStatus::Disabled => '用户已禁用', }; 这里不能写成: UserStatus::Active => echo '用户已激活' 因为match的每个分支需要产生一个表达式结果,而echo不是这种用法下的返回表达式。 Enum和match结合还有一个非常重要的工程价值:状态集合发生变化时,业务代码更容易发现遗漏。 例如以后新增: case Pending; 那么原来的match如果没有处理Pending,在运行时就可能出现未匹配分支的问题。 相比散落在代码中的: if ($status === 'active') { ... } elseif ($status === 'inactive') { ... } Enum能够让状态集合拥有更加明确的边界。 Enum可以定义自己的方法 Enum并不是只能存储几个Case。 它可以定义方法: enum UserStatus: string { case Active = 'active'; case Inactive = 'inactive'; case Disabled = 'disabled'; public function label(): string { return match ($this) { self::Active => '已激活', self::Inactive => '未激活', self::Disabled => '已禁用', }; } } 调用: $status = UserStatus::Active; echo $status->label(); 这样与状态相关的显示逻辑就可以集中在Enum内部,而不是在几十个Controller中重复写。 当然,也不能把所有业务逻辑都塞进Enum。简单的状态描述、状态分类、是否允许某种操作等与状态本身高度相关的逻辑适合放进去;如果涉及数据库查询、外部API、复杂工作流或者多个领域对象,则应该继续放在Service等业务层。 Enum可以遍历所有Case 如果需要获取一个枚举的所有Case,可以使用: foreach (UserStatus::cases() as $status) { echo $status->value; } cases()会返回这个Enum定义的所有Case。 这对于后台管理页面生成状态选项、API文档、验证逻辑或者测试数据都很方便。 例如: foreach (UserStatus::cases() as $status) { echo ''; } 这样新增一个状态以后,前端选项可以通过统一逻辑自动生成,而不需要手工维护一组字符串。 Enum也可以实现接口 如果多个Enum需要提供统一能力,可以让它们实现接口。 例如: interface HasLabel { public function label(): string; } 然后: enum UserStatus: string implements HasLabel { case Active = 'active'; case Disabled = 'disabled'; public function label(): string { return match ($this) { self::Active => '已激活', self::Disabled => '已禁用', }; } } 这样其他代码就可以依赖接口,而不是依赖某一个具体Enum。 这也是Enum逐渐从“固定值列表”变成业务类型的重要原因。 Enum与数据库设计不能混为一谈 PHP Enum并不意味着数据库一定要使用MySQL的ENUM字段。 例如PHP可以使用: enum OrderStatus: string { case Pending = 'pending'; case Paid = 'paid'; case Cancelled = 'cancelled'; } 数据库完全可以使用: status VARCHAR(20) NOT NULL 然后在应用层将数据库值转换为OrderStatus。 对于很多SaaS系统,这种设计比把业务状态完全绑定到数据库原生ENUM更加灵活。 如果业务状态变化频繁,数据库CHECK约束、外键字典表或者应用层Enum也可以根据具体需求组合使用。 关键不是“必须使用哪一种”,而是要明确哪一层负责约束什么。 数据库负责数据完整性,PHP Enum负责应用程序类型和业务代码中的状态表达,两者并不是互相替代的关系。 Enum比常量好在哪里 传统代码可能这样定义: class UserStatus { public const ACTIVE = 'active'; public const INACTIVE = 'inactive'; public const DISABLED = 'disabled'; } 然后: $status = UserStatus::ACTIVE; 这种方式当然仍然有效,而且在旧版本PHP项目中非常常见。 问题在于UserStatus::ACTIVE本身不是一个UserStatus类型。数据库查询、HTTP请求、数组和业务逻辑之间仍然可能到处传递字符串。 Enum则可以直接写: function suspendUser(UserStatus $status): void { ... } 这让函数接口本身表达了“这里接受的是哪一种状态”。 静态分析工具、IDE自动补全以及代码重构工具也能够更准确地理解这种关系。 因此Enum的价值并不是“代码更短”,而是让固定集合成为代码中的类型边界。 Enum也不是所有固定值都应该使用 Enum同样不能滥用。 如果一个值本身没有固定集合,例如用户昵称、商品名称、电话号码、金额,就不应该因为想要“类型安全”而建立Enum。 如果状态来自动态配置,而且管理员可以随时增加新的类型,那么Enum也未必适合,因为Enum的Case属于代码的一部分,需要经过代码部署才能增加。 还有一种常见误区,是把Enum当成数据库业务规则的全部实现。 例如订单状态从pending变成paid,并不意味着只要Enum里存在这两个Case,就允许任何地方随意修改。 真正的订单状态转换规则可能是: pending → paid pending → cancelled paid → refunded 而: cancelled → paid 可能根本不允许。 Enum负责描述“有哪些状态”,状态机或者业务Service负责描述“状态之间允许怎样转换”。这两个概念需要分开。 在现代PHP项目中,Enum更适合放在领域模型附近 对于一个SaaS项目,可以把类似用户状态、订阅状态、订单状态、支付状态、权限类型等稳定的固定集合定义成Enum。 例如: enum SubscriptionStatus: string { case Trialing = 'trialing'; case Active = 'active'; case PastDue = 'past_due'; case Cancelled = 'cancelled'; } Service层接收: function changeSubscriptionStatus( SubscriptionStatus $status ): void { // ... } 数据库层保存: $status->value 从数据库读取以后: $status = SubscriptionStatus::from($row['status']); 这样数据库、Repository、Service和Controller之间就形成了清晰的类型边界。 如果项目使用ORM,例如Laravel、Symfony生态中的相关组件,也可以进一步利用框架对Backed Enum的支持,让数据库字段与PHP Enum之间进行自动转换,但具体方式应该按照所使用的ORM版本实现,而不是把所有框架都当成同一种行为。 PHP Enum真正有价值的地方,不是把1、2、3换成几个漂亮的名字,而是把原本散落在程序里的“允许值集合”提升成一个明确的类型。对于大型PHP项目,这种边界能够让IDE、静态分析器、测试代码以及开发人员共同理解同一套业务状态。 从传统常量到Enum,变化的也不只是语法。以前程序员需要记住“这个字符串到底可以写什么”,现在可以让类型系统直接参与约束。数据库仍然保存外部值,API仍然需要验证输入,Service仍然负责业务规则,而Enum负责把固定状态集合表达清楚。对于PHP 8.1及以上的新项目,只要业务确实存在稳定的固定值集合,Enum通常比散落的字符串和整数常量更值得优先考虑。

喜欢这篇报道?

使用下面的功能,方便以后继续阅读和分享 MNewsTV

设为 Google 新闻首选来源 让 Google 新闻优先显示 MNewsTV 的最新报道 ›
★ 我的收藏 查看已经收藏的文章
关于文章收藏 收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。 删除收藏请进入「我的收藏」进行管理。
分享这篇报道

捐助(Paypal): https://www.paypal.me/observeccp
订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP