假設我們創建一個類要動態調用Module對象,即該類可以自動加載第三方插件並集成到已有的系統,而不需要把第三方插件硬編碼進原有的代碼。
要達到這個目的,可以在Module接口或抽象類中定義一個execute()方法,強制要求所有子類都要實現這個方法,可以允許用戶在外部XML文件中列出所有的Module類,系統可以用XML提供的信息加載一定數目的Module對象,然後每個Module對象調用execute()。XML文件可以爲每個Module實現屬性和值,Module的創建者可以爲每個屬性名提供setter方法。
interface Module{
function execute();
}
class FtpModule implements Module{
function setHost($host){
print "FtpModule::setHost(): $host<br>";
}
function setUser($user){
print "FtpModule::setUser(): $user<br>";
}
function execute(){
}
}
class PresonModule implements Module{
function setPerson(Person $person){
print "PresonModule::setPerson(): $person->name<br>";
}
function execute(){
}
}
class ModuleRunner{
private $configData = array(
"PresonModule" => array('person'=>'bob'),
"FtpModule" => array(
'host' => 'example.com',
'user' => 'anon',
),
);
private $modules = array();
function init(){
$interface = new ReflectionClass('Module');
foreach ($this->configData as $modulename => $params) {
$medule_class = new ReflectionClass($modulename);
if(!$medule_class->isSubclassOf($interface)){
throw new Exception("unknown medule type: $medulename");
}
$medule = $medule_class->newInstance();
foreach ($medule_class->getMethods() as $method) {
$this->handleMethod($medule, $method, $params);
array_push($this->modules, $method);
}
}
}
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]));
}
}
}
$test = new ModuleRunner();
$test->init();