本例演示了如何利用PHP的反射(Reflection)API来实现插件机制。

一、思路:

1、将所有实现了ILoRuiPlugin接口的类视为插件类

2、在最终的插件运行类中,可定义(通过数组定义)需要运行的插件类和方法


二、知识点:

1、ReflectionClass:反射一个类

2、ReflectionMethod:反射一个方法

3、getMethods():获取某反射类的所有方法

4、getParameters():获取某反射方法的所有参数

5、getName():获取一个反射类/方法的名称

6、implementsInterface():该类是否实现某接口

7、newInstance():创建某一反射类的实例

8、invoke():执行某类的某方法


三、源码:


/**
* 宠物类
* 用于测试插件实例化一个用户自定义类
* @author LoRui (www.LoRui.com)
*/
class Pet {
/**
* 宠物昵称
* @var string
*/
private $name;

/**
* 实例化一个Pet类
* @param string $name
*/
public function __construct($name) {
$this->name = $name;
}

/**
* 获取宠物昵称
* @return string 宠物的昵称
*/
public function getName() {
return $this->name;
}
}

/**
* 插件的接口
* 所有插件必须实现此接口
* @author LoRui (www.LoRui.com)
*
*/
interface ILoRuiPlugin {
function execute();
}

/**
* 宠物插件
* 该插件用于领养宠物,正如你想的那样,该类必须实现ILoRuiPlugin
* @author LoRui (www.LoRui.com)
*
*/
class PetPlugin implements ILoRuiPlugin {
/**
* 设置宠物信息
* @param Pet $pet
*/
public function setPet(Pet $pet) {
echo "PetPlugin::setPet(): {$pet->getName()}
";
}

/**
* 实现接口方法——仅此而已
* @see ILoRuiPlugin::execute()
*/
public function execute() {}
}

/**
* 即时通讯插件
* 该插件用于站内即时通讯,是的,它也要实现ILoRuiPlugin接口
* @author LoRui (www.LoRui.com)
*
*/
class IMPlugin implements ILoRuiPlugin {
/**
* 设置用户
* @param string $user
*/
public function setUser($user) {
echo "IMPlugin::setUser(): $user
";
}
/**
* 发送消息
* @param string $msg
*/
public function sendMessage($msg) {
echo "IMPlugin::sendMessage(): $msg
";
}
/**
* 添加好友
* @return string
* @note 本示例中,将使该方法不运行
*/
public function addFriend($user) {
echo "IMPlugin::addFriend(): $user
";
}
/**
* 实现接口方法
* @see ILoRuiPlugin::execute()
*/
public function execute() {}
}

/**
* 运行插件
* 该类用于运行指定的插件
* @author LoRui (www.LoRui.com)
*
*/
class LoRuiPluginRunner {
/**
* 配置信息
* 该数组用于配置使用哪些插件及其需要运行的方法
* @var array
*/
private $config = array (
'PetPlugin' => array('setPet' => '阿咪'),
'IMPlugin' => array(
'setUser' => 'LoRui',
'sendMessage' => '龙睿欢迎您',
#'addFriend' => 'Jane', #本示例不运行该方法
),
);
/**
* 运行插件
*/
public function run() {
foreach ($this->config as $plugin => $params) {
$reflectionClass = new ReflectionClass($plugin); #使用配置信息新建插件类
#如果不是插件类(即,没有实现ILoRuiPlugin接口)则跳到下一循环
if(!$reflectionClass->implementsInterface('ILoRuiPlugin')) continue;
#列出该插件类所有的方法
foreach ($reflectionClass->getMethods() as $method) {
#调用方法句柄
$this->hanldeMethod($reflectionClass->newInstance(), $method, $params);
}
}
}
/**
* 方法句柄
* 处理插件中对应的方法
* @param ILoRuiPlugin $plugin
* @param ReflectionMethod $method
* @param array $params
*/
private function hanldeMethod(ILoRuiPlugin $plugin, ReflectionMethod $method, array $params) {
$name = $method->getName(); #获取方法名称
$args = $method->getParameters(); #获取该方法的参数

if(count($args[0]) != 1) return false; #只接受1个参数的方法
if(!isset($params[$name])) return false; #只处理指定的方法($config数组)

$class = $args[0]->getClass(); #如果非简单数据类型,返回非空值
if(empty($class)) { #空值 ,表示该参数为简单数据类型
$method->invoke($plugin, $params[$name]);
} else { #非空值,表示该参数是一个对象
$method->invoke($plugin, $class->newInstance($params[$name]));
}
}
}

$runner = new LoRuiPluginRunner();
$runner->run();

四、本例运行结果:

PetPlugin::setPet(): 阿咪

IMPlugin::setUser(): LoRui

IMPlugin::sendMessage(): 龙睿欢迎您

上一篇 下一篇