Enums in PHP

A colleague of mine complained about the missing support for Enums in PHP and how one often wants to have a variable that is constant and has one of several specific values. We came up with an elegant solution that we want to share, maybe its even helpful to you.

If you want to implement Enum behaviour with a simple string or int value you end up with having to validate the values at several different locations in the code rather than being able to strictly enforce the Enum structure by using typehints.

We discussed several approaches to this problem that all seemed a bit ugly in one or another way. For example the SplEnum class is a step in the right direction, however you can only use a type hint for "SplEnum" or add another inheritance layer. Also you have to implement different classes for each enum type, which requires you to implement a factory method for your enum to help with creation of the different types.

We came up with a very simple Enum concept. It uses reflection in the constructor to check if the given value is a valid Enum value by checking it against all the defined constants of the implementing class and throws an Exception if it is not. The __toString() magic method is implemented to allow for simple checks of the enums value. Strict type-checks are not possible with this construct, however in our opinion it is a very elegant solution to enforce a limited set of specific values throughout your code-base.

Here is the code plus a small example:

abstract class MyEnum
{
    final public function __construct($value)
    {
        $c = new ReflectionClass($this);
        if(!in_array($value, $c->getConstants())) {
            throw IllegalArgumentException();
        }
        $this->value = $value;
    }
 
    final public function __toString()
    {
        return $this->value;
    }
}
 
class Foo extends MyEnum
{
    const FOO = "foo";
    const BAR = "bar";
}
 
$a = new Foo(Foo::FOO);
$b = new Foo(Foo::BAR);
$c = new Foo(Foo::BAR);
 
if($a == Foo::FOO) {
    echo "My value is Foo::FOO\n";
} else {
    echo "I dont match!\n";
}
 
if($a == $b) {
    echo "a value equals b value!\n";
}
if($b == $c) {
    echo "b value equals c value!\n";
}

Now you could nice things such as:

function doStuff(Foo $foo) {
    switch($foo) {
        case Foo::FOO:
            echo "do more here!\n";
            break;
        case Foo::BAR;
            echo "evil stop!\n";
            break;
    }
}
 
doStuff($a);

What are your thoughts?

Share This Post

  • Share on Twitter
  • Facebook
  • Share on deli.cio.us
  • Share on Digg
  • Share on reddit
  • Share on StumbleUpon

Comments


evert
Aug, 31. 2009

As an alternative, I like to use integers and a simple set of constants. This is not a strict solution such as yours though..



mikey
Aug, 31. 2009

Interesting approach, but as your last example shows it does not offer what enums are really good at: replacing switch/case blocks. That's due to the problem you can not define methods depending on the enum, the best approach with your implementation would be to move the switch/case block into the enum class (Foo in this example). However, it depends on what you want to achieve with - sometimes just moving the switch/case block is sufficient. :-)

For another approach take a look at the Stubbles (http://stubbles.net/wiki/Docs/Enums, http://stubbles.net/browser/framework/trunk/src/main/php/net/stubbles/lang/stubEnum.php) or XP framework (http://docs.xp-framework.net/xml/doc?core/enum) implementations. When the frameworks will move to PHP 5.3 both implementations will improve significantly using Late Static Binding and Closures.



martin
Aug, 31. 2009

The ReflectionClass is a bit overkill for such a simple task. I would prefare the following:
if(!defined(get_class($this).'::'.strtoupper($value)) {
throw IllegalArgumentException();
}

I think, that's a bit faster, but i didn't measure it. Just a suggestion ;)



optik
Aug, 31. 2009

possible, but this one - http://www.jeremyjohnstone.com/blog/2008-10-05-enums-in-php.html seems better and has nice __callstatic usage

new Foo(Foo::FOO) new Foo(Foo::BAR) is ugly, new Foo new Bar is better.



greg
Aug, 31. 2009

for all scalar values, this is serious overkill.

abstract class Enum
{
protected $valueSet = array();
protected $value;

function __construct($value)
{
if (!in_array($value, $this-valueSet)) {
settype($value, 'string');
throw new UnexpectedValueException('Invalid value ' . $value .
' must be one of ' . implode(', ', $this-valueSet));
}
$this-value = $value;
}

function __toString()
{
return $this-value;
}
}

class FooEnum extends Enum
{
protected $valueSet = array('foo', 'bar');
}

$a = new FooEnum('foo');

etc.

Also, using __toString() seems elegant at first, but you're better off treating the thing as an object and using some helper method to retrieve the value.



beberlei
Sep, 01. 2009

thanks for all the replies and ideas.

Since I will only make moderate use of this class, the lost request time due to the reflection overhead will probably be unmeasurable. Optimizations are easily done also as some of you described.

@greg: thats probably much faster and less overkill, however the use of constants is quite nice for comparisons. One could extend your example by defining the constants additionally and putting them into the array.

@martin: I know its probably overkill to use reflection, you could cache the result after the first run, or use your approach.

@optik: that solution looks very elegant, however we needed that implementation for PHP 5.2 :-)

@mikey: My intension was not to get rid of the switch, but to have more certainty on the value of a variable which has a finite set of states and which is worked with all over the code :-)



air jordan shoes
Apr, 09. 2010

Cool, i am impressed with the views there, it is really a good idea to visit UK to enjoy our holiday!
By Air Jordan shoes



air jordan shoes
Apr, 09. 2010

Cool, i am impressed with the views there, it is really a good idea to visit UK to enjoy our holiday!
http://www.airjordans.cc/



air jordan shoes
Apr, 09. 2010

Cool, i am impressed with the views there, it is really a good idea to visit UK to enjoy our holiday!
http://www.airjordans.cc/


Write a Comment