How to check if non-backed enum contains case?
Asked Answered
P

4

9

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()
Popelka answered 9/1, 2022 at 20:25 Comment(0)
L
10

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

Laporte answered 9/1, 2022 at 21:8 Comment(0)
A
2
$fruitFromPost = current(array_filter(
    Fruit::cases(),
    fn(Fruit $fruitCase) => $fruitCase->name === $_POST['fruit']
)) ?: Fruit::APPLE;

We filter Fruit::cases with anonymous call on each case, where we check, if case name is same, as provided in POST. Then we get current value from filtered, and if it's false (not found in cases by name) we assign APPLE as default (or you can stay with false or maybe null, as you wish). Please note, that it's case sensitive.

Amoy answered 7/7, 2022 at 18:3 Comment(0)
B
1

just use :

echo defined("\App\Enums\OrderStatus::CREATED") ? 'Case exists' : 'Case does not exist';

Bores answered 26/12, 2023 at 16:3 Comment(0)
F
1

You could also write a method fromName in your Fruit Enum like this:

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

  public static function fromName($name)
  {
    return defined("self::$name") ? constant("self::$name") : false;
  }  
}

Then you can simply check like this:

$fruit = Fruit::fromName($_POST['fruit']);
if ($fruit) {
  eatFruit($fruit);
  ...
}
Freehanded answered 9/8 at 16:26 Comment(2)
this is backed enumPopelka
Thank you @norr. I've corrected my answer.Freehanded

© 2022 - 2024 — McMap. All rights reserved.