反射介绍参考官方介绍的话,PHP5拥有完整的反射API,加入了对类、接口、函数进行逆向工程的能力,方法和扩展。此外,ReflectionAPI提供了从函数、类和方法中提取文档注释的方法。YII2框架中的示例。对于yii2框架,大家应该都知道di容器。di容器的源码这里主要讲解Container类。首先看看平时如何使用di,使用yii2框架中注释的示例代码来展示;容器调用示例命名空间app\models;useyii\base\BaseObject;useyii\db\Connection;useyii\di\Container;interfaceUserFinderInterface{functionfindUser();}classUserFinderextendsBaseObjectimplementsUserFinderInterface{public$db;公共函数__construct(Connection$db,$config=[]){$this->db=$db;父母::__构造($配置);}publicfunctionfindUser(){}}classUserListerextendsBaseObject{public$finder;公共函数__construct(UserFinderInterface$finder,$config=[]){$this->finder=$finder;父母::__构造($配置);}}$容器=新容器;$container->set('yii\db\Connection',['dsn'=>'...',]);$container->set('app\models\UserFinderInterface',['class'=>'app\models\UserFinder',]);$container->set('userLis之三','应用程序\模型\UserLister');$lister=$container->get('userLister');//上面的操作等价于下面的实现$db=new\yii\db\Connection(['dsn'=>'...']);$finder=newUserFinder($db);$lister=newUserLister($finder);上面的示例代码只是实例化了Container类,然后调用了set方法注入其他对象,最后得到了dependencies和其他对象创建的lister对象,由于只调用了set方法和get方法,下面看容器从最常调用的集合开始编码设置方法publicfunctionset($class,$definition=[],array$params=[]){$this->_definitions[$class]=$this->normalizeDefinition($class,$definition);$this->_params[$class]=$params;取消设置($this->_singletons[$class]);return$this;}上面的代码比较简洁,调用了类的normalizeDefinition方法。_definitions数组中出现的三个属性的含义,保存依赖定义_params数组,保存构造函数的参数_singletons,保存单例再看normalizeDefinition方法。该方法的主要作用是规范类定义protectedfunctionnormalizeDefinition($class,$definition){if(empty($definition)){//为空return['class'=>$class];}elseif(is_string($definition)){//是一个字符串return['class'=>$definition];}elseif(is_callable($definition,true)||is_object($definition)){//检查它是可调用函数还是对象return$definition;}elseif(is_array($definition)){//检查是否是数组if(!isset($definition['class'])){if(strpos($class,'\\')!==false){$定义['类']=$c姑娘;}else{thrownewInvalidConfigException('类定义需要一个“类”成员。');}}返回$定义;}thrownewInvalidConfigException("Unsupporteddefinitiontypefor\"$class\":".gettype($definition));}上面代码中做了一些判断注释。不难发现,最后需要返回的定义变量需要是数组形式,或者可以调用一个函数和一个对象。注意回到最开始的调用示例代码,定义变量分别是没有classkey的数组格式,有classkey的数组格式,string类型的set方法调用已经完成。从源码分析,基本没有反射的影子,就是对一些传入的参数格式进行了兼容处理,然后再写。类属性,再看示例代码中的get方法。get方法publicfunctionget($class,$params=[],$config=[]){if(isset($this->_singletons[$class])){//直接返回示例return$this->_singletons[$类];}elseif(!isset($this->_definitions[$class])){//调用bulidreturn$this->build($class,$params,$config);}$definition=$this->_definitions[$class];if(is_callable($definition,true)){//可调用函数$params=$this->resolveDependencies($this->mergeParams($class,$params));$object=call_user_func($definition,$this,$params,$config);}elseif(is_array($definition)){//数组$concrete=$definition['class'];取消设置($定义['类']);$config=array_merge($definition,$config);$params=$this->mergeParams($class,$params);如果($concrete===$class){$object=$this->build($class,$params,$config);}else{$object=$this->get($混凝土,$参数,$配置);}}elseif(is_object($definition)){//对象直接存放在单例属性集合中return$this->_singletons[$class]=$definition;}else{thrownewInvalidConfigException('意外的对象定义类型:'.gettype($definition));}if(array_key_exists($class,$this->_singletons)){//单例$this->_singletons[$class]=$object;}return$object;}以上代码简单划分,大家浏览一下,后面会继续介绍。首先说明一下属性_definitions集合中不存在的情况,即调用build。这个后面会解释,再看看有没有。对于相关的类键,下面将处理几种情况。可调用函数的情况下,调用resolveDependencies方法,call_user_func调用函数数组的情况下,将得到的值与class进行比较,相等则调用build方法。调用get方法使用对象的值直接存储在_singletons属性集合中,直接返回对象。这将不再重复。下面简单分析一下上述调用方法,bulid方法和resolveDependencies方法的调用逻辑。先看build方法调用的源码protectedfunctionbuild($class,$params,$config){//声明变量存放getDependencies方法返回的数组list($reflection,$dependencies)=$this->getDependencies($class);//合并params数组的数据并覆盖到变量$dependenciesf中oreach($paramsas$index=>$param){$dependencies[$index]=$param;}//调用resolveDependencies方法$dependencies=$this->resolveDependencies($dependencies,$reflection);//调用反射类方法检测类是否可以被实例化if(!$reflection->isInstantiable()){thrownewNotInstantiableException($reflection->name);}if(empty($config)){//创建类的新实例,变量$dependencies将作为参数传递给类的构造函数返回$reflection->newInstanceArgs($dependencies);$config=$this->resolveDependencies($config);//如果变量$dependencies为空且类是yii\base\Configurable接口的实现if(!empty($dependencies)&&$reflection->implementsInterface('yii\base\Configurable')){//将$config设置为最后一个参数(现有参数将被覆盖)$dependencies[count($dependencies)-1]=$config;返回$reflection->newInstanceArgs($dependencies);}//创建一个对象,注入参数$object=$reflection->newInstanceArgs($dependencies);//对象属性赋值foreach($configas$name=>$value){$object->$name=$value;}return$object;}看了上面的源码,也基本明白这个方法就是返回实例化的对象,调用反射的一些接口函数。到这里我们基本可以知道反射的一些Function,第一个是检测类的合法性,比如检测是否是接口实现,是否可以实例化,另一个是创建。从上面可以看出,类的实例是根据反射创建的,注入了构造函数依赖的参数。我们看一下这个方法中调用的两个依赖方法,就是一开始的变量声明getDependencies和resolveDependencies来处理变量。getDependencies方法调用protectedfunctiongetDependencies($class){//检查反射是否已经存在if(isset($this->_reflections[$class])){return[$this->_reflections[$class],$this->_dependencies[$class]];$dependencies=[];$reflection=newReflectionClass($class);//反映对应类的信息$constructor=$reflection->getConstructor();//获取类的构造函数if($constructor!==null){//如果构造函数不为空,则获取构造函数中的参数进行循环处理foreach($constructor->getParameters()as$param){if(version_compare(PHP_VERSION,'5.6.0','>=')&&$param->isVariadic()){//检测php版本和构造参数,检测是否为可变参数break;}elseif($param->isDefaultValueAvailable()){//检测参数是否有默认值,如果有数据保存默认值$dependencies[]=$param->getDefaultValue();}else{//获取参数的类型提示,检查是否为null,返回reflectClass对象//这里再举个例子,比如构造函数是这样的__construct(Db$db);这里返回的是Db类的反射$c=$param->getClass();//创建Instance实例存储类名$dependencies[]=Instance::of($c===null?null:$c->getName());}}}//存储$this->_reflections[$class]=$reflection;$this->_dependencies[$class]=$dependencies;return[$reflection,$dependencies];}这个方法主要用来解析依赖信息,主要是获取类的构造函数的信息,以便调用构造函数创建实例resilveDependencies方法调用这个方法主要是为了实例化依赖,也就是创建构造函数的参数对象,这里就不赘述了。protectedfunctionresolveDependencies($dependencies,$reflection=null){foreach($dependenciesas$index=>$dependency){//在解析依赖信息的getDependencies中,有些参数没有默认值,而是创建Instance对象//这里,这些Instance对象会被实例化为真正的构造函数的参数对象($dependency->id);}elseif($reflection!==null){$name=$reflection->getConstructor()->getParameters()[$index]->getName();$class=$reflection->getName();thrownewInvalidConfigException("在实例化\"$class\"时缺少必需的参数\"$name\"。");}}}return$dependencies;}总结在上面的源码中,基本上可以看到反射的几种应用,但是反射到底是什么,它的作用是什么?想必看了上面的内容,会有一点理解。嗯,其实意思就和名字一样,就是反射类的信息。它的作用是获取类的信息,php的反射类也提供了很多接口函数供使用。使用时可以查看官网说明书。从上面也可以看出yii2框架中的di容器创建对象。这里还是希望在开头描述一下示例代码。它首先将数据库连接类、查找器类、侦听器类和查找器类构造函数注入到容器中。根据数据库连接类,侦听器类取决于查找器类。从获取依赖信息的方法可以知道,在构造中会获取依赖对象信息,然后调用parse依赖信息再次调用get方法返回实例化对象,实现注入关系。个人博客地址
