通八洲科技

PHP 枚举:从字符串获取非 Backed Enum 案例的实现方法

日期:2025-11-28 00:00 / 作者:花韻仙語

本文旨在解决在 php 中如何根据字符串名称获取非 backed enum 的枚举案例。由于非 backed enum 不支持 `tryfrom` 或 `from` 方法,文章将详细介绍通过自定义静态方法遍历枚举所有案例并进行名称匹配的实现方案,并提供示例代码和相关注意事项,帮助开发者有效管理和使用非 backed enum。

PHP 枚举基础与 Backed Enum 的区别

PHP 8.1 引入了枚举(Enums),为开发者提供了一种定义一组命名常量的新方式。在 PHP 中,枚举分为两种主要类型:

  1. 纯枚举(Pure Enums / Plain Enums):这类枚举只包含案例名称,没有关联的具体值。例如:

    对于纯枚举的案例,可以通过 Status::OK->name 获取其字符串名称 "OK"。

  2. 支持值枚举(Backed Enums):这类枚举的每个案例都关联一个具体的值,可以是字符串或整数。例如:

    支持值枚举的案例除了 name 属性外,还拥有 value 属性,例如 Status::OK->value 将返回 "success"。更重要的是,Backed Enums 提供了 from() 和 tryFrom() 静态方法,允许开发者通过关联值(而非案例名称)直接获取对应的枚举案例。例如 Status::tryFrom("success") 将返回 Status::OK。

问题:从字符串获取非 Backed Enum 案例

开发中常见的一种需求是,给定一个字符串(通常是枚举案例的名称),需要获取对应的枚举案例实例。对于 Backed Enum,如果字符串恰好是其关联值,可以直接使用 tryFrom() 方法。然而,对于纯枚举,我们没有关联值,也无法直接使用 tryFrom() 方法(因为它期望的是关联值而不是案例名称)。

例如,我们希望实现类似 Status::get("OK") 的功能,使其返回 Status::OK 实例,但又不希望将 Status 定义为 Backed Enum(即避免 case OK = "OK"; 这种重复定义)。

解决方案:自定义静态查找方法

由于 PHP 的纯枚举没有内置的通过名称查找案例的方法,最直接和推荐的解决方案是在枚举类内部实现一个自定义的静态方法来完成此功能。核心思路是遍历枚举的所有案例,并比较每个案例的 name 属性与传入的字符串是否匹配。

以下是一个实现此功能的示例代码:

name === $normalizedName) {
                return $status; // 找到匹配项,返回该案例
            }
        }

        // 未找到匹配的案例则返回 null
        return null;
    }
}

// 示例用法
$statusOk = Status::get("OK"); // 返回 Status::OK
$statusFailed = Status::get("  failed  "); // 经过内部处理,返回 Status::FAILED
$statusPending = Status::get("pending"); // 经过内部处理,返回 Status::PENDING
$statusNotFound = Status::get("UNKNOWN"); // 未找到,返回 null

var_dump($statusOk);
var_dump($statusFailed);
var_dump($statusPending);
var_dump($statusNotFound);

// 输出示例:
// object(Status)#1 (0) {
// }
// object(Status)#2 (0) {
// }
// object(Status)#3 (0) {
// }
// NULL
?>

代码解析:

  1. public static function get(string $name): ?Status
    • 定义了一个公共的静态方法 get,可以直接通过类名调用(Status::get(...))。
    • 它接受一个字符串 $name 作为参数,表示要查找的枚举案例名称。
    • 返回类型 ?Status 表示该方法可能返回 Status 类的实例,也可能返回 null(如果未找到匹配的案例)。
  2. $normalizedName = strtoupper(trim($name));
    • 这是一个重要的预处理步骤。trim() 函数用于去除字符串两端的空白字符,strtoupper() 函数将字符串转换为大写。
    • 这样做是为了增强查找的健壮性,使得输入 'ok'、' OK ' 或 'pending' 都能正确匹配到 Status::OK 或 Status::PENDING,而无需严格区分大小写和空白。
  3. if (empty($normalizedName)) { return null; }
    • 处理了输入字符串为空或仅包含空白字符的情况,避免不必要的遍历和潜在错误。
  4. foreach (self::cases() as $status)
    • self::cases() 是 PHP 枚举内置的静态方法,它返回一个包含所有枚举案例实例的数组。
    • 通过 foreach 循环,我们可以逐一访问枚举中的每个案例。
  5. if ($status->name === $normalizedName)
    • 在循环内部,我们将当前枚举案例的 name 属性(例如 Status::OK->name 结果为 "OK")与经过标准化处理的输入名称 $normalizedName 进行严格比较。
    • 如果匹配成功,则说明找到了对应的枚举案例,立即将其返回。
  6. return null;
    • 如果循环结束后仍未找到匹配的案例,则表示传入的名称无效,方法返回 null。

注意事项与最佳实践

总结

尽管 PHP 的纯枚举没有像 Backed Enum 那样内置 tryFrom() 或 from() 方法来通过字符串名称获取案例,但通过在枚举类中实现一个自定义的静态查找方法,可以优雅且高效地解决这一问题。这种方法利用了 self::cases() 提供的所有枚举案例列表,结合字符串处理和比较逻辑,实现了灵活的案例查找功能,同时保持了代码的清晰性和可维护性。理解纯枚举和 Backed Enum 的区别,并根据具体需求选择合适的实现方式,是有效利用 PHP 枚举的关键。