Enum support in PHP






As any PHP developer knows, there is no native enum support in PHP due to the fact that PHP is a dynamic language where a variable can be anything and everything. Many have tried to implement their own version of enum support, some solutions working better than others. Here, I’ll give you my version of enum support. If you like this approach, or if you prefer any other one, is basically a matter of taste.

An abstract enum template class
The basic idea behind the enum support is an abstract enum class with the simple functionality we want the enum to have. It includes type checking where you can check if a variable is of a certain enum type and also the possibility to verify that only allowed enum values are being used.

This is the code for the abstract class:

abstract class Enum {
    protected $value;

    /**
     * Return string representation of this enum
     *
     * @return string
     */
    public function getValue()
    {
        return $this->value;
    }

   /**
     * Tries to set the value  of this enum
     *
     * @param string $value
     * @throws Exception If value is not part of this enum
     */
    public function setValue($value)
    {
        if ($this->isValidEnumValue($value))
            $this->value = $value;
        else
            throw new Exception("Invalid type specified!");
    }

   /**
     * Validates if the type given is part of this enum class
     *
     * @param string $checkValue
     * @return bool
     */
    public function isValidEnumValue($checkValue)
    {
        $reflector = new ReflectionClass(get_class($this));
        foreach ($reflector->getConstants() as $validValue)
        {
            if ($validValue == $checkValue) return true;
        }
        return false;
    }

    /**
     * @param string $value Value for this display type
     */
    function __construct($value)
    {
        $this->setValue($value);
    }

    /**
     * With a magic getter you can get the value from this enum using
     * any variable name as in:
     *
     * <code>
     *   $myEnum = new MyEnum(MyEnum::start);
     *   echo $myEnum->v;
     * </code>
     *
     * @param string $property
     * @return string
     */
    function __get($property)
    {
        return $this->value;
    }

    /**
     * With a magic setter you can set the enum value using any variable
     * name as in:
     *
     * <code>
     *   $myEnum = new MyEnum(MyEnum::Start);
     *   $myEnum->v = MyEnum::End;
     * </code>
     *
     * @param string $property
     * @param string $value
     * @throws Exception Throws exception if an invalid type is used
     */
    function __set($property, $value)
    {
        $this->setValue($value);
    }

    /**
     * If the enum is requested as a string then this function will be automatically
     * called and the value of this enum will be returned as a string.
     *
     * @return string
     */
    function __toString()
    {
        return (string)$this->value;
    }
}

How to create your own enum
To create your own enum using this abstract class simply do in the following way:

class MyEnum extends Enum {
  const TypeA = "1";
  const TypeB = "2";
  const TypeC = "3";
}

The only thing your class should contain is constants with the values of your enum. Why the integers are written as strings is explained further down. It’s not mandatory but it brings some extra possibilities.

To use the new enum in code simply write in the following way:

// Create a new enum with a given value
$myType = new MyEnum(MyEnum::TypeB);

// Change value of enum
$myType->setValue(MyEnum::TypeC);

// Check value of enum
if ($myType == MyEnum::TypeC) {
  // do something
}

// Verify that variable is of enum type
if ($myType instanceof MyEnum) {
  // do something
}

// Force type checking in function definitions
function myFunction(MyEnum $myType)
{
  // do something
}






Notes on the abstract enum class
The code for the abstract enum class is quite straight forward but there are a few things I would like to highlight:

  • The magic __get and __set are not at all needed. They just add an easier way to get and set the value of the enum. Instead of, as in the code above, typing $myType->setValue(MyEnum::TypeC); you can shorten it down to just $myType->v = MyEnum::TypeC;. Your IDE might complain that you’re accessing a dynamically assigned variable, but in our abstract enum class all dynamic variables are redirected to the protected $value variable, and you won’t get any errors when you run it.
  • When you create an instance of the enum you simply create an object. To access the actual value of that enum object you have to use the getValue() function. But if you use string enum values (as illustrated further up) you can use the magic __string() function to access the enum’s value right off from the object, as illustrated here:
    $e = new MyEnum(MyEnum::Start);
    if ($e == MyEnum::TypeA) {
      // Do something
    }

    This only works if you define your enum values as strings as illustrated in the example earlier, and if you try to access the enum as a string. The reason for this is that there is no “magic” way of converting objects to ints (or other datatypes) but you can convert them to strings. If TypeA was an integer PHP would try to convert $e to integer to match, and that would fail.

  • In the isValidEnumValue() method (in the abstract class) you can’t use the __CLASS__ constant because it refers to the abstract class and not to the class extending it. Instead we have to use get_class($this) to get the class name of the class extending the enum.

Limitations using this type of enum
When you use enums in a language having it built in, you also have an “enum understanding” in each and every enum value. That’s not the case here where each value is an string (or int). You therefore have to take a little longer way to set an enum, as illustrated here:

// First way of setting to TypeA
$myType = new MyEnum(MyEnum::TypeA);

// Second way of setting to TypeA
$myType = new MyEnum(MyEnum::TypeC);
$myType->setValue(MyEnum::TypeA);

// This way doesn't work at all though
$myType = MyEnum::TypeA;

If you can live with that, you might have found a decent enum implementation in PHP.

More reading
If you want to read some more about the different PHP techniques used in this solution you can continue here:

And here are some links to other people’s suggestions on enum support in PHP:

Tags: