phpenumsphp-8.1

How to check if non-backed enum contains case?


I have basic enum

enum Fruit
{
  case APPLE;
  case ORANGE;
  case BANANA;
}

and some function that uses typing with that enum:

function eatFruit (Fruit $fruit)
{
  // do stuff
}

and variable with unknown content

$fruit = $_POST['fruit']; // user choosed "MILK"
if (?????) { // how to check if it's fruit?
  eatFruit($fruit); // this should not be executed
}

I cannot find in documentation simple way to check if enum contains specific case.

It is possible with backed enums like that

enum Fruit
{
  case APPLE = 'APPLE';
  case ORANGE = 'ORANGE';
  case BANANA = 'BANANA';
}

Fruit::from('');
Fruit::tryFrom('');

This will work, but from does not exist on non-backed enums form my first example.

Fatal error: Uncaught Error: Call to undefined method Fruit::from()

Solution

  • You can use the static method cases() for this. This returns an array of all values in the enum. The values have a "name" property that is a string representation you can check against (backed enums also have a "value" property that contains the string value you defined in the enum).

    So an example implementation could be something like:

    enum Fruit {
        case APPLE;
        case ORANGE;
        case BANANA;
    }
    
    // String from user input
    $fruit = $_POST['fruit'];
    
    // Find matching fruit in all enum cases
    $fruits = Fruit::cases();
    $matchingFruitIndex = array_search($fruit, array_column($fruits, "name"));
    
    // If found, eat it
    if ($matchingFruitIndex !== false) {
        $matchingFruit = $fruits[$matchingFruitIndex];
        eatFruit($matchingFruit);
    } else {
        echo $fruit . " is not a valid Fruit";
    }
    
    function eatFruit(Fruit $fruit): void {
        if ($fruit === Fruit::APPLE) {
            echo "An apple a day keeps the doctor away";
        } elseif ($fruit === Fruit::ORANGE) {
            echo "When life gives you oranges, make orange juice";
        } elseif ($fruit === Fruit::BANANA) {
            echo "Banana for scale";
        }
    }
    

    Working version with sample data: https://3v4l.org/ObD3s

    If you want to do this more often with different enums, you could write a helper function for this:

    function getEnumValue($value, $enumClass) {
        $cases = $enumClass::cases();
        $index = array_search($value, array_column($cases, "name"));
        if ($index !== false) {
            return $cases[$index];
        }
        
        return null;
    }
    
    $fruit = getEnumValue($_POST['fruit'], Fruit::class);
    if ($fruit !== null) {
        eatFruit($fruit);
    } else {
        echo $_POST['fruit'] . " is not a valid Fruit";
    }
    

    Example with the same sample data: https://3v4l.org/bL8Wa