Enums in PHP
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?
Category: PHP,
Comments (6)
Tags for this Article
Comments (6)
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. User: mikey - Date: 2009/08/31 21:45
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 ;) User: martin - Date: 2009/08/31 22:14
new Foo(Foo::FOO) new Foo(Foo::BAR) is ugly, new Foo new Bar is better.
User: optik - Date: 2009/08/31 22:29
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. User: greg - Date: 2009/08/31 23:23
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 :-) User: beberlei - Date: 2009/09/01 00:32
Post A Comment
Back To The Top

