Reputation: 5066
In PHP 8.1, BackedEnum offer a from
and tryFrom
method to get an enum from a value. How can the same be achieved by non backed enums?
Example BackedEnum:
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
However this doesn't exist for regular enums.
How would I retrieve a "normal" Enum by name, like:
enum MainType
{
case Full;
case Major;
case Minor;
}
$name = (MainType::Full)->name
var_dump(name); // (string) Full
One option I've found is to simply add a tryFromName
function, accepting a string and looping over all the cases like this:
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
This works, however it seams counter intuitive to enable a foreach loop going over all possibilities just to create an enum.
Therefore the question is, what is the right way to get an Enum in PHP from the name.
Upvotes: 13
Views: 5976
Reputation: 1
I can't comment on Duncanmoo's response since I have no reputation yet.
But in PHP 8.4, you can simplify it to
public static function tryFromName(string $name): ?static
{
return array_find(self::cases(), fn($case) => $case->name === $name);
}
Upvotes: 0
Reputation: 1367
You can use Reflection:
trait Enum {
public static function tryFromName(string $name): ?static
{
$reflection = new ReflectionEnum(static::class);
if (!$reflection->hasCase($name)) {
return null;
}
/** @var static */
return $reflection->getCase($name)->getValue();
}
}
enum Foo {
use Enum;
case ONE;
case TWO;
}
var_dump( Foo::tryFromName('TWO') ); // enum(Foo::TWO)
var_dump( Foo::tryFromName('THREE') ); // null
Works also for Backed Enums.
Upvotes: 11
Reputation: 4181
I love the use of ReflectionEnum
in @hejdav's answer and it works PHP8.1+, and this would be my preference.
Unfortunately it fails Phpstan tests saying Method EnumClass::tryFromName() should return EnumClass|null but returns UnitEnum|null.
, so I resorted to using the following:
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;
}
}
Upvotes: 7