5/16/2008

PHP Reflections In Time

Reflection in PHP is a little clunky and I say that with the greatest of compassion.

First of all we have the standard function interface to all that is good, call_user_func, call_user_method, func_get_args, get_defined_functions and so on.

So you can imagine a call_user_func to start with and then when classes came in they added call_user_method that takes the instance as well (surmising the history, I don't have any facts).

   class Fred
{
public function someMethod()
{
echo "Hello World\n";
}
}

$fred = new Fred();
$fred->someMethod();

// OR

call_user_method("someMethod", $fred);


However, this method is deprecated, in preference to old call_user_function with different parameters.

   call_user_func(array($fred, "someMethod"));


Then there are the new class oriented evolution with ReflectionClass, ReflectionMethod, ReflectionProperty and so on.

   $method = new ReflectionMethod("Fred", "someMethod");
$method->invoke($fred);


It doesn't seem to bad but there are new problems. This fails because "someMethod" is private, even though the call is from the same class. There doesn't appear to be a way around this. You can not call private methods with the new Reflection classes.

   class Fred
{
private function someMethod()
{
echo "Hello World\n";
}

public function testInvoke()
{
$method = new ReflectionMethod(__CLASS__, "someMethod");
$method->invoke($this);
}
}

$fred = new Fred();
$fred->testInvoke();


This produces this error message:

Fatal error: Uncaught exception 'ReflectionException' with message 'Trying to invoke private method Fred::someMethod() from scope ReflectionMethod'


However, you can switch back to the old way of doing it like this:

    class Fred
{
private function someMethod()
{
echo "Hello World\n";
}

public function testInvoke()
{
call_user_func(array($this, "someMethod"));
}
}

$fred = new Fred();
$fred->testInvoke();


The same situation exists with the two forms of property access:

$prop = new ReflectionProperty("Fred", "property");
$prop->getValue($fred);

and

    $fred->$propertyName


This mix of old an new techniques is how PHP is today and there is not much we can do but to wrap the variations in our own classes so that we can stay as portable as possible across future versions.

No comments: