反射API

反射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的很多相关的类的调用以及遵守我们上面设定的规则。

猜你喜欢

转载自blog.csdn.net/YQXLLWY/article/details/82945493