PHP拥有完备的反射API,提供了对类、接口、函数、方法、扩展进行逆向工程的能力。通过类反射提供的能力,我们可以知道一个类是如何定义的,它有哪些属性,有哪些方法,方法有哪些参数,类文件的路径是什么等重要信息。也正是因为类的反射,很多PHP框架都可以实现依赖注入,自动解决类之间的依赖关系,给我们平时的开发带来了极大的方便。本文主要讲解如何使用类反射实现依赖注入(DependencyInjection),不会一一描述PHPReflection中的各个API。详细的API参考信息请参考官方文档。同样,这里实现的依赖注入非常简单,并不能应用到实际开发中,可以参考我的另一篇文章了解laravel框架的服务容器(IocContainer),这里我会介绍Laravel的服务容器是如何实现依赖注入的。为了更好地理解,让我们通过一个例子来看看类反射以及如何实现依赖注入。下面这个类表示坐标系中的一个点,有两个属性,横坐标x和纵坐标y。/***类点*/类点{public$x;公共$y;/***点构造函数。*@paramint$x点坐标的水平值*@paramint$y点坐标的垂直值*/publicfunction__construct($x=0,$y=0){$this->x=$x;$this->y=$y;}}下一个类表示一个圆,在其构造中可以看出函数中有一个参数是Point类的,即Circle类依赖于Point类。classCircle{/***@varint*/public$radius;//radius/***@varPoint*/public$center;//中心点constPI=3.14;公共函数__construct(Point$point,$radius=1){$this->center=$point;$this->radius=$radius;}//打印点的坐标}//计算圆的面积publicfunctionarea(){return3.14*pow($this->radius,2);}}ReflectionClass接下来,我们使用反射对Circle类进行逆向工程。将Circle类的名称传递给reflectionClass以实例化ReflectionClass类的对象。$reflectionClass=newreflectionClass(Circle::class);//返回值如下object(ReflectionClass)#1(1){["name"]=>string(6)"Circle"}反射常量类$reflectionClass->getConstants();返回常量名和值的关联数组array(1){["PI"]=>float(3.14)}通过反射获取属性$reflectionClass->getProperties();返回一个ReflectionProperty对象Array(2){[0]=>object(ReflectionProperty)#2(2){["name"]=>string(6)"radius"["class"]=>string(6)"Circle"}[1]=>object(ReflectionProperty)#3(2){["name"]=>string(6)"center"["class"]=>string(6)"Circle"}}反映了class$reflectionClass->getMethods();中定义的方法返回ReflectionMethod对象数组array(3){[0]=>object(ReflectionMethod)#2(2){["name"]=>string(11)"__construct"["class"]=>string(6)"Circle"}[1]=>object(ReflectionMethod)#3(2){["name"]=>string(11)"printCenter"["class"]=>string(6)"Circle"}[2]=>对象(反射方法)#4(2){["name"]=>string(4)"area"["class"]=>string(6)"Circle"}}我们也可以通过getConstructor()单独获取类的构造函数,其返回值为一个反射方法对象$constructor=$reflectionClass->getConstructor();反映方法的参数$parameters=$constructor->getParameters();返回值是ReflectionParameter对象的数组。array(2){[0]=>object(ReflectionParameter)#3(1){["name"]=>string(5)"point"}[1]=>object(ReflectionParameter)#4(1){["name"]=>string(6)"radius"}}DependencyInjectionOK接下来我们写一个make函数,把类名传给make函数返回类的对象,它会帮我们注入在makeClassdependencies,也就是本例中,帮我们把Point对象注入到Circle类的构造函数中。//构造类对象functionmake($className){$reflectionClass=newReflectionClass($className);$constructor=$reflectionClass->getConstructor();$parameters=$constructor->getParameters();$dependencies=getDependencies($parameters);return$reflectionClass->newInstanceArgs($dependencies);}//依赖解析函数getDependencies($parameters){$dependencies=[];foreach($parametersas$parameter){$dependency=$parameter->getClass();if(is_null($dependency)){if($parameter->isDefaultValueAvailable()){$dependencies[]=$parameter->getDefaultValue();}else{//不是可选参数,简单直接赋值String0//为构造方法的必要参数//laravel通过服务提供者将闭包注册到IocContainer,//在闭包中,可以return类实例通过returnnewClass($param1,$param2)//然后在make的时候回调这个闭包来解析出对象//$depen具体细节我会在另一篇文章中描述dencies[]='0';}}else{//递归解析依赖类的对象$dependencies[]=make($parameter->getClass()->name);}}return$dependencies;}定义make方法后,我们用它来帮助我们实例化Circle类的对象:$circle=make('Circle');$area=$circle->area();/*var_dump($circle,$area);object(Circle)#6(2){["radius"]=>int(1)["center"]=>object(Point)#11(2){["x"]=>int(0)["y"]=>int(0)}}float(3.14)*/通过上面的例子,我简单描述了如何使用PHP类反射实现依赖注入。Laravel的依赖注入也是通过这个思路实现的,但是设计上更精确的使用闭包回调来处理各种复杂的依赖注入,具体可以参考我的另一篇介绍Laravel服务容器的文章,这里示例代码的下载链接文章
