PHP Objects, Patterns and Practice (3rd edition)

(Barry) #1

CHAPTER 5 ■ OBJECT TOOLS


Before you can invoke the execute() method of each Module, an instance has to be created. That’s
the purpose of method::ReflectionClass::newInstance(). That method accepts any number of
arguments, which it passes on to the relevant class’s constructor method. If all’s well, it returns an
instance of the class (for production code, be sure to code defensively: check that the constructor
method for each Module object doesn’t require arguments before creating an instance).
ReflectionClass::getMethods() returns an array of all ReflectionMethod objects available for the
class. For each element in the array, the code invokes the ModuleRunner::handleMethod() method; passes
it a Module instance, the ReflectionMethod object, and an array of properties to associate with the Module.
handleMethod() verifies; and invokes the Module object’s setter methods.


class ModuleRunner {
// ...
function handleMethod( Module $module, ReflectionMethod $method, $params ) {
$name = $method->getName();
$args = $method->getParameters();


if ( count( $args ) != 1 ||
substr( $name, 0, 3 ) != "set" ) {
return false;
}


$property = strtolower( substr( $name, 3 ));
if (! isset( $params[$property] ) ) {
return false;
}


$arg_class = $args[0]->getClass();
if ( empty( $arg_class ) ) {
$method->invoke( $module, $params[$property] );
} else {
$method->invoke( $module,
$arg_class->newInstance( $params[$property] ) );
}
}
}


handleMethod() first checks that the method is a valid setter. In the code, a valid setter method must
be named setXXXX() and must declare one and only one argument.
Assuming that the argument checks out, the code then extracts a property name from the method
name by removing set from the beginning of the method name and converting the resulting substring to
lowercase characters. That string is used to test the $params array argument. This array contains the
user-supplied properties that are to be associated with the Module object. If the $params array doesn’t
contain the property, the code gives up and returns false.
If the property name extracted from the module method matches an element in the $params array, I
can go ahead and invoke the correct setter method. To do that, the code must check the type of the first
(and only) required argument of the setter method. The ReflectionParameter::getClass() method
provides this information. If the method returns an empty value, the setter expects a primitive of some
kind; otherwise, it expects an object.
To call the setter method, I need a new Reflection API method. ReflectionMethod::invoke()
requires an object and any number of method arguments to pass on to the method it represents.
ReflectionMethod::invoke() throws an exception if the provided object does not match its method. I
call this method in one of two ways. If the setter method doesn’t require an object argument, I call
ReflectionMethod::invoke() with the user-supplied property string. If the method requires an object, I
use the property string to instantiate an object of the correct type, which is then passed to the setter.

Free download pdf