在 PHP 中根据名称创建枚举

waw*_*awa 10 php enums

在 PHP 8.1 中,BackedEnum提供了fromandtryFrom方法来从值获取枚举。如何通过无支持的枚举来实现相同的效果?

支持枚举示例:

enum MainType: string
{
    case Full = 'a';
    case Major = 'b';
    case Minor = 'c';
}

var_dump(MainType::tryFrom('a')); // MainType::Full
var_dump(MainType::tryFrom('d')); // null
Run Code Online (Sandbox Code Playgroud)

然而,这对于常规枚举来说并不存在。

我如何按名称检索“正常”枚举,例如:

enum MainType
{
    case Full;
    case Major;
    case Minor;
}

$name = (MainType::Full)->name
var_dump(name); // (string) Full
Run Code Online (Sandbox Code Playgroud)

我发现的一个选择是简单地添加一个tryFromName函数,接受一个字符串并循环遍历所有情况,如下所示:

enum MainType
{
    case Full;
    case Major;
    case Minor;

    public static function tryFromName(string $name): ?static
    {
        foreach (static::cases() as $case) {
            if ($case->name === $name) {
                return $case;
            }
        }

        return null;
    }
}

$name = (MainType::Full)->name
var_dump(name); // (string) Full
var_dump(MainType::tryFromName($name)); // MainType::Full
Run Code Online (Sandbox Code Playgroud)

这是可行的,但是让 foreach 循环遍历所有可能性只是为了创建一个枚举是违反直觉的。

因此问题是,在 PHP 中从名称获取 Enum 的正确方法是什么。

hej*_*dav 10

您可以使用反射:

trait Enum {

    public static function tryFromName(string $name): ?static
    {
        $reflection = new ReflectionEnum(static::class);

        return $reflection->hasCase($name)
            ? $reflection->getCase($name)->getValue()
            : null;
    }

}

enum Foo {
    use Enum;

    case ONE;
    case TWO;
}

var_dump( Foo::tryFromName('TWO')   ); // enum(Foo::TWO)
var_dump( Foo::tryFromName('THREE') ); // null
Run Code Online (Sandbox Code Playgroud)

也适用于支持枚举。


Dun*_*moo 4

ReflectionEnum我喜欢在@hejdav 的答案中使用,它适用于 PHP8.1+,这将是我的偏好。

不幸的是,它失败了 Phpstan 测试Method EnumClass::tryFromName() should return EnumClass|null but returns UnitEnum|null.,所以我求助于使用以下内容:

trait EnumFromName
{
    /**
     * To mirror backed enums tryFrom - returns null on failed match.
     */
    public static function tryFromName(string $name): ?static
    {
        foreach (self::cases() as $case) {
            if ($case->name === $name) {
                return $case;
            }
        }

        return null;
    }

    /**
     * To mirror backed enums from - throws ValueError on failed match.
     */
    public static function fromName(string $name): static
    {
        $case = self::tryFromName($name);
        if (! $case) {
            throw new ValueError($name.' is not a valid case for enum '.self::class);
        }

        return $case;
    }
}
Run Code Online (Sandbox Code Playgroud)