PHP 8.1版本正式引入了枚举(enum)特性,让开发者可以用更规范的方式定义固定取值的集合,替代之前用常量组合实现的伪枚举方案。在实际业务场景中,经常会遇到前端或接口传入字符串参数,需要转换成对应的枚举案例的情况,比如传入"pending"需要匹配到订单状态枚举中的待处理案例。

基础枚举定义示例
首先我们定义一个简单的订单状态枚举,后续所有策略都基于这个枚举展开:
<?php
enum OrderStatus: string
{
case PENDING = 'pending';
case PAID = 'paid';
case SHIPPED = 'shipped';
case COMPLETED = 'completed';
case CANCELLED = 'cancelled';
}
策略一:使用枚举内置的tryFrom方法
PHP枚举自带了tryFrom方法,支持传入字符串尝试匹配枚举案例,匹配成功返回对应案例,失败返回null,这是最简洁的实现方式。
实现代码
<?php
$input = 'paid';
$status = OrderStatus::tryFrom($input);
if ($status !== null) {
echo "匹配成功,枚举案例为:" . $status->name;
} else {
echo "未找到匹配的枚举案例";
}
优缺点分析
- 优点:语法简洁,无需额外编写匹配逻辑,内置方法性能稳定
- 缺点:匹配失败仅返回null,无法自定义异常信息,且严格区分大小写,传入"Paid"会匹配失败
策略二:自定义不区分大小写的匹配方法
如果业务需要支持不区分大小写的字符串匹配,可以基于枚举的cases方法自定义匹配逻辑,遍历所有枚举案例进行比对。
实现代码
<?php
function getOrderStatusByString(string $input): ?OrderStatus
{
$lowerInput = strtolower($input);
foreach (OrderStatus::cases() as $case) {
if (strtolower($case->value) === $lowerInput) {
return $case;
}
}
return null;
}
$input = 'Paid';
$status = getOrderStatusByString($input);
var_dump($status); // 输出 OrderStatus 的 PAID 案例
适用场景
适合需要兼容不同大小写输入的场景,比如前端传入的参数大小写不统一的接口场景。
策略三:带异常抛出的严格匹配方案
如果业务要求字符串必须匹配到枚举案例,匹配失败需要抛出明确的异常,可以封装一个带异常处理的匹配方法。
实现代码
<?php
class EnumMatchException extends Exception {}
function getOrderStatusStrict(string $input): OrderStatus
{
$status = OrderStatus::tryFrom($input);
if ($status === null) {
throw new EnumMatchException("无效的订单状态字符串:{$input}");
}
return $status;
}
try {
$status = getOrderStatusStrict('refunded');
} catch (EnumMatchException $e) {
echo $e->getMessage(); // 输出 无效的订单状态字符串:refunded
}
优缺点分析
- 优点:匹配失败时抛出明确异常,便于上层业务捕获处理,符合严格校验的业务需求
- 缺点:需要额外定义异常类,代码量比内置方法更多
不同策略的对比选择
我们可以通过下表快速选择适合自己业务的方案:
| 策略方案 | 大小写敏感 | 失败返回值 | 适用场景 |
|---|---|---|---|
| 内置tryFrom方法 | 是 | null | 简单场景,输入大小写固定,允许匹配失败返回null |
| 自定义不区分大小写匹配 | 否 | null | 输入大小写不固定,允许匹配失败返回null |
| 带异常严格匹配 | 是 | 抛出异常 | 必须匹配到枚举,失败需要明确错误提示的场景 |
注意事项
使用枚举匹配时需要注意,枚举的value是定义时指定的字符串,name是枚举案例的名称,比如OrderStatus::PAID的name是PAID,value是paid,匹配时要确认是用name还是value做比对,避免逻辑错误。
如果枚举没有指定后端值类型(即纯枚举而不是 backed enum),则无法使用tryFrom方法,只能通过遍历cases方法匹配name属性来实现字符串到枚举的转换。