反射存在于每一种面向对象的编程语言中。其主要目的是分析类或对象在运行时的状态,派生或提取类、方法、属性、参数等细节信息,包括注释。反射是一种用于在面向对象范例中操作元模型的API,可用于构建复杂的、可扩展的应用程序。反射在日常的web开发中用处不大,更多的是在底层代码中,比如依赖注入、对象池、动态代理、插件列表的自动获取、文档的自动生成以及一些设计模式等,都会广泛使用反射技术。PHP中的反射API有很多,但常用的一般是ReflectionClass和ReflectionMethod:1.ReflectionClass用于获取类信息,可以简单测试一下:classStudent{private$name;publicfunctionsetName($name){$this->name=$name;}protectedfunctiongetName(){return$this->name;}}获取类的方法列表:$ref=newReflectionClass(Student::class);var_dump($ref->getMethods());返回ReflectionMethod数组:array(1){[0]=>object(ReflectionMethod)#2(2){["name"]=>string(7)"setName"["class"]=>string(7)"Student"}}附上一些常用方法,详见文档:ReflectionClass::getMethods获取方法数组ReflectionClass::getName获取类名ReflectionClass::hasMethod检查方法是否已定义ReflectionClass::hasProperty检查一个属性是否被定义ReflectionClass::isAbstract检查一个类是否是抽象的ReflectionClass::isFinal检查一个类是否被声明finalReflectionClass::isInstantiable检查一个类是否是可实例化的parametersExample2.ReflectionMethod这个主要是针对方法的反射,我们可以简单的执行一下:$stu=newStudent();$ref=newReflectionClass(Student::class);$method=$ref->getMethod('setName');$method->invoke($stu,'john');var_dump($stu->名字);输出:john附上了一些常用的方法。具体可以参考文档:ReflectionMethod::invokeExecuteReflectionMethod::invokeArgs带参数执行ReflectionMethod::isAbstract判断方法是否为抽象方法ReflectionMethod::isConstructor判断方法是否为构造方法ReflectionMethod::isDestructor判断方法是否为析构方法ReflectionMethod::isFinal判断方法是否定义了finalReflectionMethod::isPrivate判断方法是否为私有方法ReflectionMethod::isProtected判断方法是否受保护ReflectionMethod::isPublic判断方法是否为公有methodReflectionMethod::isStatic判断方法是否为静态方法ReflectionMethod::setAccessible设置方法是否可访问接下来,一些反射在实际开发中比较常见。执行私有方法其实反射不仅可以执行私有方法,还可以读取私有属性。这主要用在一些设计不合理的SDK中,一些有用的方法和属性没有对外开放。类学生{私人$名称;私有函数setName($name){$this->name=$name;}}执行私有方法:$stu=newStudent();$ref=newReflectionClass($stu);$method=$ref->getMethod('setName');$method->setAccessible(true);$method->invoke($stu,'john');读取私有属性:$stu=newStudent();$ref=newReflectionClass($stu);$prop=$ref->getProperty('name');$prop->setAccessible(true);$val=$prop->getValue($stu);var_dump($val);动态代理在PHP中其实有一个魔术方法,所以实现动态代理非常简单,但是魔术方法并不完美。个人认为最好的实现应该是JDK中的动态代理。基于一个接口的扫描确实是PHP也可以在.我们先来看看JDK中是如何使用动态代理的:1.首先定义一个实现类的接口。JDK的动态代理必须基于接口(Cglib没有)packagecom.yao.proxy;publicinterfaceHelloworld{voidsayHello();}2。定义一个实现类,即被代理的对象}}3。调用代理对象方法包的实现类com.yao.proxy;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;publicclassMyInvocationHandlerimplementsInvocationHandler{privateObjecttarget;publicMyInvocationHandler(Objecttarget){this.target=target;}publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{System.out.println("Pre-work!");对象obj=method.invoke(target,args);System.out.println("下班后!");返回对象;}4。测试包com.yao.proxy;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Proxy;公开课Demo{publicstaticvoidmain(String[]args){HelloworldImplrealSubject=newHelloworldImpl();MyInvocationHandlerhandler=newMyInvocationHandler(realSubject);ClassLoaderloader=realSubject.getClass().getClassLoader();Class[]interfaces=realSubject.getClass().getInterfaces();HelloworldImplproxySubject=(HelloworldImpl)Proxy.newProxyInstance(loader,interfaces,handler);Stringhello=proxySubject.sayHello();}}JDK的动态代理其实是在底层扫描实现的接口,然后动态生成类的字节码文件PHP是动态语言,所以可以用eval来实现。1.定义调度器接口interfaceInvocationHandler{functioninvoke($method,array$arr_args);}2.动态代理实现定义类的存根:returnnewClass($handler,$target)implements%s{private$handler;私人$目标;公共函数__construct(InvocationHandler$handler,$target){$this->handler=$handler;$this->target=$target;}%s};定义一个方法存根:publicfunction%s(%s){$args=func_get_args();$method=explode("::",__METHOD__);$this->handler->invoke(newReflectionMethod($this->target,$method[1]),$args);}代理实现:finalclassProxy{constCLASS_TEMPLATE=class_stub;//这里显示上面的定义,方便阅读constFUNCTION_TEMPLATE=function_stub;//同上publicstaticfunctionnewProxyInstance($target,array$interfaces,InvocationHandler$handler){}protectedstaticfunctiongenerateClass(array$interfaces){}protectedstaticfunctioncheckInterfaceExists(array$interfaces){}}其中newProxyInstance和generateClass代码:publicstaticfunctionnewProxyInstance($target,array$interfaces,InvocationHandler$handler){self::checkInterfaceExists($interfaces);$code=self::generateClass($interfaces);返回评估($代码);}protectedstaticfunctiongenerateClass(array$interfaces){$interfaceList=implode(',',$interfaces);$函数列表='';foreach($interfacesas$interface){$class=newReflectionClass($interface);$methods=$class->getMethods();foreach($methodsas$method){$parameters=[];foreach($method->getParameters()as$parameter){$parameters[]='$'.$参数->getName();}$functionList.=sprintf(self::FUNCTION_TEMPLATE,$method->getName(),implode(',',$parameters));}}返回sprintf(self::CLASS_TEMPLATE,$interfaceList,$functionList);其中generateClass是通过反射扫描接口方法,然后根据stub模板生成方法拼接成代码,最后通过eval执行2.TestinterfaceTest1{publicfunctiont1();}interfaceTest2{publicfunctiont2();}classTestImplimplementsTest1,Test2{publicfunctiont1(){echo't1';}publicfunctiont2(){echo't2';}}$impl=newTestImpl();classHandlerimplementsInvocationHandler{private$target;公共函数__construct($impl){$this->target=$impl;}functioninvoke(ReflectionMethod$method,array$arr_args){echo'pre-operation';$method->invokeArgs($this->target,$arr_args);echo'手术后';}}$proxy=Proxy::newProxyInstance($impl,['Test1','Test2'],newHandler($impl));$proxy->t1();Output:Pre-operationt1Post-operationDependencyInjection依赖注入是现代框架中很常见的功能,必须和服务容器配合使用。用过Laravel框架的童鞋应该不陌生。我们可以在任何需要服务的地方通过类型提示声明,运行时框架会自动为我们注入需要的对象。Laravel框架源码简单分析:在Laravel框架中,我们可以这样解析一个对象:$obj=App::make(ClassName);make方法底层其实调用了Illuminate\Container\Contaiern::build($concrete)这个方法,整理一下源码是:publicfunctionbuild($concrete){$reflector=newReflectionClass($concrete);$constructor=$reflector->getConstructor();如果(is_null($constructor)){返回新的$concrete;}$dependencies=$constructor->getParameters();$instances=$this->resolveDependencies($dependencies);返回$reflector->newInstanceArgs($instances);}实际代码很简单,反射类获取Construct方法,然后解析依赖参数,传入执行。欢迎关闭我的个人公众号:左手码
