MartijnCMT
MartijnCMT

Reputation: 69

Using PHP reflection to build enumeration

I was trying to find a method to handle enumeration in PHP. None of the approaches I found can actually use enumeration to limit the arguments to functions/methods, which is the main reason I want enums.

So I started trying to create my own, but have now run into a problem using reflection:

<?php

class Enum {
    private $_value;
    protected function __construct($value) {
        $this->_value = $value;
    }

    public function getValue() {
        return $this->_value;
    }

    protected static function enumerate($class) {
        $ref = new ReflectionClass($class);
        $instances = array();
        foreach ($ref->getStaticProperties() as $name => $value) {
            $ref->setStaticPropertyValue($name, new $class($value));
        }
    }
}

class Book extends Enum {
    public static $COMIC = 1;
    public static $NOVEL = 2;
    public static $EDUCATIONAL = 3;

    public static function enumerate() {
        parent::enumerate(__CLASS__);
    }
}
Book::enumerate();

function read(Book $book) {
    echo '<hr/>';
    var_dump($book);
}

read(Book::$COMIC);
read(Book::$EDUCATIONAL);

?>

I expected output to show two Book objects with their respective values, instead I got this:

object(Book)#2 (1) { ["_value:private"]=> object(Book)#2 (1) { ["_value:private"]=> *RECURSION* } }
object(Book)#4 (1) { ["_value:private"]=> object(Book)#4 (1) { ["_value:private"]=> *RECURSION* } }

The value (new $class) going into setStaticPropertyValue is correct and there are created only three of these.

getStaticProperties only picks up the static properties (as it should) and the only place the private $_value is assigned is in the constructor. For completeness sake, the constructor is called only three times and inside the constructor, the value is, as expected, only ever 1, 2 or 3. In short, everything seems to be perfectly fine until the calls to setStaticPropertyValue.

I can't figure out what is going on here. Where and why is the private $_value assigned this wrong value? Am I using reflection the wrong way? What is creating these infinite recursive objects?

Upvotes: 1

Views: 1090

Answers (1)

Tim G
Tim G

Reputation: 1822

This is fascinating. I love it when I run across things that push the boundaries of PHP.

I have to say, though, that this seems a bit extreme, and depending on how fast reflection is, it might be fairly slow.

For a framework I wrote, I created a simple enum function. All it's doing is automating defines. I like your approach better because you can type-restrict.

Your code works for me. I'm posting my edits below - they are mainly formatting changes so I could read it more easily.

Check out what I've done to it. I tested this with 5.3.5 and 5.2.17 - works fine on either one.

<?php
header( 'content-type: text/plain' );

Book::enumerate();
Book::$COMIC->read();
read(Book::$EDUCATIONAL);
read(Book::$NOVEL );

class Enum
{
    private $_value;
    protected function __construct($value)
    {
        $this->_value = $value;
    }

    public function getValue()
    {
        return $this->_value;
    }

    protected static function enumerate($class)
    {
        $ref = new ReflectionClass($class);
        $instances = array();

        foreach ( $ref->getStaticProperties() as $name => $value )
        {
            $ref->setStaticPropertyValue( $name, new $class( $value ) );
        }
    }
}

class Book extends Enum
{
    public static $COMIC = 'comic';
    public static $NOVEL = 'novel';
    public static $EDUCATIONAL = 'edu';

    public static function enumerate()
    {
        parent::enumerate(__CLASS__);
    }
    public function read()
    {
        echo "\nReading a {$this->getValue()} book\n";
    }
}

function read( Book $book )
{
    echo "\nReading a {$book->getValue()} book\n";
}

Output

Reading a comic book

Reading a edu book

Reading a novel book

Upvotes: 1

Related Questions