CHAPTER 5 ■ OBJECT TOOLS
Here, PersonModule and FtpModule both provide empty implementations of the execute() method.
Each class also implements setter methods that do nothing but report that they were invoked. The
system lays down the convention that all setter methods must expect a single argument: either a string
or an object that can be instantiated with a single string argument. The PersonModule::setPerson()
method expects a Person object, so I include a Person class in my example.
To work with PersonModule and FtpModule, the next step is to create a ModuleRunner class. It will use a
multidimensional array indexed by module name to represent configuration information provided in
the XML file. Here’s that code:
class ModuleRunner {
private $configData
= array(
"PersonModule" => array( 'person'=>'bob' ),
"FtpModule" => array( 'host'
=>'example.com',
'user' =>'anon' )
);
private $modules = array();
// ...
}
The ModuleRunner::$configData property contains references to the two Module classes. For each
module element, the code maintains a subarray containing a set of properties. ModuleRunner’s init()
method is responsible for creating the correct Module objects, as shown here:
class ModuleRunner {
// ...
function init() {
$interface = new ReflectionClass('Module');
foreach ( $this->configData as $modulename => $params ) {
$module_class = new ReflectionClass( $modulename );
if (! $module_class->isSubclassOf( $interface ) ) {
throw new Exception( "unknown module type: $modulename" );
}
$module = $module_class->newInstance();
foreach ( $module_class->getMethods() as $method ) {
$this->handleMethod( $module, $method, $params );
// we cover handleMethod() in a future listing!
}
array_push( $this->modules, $module );
}
}
//...
}
$test = new ModuleRunner();
$test->init();
The init() method loops through the ModuleRunner::$configData array, and for each module
element, it attempts to create a ReflectionClass object. An exception is generated when
ReflectionClass’s constructor is invoked with the name of a nonexistent class, so in a real-world
context, I would include more error handling here. I use the ReflectionClass::isSubclassOf() method
to ensure that the module class belongs to the Module type.