
本文旨在深入探讨在 PHP 中如何通过字符串名称或值获取枚举(Enum)成员的实用策略。我们将详细介绍利用支持枚举(Backed Enums)的 `tryFrom` 或 `from` 方法,以及为纯枚举(Pure Enums)实现自定义静态查找功能,旨在解决在不重复定义字符串值的情况下进行高效查找的需求。
在 PHP 8.1 及更高版本中引入的枚举(Enums)为定义一组有限的常量值提供了强大的类型安全机制。在实际开发中,我们经常需要根据一个字符串(例如,从用户输入、API 请求或数据库中获取)来查找并获取对应的枚举成员。本文将详细介绍两种主要的方法来解决这一常见需求。
一、利用支持枚举(Backed Enums)进行查找
PHP 枚举分为两种类型:纯枚举(Pure Enums)和支持枚举(Backed Enums)。支持枚举允许每个枚举成员关联一个标量值(字符串或整数),这为通过这些值进行查找提供了便利的内置机制。
1. 定义支持枚举
要创建一个支持枚举,你需要为它指定一个类型(string 或 int),并为每个成员提供一个相应类型的值。
立即学习“PHP免费学习笔记(深入)”;
在这个例子中,Status 枚举的每个成员都关联了一个字符串值。值得注意的是,为了通过成员的名称(例如 "OK")进行查找,我们通常会将成员的名称作为其关联值,尽管这正是用户最初试图避免的“重复定义”。
2. 使用 tryFrom 和 from 方法
支持枚举提供了两个静态方法来根据其关联值进行查找:
- tryFrom(string|int $value): ?self: 尝试从给定的值中获取对应的枚举成员。如果找不到匹配的成员,它会返回 null,这使得它非常适合处理不确定的输入,而无需额外的错误处理。
- from(string|int $value): self: 从给定的值中获取对应的枚举成员。如果找不到匹配的成员,它会抛出一个 ValueError 异常。此方法适用于你确信输入值一定有效的情况。
示例代码:
getMessage() . "\n"; } // 获取枚举成员的名称和值 echo "Status::OK 的名称: " . Status::OK->name . "\n"; // 输出: OK echo "Status::OK 的值: " . Status::OK->value . "\n"; // 输出: OK
优点:
- 原生支持: 这是 PHP 枚举的内置功能,无需额外代码。
- 类型安全: 返回的是具体的枚举成员实例。
- 健壮性: tryFrom 提供了优雅的错误处理机制。
局限性:
- 值重复: 如示例所示,为了通过名称查找,通常需要将枚举成员的名称作为其关联值,这可能导致代码中的字符串重复。这是用户最初希望避免的痛点。
- 仅通过值查找: tryFrom 和 from 只能通过成员的 值 进行查找,不能直接通过成员的 名称 进行查找(除非名称和值相同)。
二、为纯枚举(Pure Enums)实现自定义静态查找方法
当你不希望为每个枚举成员重复定义一个与其名称相同的字符串值时,纯枚举结合自定义静态方法是一个更灵活的选择。纯枚举不关联任何标量值,但每个枚举成员都隐式地拥有一个 name 属性,其值为该成员的声明名称。
1. 定义纯枚举
纯枚举的定义不指定任何类型或关联值:
2. 实现自定义静态查找方法
我们可以为纯枚举添加一个自定义的静态方法,该方法遍历所有枚举成员,并将其 name 属性与输入的字符串进行比较。
示例代码:
name) === $cleanedName) { return $status; } } // 如果遍历结束后仍未找到匹配项,则返回 null return null; } } // 使用自定义的 get 方法进行查找 $status1 = Status::get("OK"); // 返回 Status::OK $status2 = Status::get("failed"); // 返回 Status::FAILED (由于内部转换为大写) $status3 = Status::get(" pending "); // 返回 Status::PENDING (去除空格并转换为大写) $status4 = Status::get("UNKNOWN"); // 返回 null $status5 = Status::get(""); // 返回 null // 获取纯枚举成员的名称 echo "Status::OK 的名称: " . Status::OK->name . "\n"; // 输出: OK实现原理:
- Status::cases(): 这是一个内置的静态方法,它返回一个包含所有枚举成员的数组。
- $status->name: 每个枚举成员(无论是纯枚举还是支持枚举)都有一个 name 属性,它返回该成员的声明名称(例如,Status::OK->name 返回 "OK")。
- 循环比较: 代码遍历 Status::cases() 返回的数组,将每个成员的 name 属性与传入的 $name 参数进行比较。
- 清理和标准化: 示例中的 get 方法还包含了 strtoupper(trim($name)),这使得查找更加健壮,可以处理输入字符串的大小写不一致和前后空格问题。
优点:
- 避免重复定义: 无需为枚举成员定义与其名称相同的 value。
- 高度定制化: 可以根据需要实现大小写不敏感、去除空格、自定义匹配逻辑等。
- 适用于纯枚举: 完美解决了纯枚举通过名称查找的需求。
注意事项:
- 非原生: 需要为每个需要此功能的枚举手动实现该静态方法。
- 性能: 对于拥有极大量成员的枚举,循环遍历可能会有轻微的性能开销,但在绝大多数实际应用中,枚举成员数量有限,此开销可忽略不计。
三、选择适合你的方法:对比与最佳实践
在决定使用哪种方法时,请考虑以下因素:
-
是否有业务关联值?
- 如果你的枚举成员需要关联一个特定的、在业务逻辑中有意义的字符串或整数值(例如,数据库中的状态码、API 响应中的错误代码),并且你希望通过这些 值 来查找枚举,那么支持枚举 (Backed Enum) 配合 tryFrom 或 from 是最自然、最推荐的选择。
- 例如:enum HttpCode: int { case OK = 200; case NOT_FOUND = 404; },你显然会通过 HttpCode::tryFrom(200) 来查找。
-
是否仅通过成员名称查找,且不希望重复定义值?
- 如果你的需求是仅通过枚举成员的 声明名称(例如 Status::OK 的 "OK")来查找,并且你希望避免在 Backed Enum 中将名称再次作为 value 重复定义,那么为纯枚举 (Pure Enum) 实现一个自定义的静态查找方法(如 get)是最佳方案。
-
代码简洁性与可维护性:
- 对于简单的查找,tryFrom 提供了极致的简洁性。
- 自定义方法虽然需要额外编写代码,但它提供了更高的灵活性和控制力,可以根据具体业务需求调整查找逻辑(如大小写敏感性)。
总结
在 PHP 中通过字符串获取枚举成员,主要有两种策略:
- 使用支持枚举(Backed Enums)的 tryFrom/from 方法:当你希望通过枚举成员的 关联值 进行查找时,这是最直接、最原生的方式。它利用了枚举内置的映射机制。
- 为纯枚举(Pure Enums)实现自定义静态方法:当你希望通过枚举成员的 名称 进行查找,并且不想为每个成员重复定义一个与其名称相同的关联值时,自定义静态方法提供了一个灵活且高效的解决方案,通过遍历 Status::cases() 并比较 name 属性来实现。
根据你的具体需求和对代码简洁性、灵活性以及数据冗余的权衡,选择最适合你的方法。在大多数情况下,如果枚举的名称本身就是你想要查找的字符串,自定义的 get 方法可以提供一个更“纯粹”的解决方案,避免了 Backed Enum 中名称与值重复的问题。











