Search code examples
phpenumsphp-8.1

PHP: Select enum case from string value of case name


I'm looking at enum in PHP 8.1. I have a string variable gathered from a database record, and I want to select the correct enum value case based on that string. For example:

enum Test: string {
    case FOO = 'baz';
    case BAR = 'buz';
}

I can select the first of those using the string value of the case:

$x = 'baz';
$y = Test::from($x);

// $y is now `Test::FOO`.

But what about the other way around? What if $x = "FOO"? How do I select the enum case from "FOO"?

Hard-coded, it would be Test::FOO, but I don't know the syntax for using a variable. I've tried a few things like Test::{$x} but the enum syntax doesn't seem to like variables very much.

The standard example all the tutorials give seems a perfect use case for this usage, but none of them mention it.

enum Suit: string {
    case Clubs = '♣';
    case Diamonds = '♦';
    case Hearts = '♥';
    case Spades = '♠';
}

I can totally imagine needing to get from "Clubs". I can't imagine ever needing the reverse.


Solution

  • To me, storing the name of the constant and not the value defeats the purpose of a backed enum. But, reading through many of the discussions on enums, many people were against backed enums and/or stringified enums in general, so I won't argue the point except to say that I would consider it the same as storing Clubs for a class constant such as:

    class Suit
    {
        const Clubs = '♣';
        const Diamonds = '♦';
        const Hearts = '♥';
        const Spades = '♠'; 
    }
    

    To your question, yes you can get the enum back by using the constant function (as noted in this thread):

    enum Suit: string {
        case Clubs = '♣';
        case Diamonds = '♦';
        case Hearts = '♥';
        case Spades = '♠';
    }
    
    $cardString = 'Clubs';
    $cardEnumFQString = Suit::class . '::' . $cardString;
    $cardEnum = constant($cardEnumFQString);
    
    echo $cardEnum->value;
    

    Demo here: https://3v4l.org/WTrIv#v8.1.10

    Personally, when I use backed enums it is for database, URL transport or similar, and I use the value. If I want to expose anything user-facing I more often than not just add a custom attribute and I have a common utility function that parses that.

    EDIT Here's some helper code that shows a trait I use to reflect on enum attributes: https://3v4l.org/QKBvU#v8.1.10. It might seem like overkill for some people, but for some objects I like to keep their meta information attached to the object instead of a separate render file.

    EDIT: Enums vs Class Constants

    This is not a definitive answer to the question in any way, I want to make that clear. There is definitely a lot more that I'm not covering and I'd encourage a read-through on the original discussion as well as the follow-up discussion and the over-arching ADTs RFC. There's a lot in those discussions, pros, cons, WTFs, alternatives, "I don't get it", etc.

    One of the major differences between an enum and a class constant is that an enum is a first-class object in PHP and as such it can have methods, whereas a class constant is limited to scalars, scalar expressions and arrays (ignoring recent post-enum changes). This means you can pass an object that holds a specific value around, and have that object include methods. The PHP documentation has a great example for the color method on Suit.

    You could define your constants in one file and include a utility class with methods like getColor(string $colorName) however there's no way to limit the choices that get passed to it, nor can you guard against someone using a literal instead of your constant.

    Can you do that with pure classes? Absolutely, and many of us used libraries such as https://github.com/myclabs/php-enum or https://github.com/spatie/enum to do that. This just unifies it at the language level.

    I definitely agree that it can be a grey area. I came from a .Net background originally so I've had enums for a very long time, and even wrote a "why enums vs static class members" over a decade ago, so for me I've had a long time to play with them, and was very excited when they came to PHP. The best I can tell you is that you might just have to play with them to see if they fit. I'm very certain that some long-time PHP people won't use enums ever because they've solved this problem in the past in other ways, and I think that's okay, too.