反射API
这是个大头,而且贼烦人,因为一切都要很智能,所以这里先说一个核心类的目的:
根据配置,对应的类都能自动实例化,并且将类中的方法都执行一遍。
当然,这里对所执行的类也有一定的要求,毕竟只是一个demon,后期也可以进行很多优化,关于配置的类的一些要求就是:
- 所有方法都要是
public
,例子中不会对类方法的可见性进行判断。 - 所有方法都是
set{$Params}
形式的,因为这里只是为了介绍反射API,不是现实场景中使用该类。 - 类中方法支持限定类型,但是不会对类是否可以引用到进行限制,默认都是可以直接调用到的。
下面先来看对应要实例化的类:
// 为了设定执行构造函数时,指定数据类型为对象而设置的待作为参数的类
class Person{
public $name;
public function __construct($name)
{
$this->name=$name;
}
}
// 接口,为了所有实现该接口的类都可以执行 execute 方法
interface Module{
public function execute();
}
// FtpModule类,注意其中的方法名称
class FtpModule implements Module{
public function execute()
{
}
public function setHost($host){
print "Set Host:{$host}\n";
}
public function setUser($user){
print "Set User:{$user}\n";
}
}
// PersonModule类,注意其中一个方法传入的参数是执行了类型的
class PersonModule implements Module{
public function setPerson(Person $person){
print "Set Person:{$person->name}\n";
}
public function execute()
{
}
}
下面就是重头戏了,看反射API如何自动将上面的类都实例化一遍,并且将其中的方法都执行一遍。
class ModuleRunner{
/**
* 配置文件,配置规则:
* 需要实例化的类名=>array(对应的set方法=>执行时所需要传入的参数)
**/
private $configData=array(
"PersonModule"=>array('person'=>"bob"),
'FtpModule'=>array('host'=>"example.com",'user'=>"anon")
);
// 保存传入的Module实现
private $modulea=array();
/**
* 初始化类
* @throws Exception
* @throws ReflectionException
*/
public function init(){
$interface=new ReflectionClass('Module');
foreach ($this->configData as $moduleName=>$params){
$module_class=new ReflectionClass($moduleName);
// 检查对应传入的类是不是module的实现,如果不是则抛出异常
if(!$module_class->isSubclassOf($interface)){
throw new Exception("该对象不是Module的实现:{$moduleName}");
}
// 实例化模块
$module=$module_class->newInstance();
foreach ($module_class->getMethods() as $method){
$this->handleMethod($module,$method,$params);
}
array_push($this->modulea,$module);
}
}
/**
* @param Module $module
* @param ReflectionMethod $method
* @param $params
* @return bool
*/
public 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]));
}
}
}
其中配置文件中有一个重点:"PersonModule"=>array('person'=>"bob"),
,其中上面setPerson
中传入的参数是Person
类的实例,但是实例化Person
类则需要传入一个字符串作为参数,而这里则可以一步到位。其中的重点实现方式其实就是ReflectionClass
的很多相关的类的调用以及遵守我们上面设定的规则。