当前位置: 首页 > 后端技术 > PHP

ThinkPHP5.1框架解析(三):容器与依赖注入

时间:2023-03-29 19:42:01 PHP

上一篇我们讲了ThinkPHP是如何实现自动加载的。想看的可以看ThinkPHP5.1源码解析(二)自动加载机制在读这篇文章之前,希望你已经掌握了IOC、DI、Facade的基础知识。不懂的请先看几篇文章。深入了解控制反转(IoC)和依赖注入(DI)然后进入正题。服务根据分析框架调用入口脚本文件index.php//加载基础文件require__DIR__。'/../thinkphp/base.php';//支持使用静态方法预先设置Request对象和Config对象//执行应用和ResponseContainer::get('app')->run()->发送();上面base.php的作用是加载自动加载机制,处理异常,开启日志功能。//执行应用并响应Container::get('app')->run()->send();这里是IOC容器功能的使用,将app容器1放入Container后,我们将引入其第一个Class属性protectedstatic$instance;//定义我们的容器类实例,采用单例模式,只实例化一次protected$instances=[];//容器中的对象实例受保护$bind=[];//containerbindingDefinedlogoprotected$name=[];//容器标识别名$instances实现注册树模式2,存储值数组('think\\App'=>Appinstance,'think\\Env'=>Envinstance,'think\\Config'=>Configinstance,...)bind会加载一堆初始数据,并在初始化时记录一堆类名和类名的映射关系。protected$bind=['app'=>'think\\App','build'=>'think\\Build','cache'=>'think\\Cache','config'=>'think\\config','cookie'=>'think\\Cookie',...]name和bind属性记录值很相似,都是类名和类名的映射关系。不同的是name记录的是已经实例化的映射关系。进入get方法publicstaticfunctionget($abstract,$vars=[],$newInstance=false){returnstatic::getInstance()->make($abstract,$vars,$newInstance);}这段代码没什么好说的,就是先获取当前容器的实例(单例),然后实例化。进入make方法publicfunctionmake($abstract,$vars=[],$newInstance=false){if(true===$vars){//总是创建一个新的实例化对象$newInstance=true;$变量=[];}//如果该类已经存在并被实例化,使用别名获取他的类$abstract=isset($this->name[$abstract])?$this->名称[$抽象]:$抽象;//如果已经实例化过,不需要每次都创建新的实例,直接返回注册树上的实例if(isset($this->instances[$abstract])&&!$newInstance){返回$this->instances[$abstract];}//如果我们绑定了这个类,例如'app'=>'think\\App',if(isset($this->bind[$abstract])){$concrete=$this->bind[$抽象的];//因为ThinkPHP实现可以绑定闭包或者匿名函数进入,所以这里是对闭包的处理}else{//记录映射关系,根据类名实例化,如think\\App$this->name[$abstract]=$concrete;返回$this->make($concrete,$vars,$newInstance);}}别的{//根据类名调用类$object=$this->invokeClass($abstract,$vars);}if(!$newInstance){$this->instances[$abstract]=$object;}//返回production出来的类return$object;}我们拆分一下,if(true===$vars){//总是创建一个新的实例化对象$newInstance=true;$vars=[];}这段代码是让我们的函数使用make($abstract,true)来调用这个函数,这样我们每次都得到一个新的实例(我觉得这个方法不是很好,每个变量的意思不清楚)//如果该类已经存在并被实例化,则使用别名获取他的类$abstract=isset($this->name[$abstract])?$this->名称[$抽象]:$抽象;//如果已经Instantiate了,不用每次都创建一个新的实例,直接返回注册树上的实例if(isset($this->instances[$abstract])&&!$newInstance){return$this->instances[$摘要];}前面说了name存储的是已经实例化的alias=>类名的映射关系,我们这里尝试获取类名,如果类被实例化了直接返回。//如果我们绑定了这个类,例如'app'=>'think\\App',if(isset($this->bind[$abstract])){$concrete=$this->bind[$abstract];//因为ThinkPHP实现可以绑定闭包或者匿名函数进入,所以这里是对闭包的处理if($concreteinstanceofClosure){$object=$this->invokeFunction($concrete,$vars);}else{//记录映射关系,根据类名实例化,如think\\App$this->name[$abstract]=$concrete;返回$this->make($concrete,$vars,$newInstance);}}else{//根据类名调用类$object=$this->invokeClass($abstract,$vars);}这里是看我们需要容器加载的类之前是否绑定了别名(我们也可以直接bind('classNickName')设置一个)如果绑定了就实例化。如果不是,那么就假定他是一个类名,直接调用。3门面模式在上面的IOC容器中,我们需要$ioc->get('test');获取测试类并使用我们的$user->hello()方法来打招呼。有了门面之后,我们就可以直接使用Test::hello()进行静态调用了。下面介绍一下这种在我们写代码的时候经常会用到facade包下的类来接口的静态调用。这里以官网为例。如果我们定义了一个app\common\Test类,里面有一个hello动态方法。hello('thinkphp');//输出hello,thinkphp接下来,我们为这个类定义一个静态代理类app\facade\Test(这个类名不必和Test类保持一致,但通常为了方便管理,建议保留名字制服)。make('Test');为什么使用Facade使用Facades最重要的是它提供了简单易记的语法,因此无需手动注入或配置长类名。此外,由于它们对PHP静态方法的独特调用,它们非常容易测试。这里系统找不到Container类的位置,所以会执行自动加载机制,找到Container的位置并加载?把一堆实例挂在树上,需要的时候再用。?直接调用是使用反射的结果,关于反射的知识点可以自行查看?