Reputation: 69
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
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