thinkphp5.1框架中容器(Container)和门面(Facade)的实现方法分析
本文实例讲述了thinkphp5.1框架中容器(Container)和门面(Facade)的实现方法。分享给大家供大家参考,具体如下:
tp5.1中引入了容器(Container)和门面(Facade)这两个新的类
官方文档已经给出了定义:
容器(Container)实现类的统一管理,确保对象实例的唯一性。
门面(Facade)为容器(Container)中的类提供了一个静态调用接口,相比于传统的静态方法调用,带来了更好的可测试性和扩展性,你可以为任何的非静态类库定义一个facade类。
深入源码,我们来看看它到底是如何实现的:
//在框架目录下的base.php文件 //注册核心类到容器 Container::getInstance()->bind([ 'app'=>App::class, 'build'=>Build::class, 'cache'=>Cache::class, 'config'=>Config::class, ... ]); //注册核心类的静态代理 Facade::bind([ facade\App::class=>App::class, facade\Build::class=>Build::class, facade\Cache::class=>Cache::class, facade\Config::class=>Config::class, ... ]); //注册类库别名 Loader::addClassAlias([ 'App'=>facade\App::class, 'Build'=>facade\Build::class, 'Cache'=>facade\Cache::class, 'Config'=>facade\Config::class, ... ]);
容器实现:
这里,框架已经帮我们绑定了系统常用类到容器中,在之后使用时,只需要调用助手函数app()进行容器中的类解析调用,对于已经绑定的类标识,会自动快速实例化。
//实例化缓存类 app('cache'); //app('cache',['file']);参数化调用 //相当于执行了 Container::get('cache'); //查看源码,Container调用的其实是make方法,在该方法里调用反射等实现类的实例化,过程如下: publicfunctionmake($abstract,$vars=[],$newInstance=false) { if(true===$vars){ //总是创建新的实例化对象 $newInstance=true; $vars=[]; } if(isset($this->instances[$abstract])&&!$newInstance){ $object=$this->instances[$abstract]; }else{ if(isset($this->bind[$abstract])){ $concrete=$this->bind[$abstract]; //闭包实现 if($concreteinstanceof\Closure){ $object=$this->invokeFunction($concrete,$vars); }else{ $object=$this->make($concrete,$vars,$newInstance); } }else{ //反射实现 $object=$this->invokeClass($abstract,$vars); } if(!$newInstance){ $this->instances[$abstract]=$object; } } return$object; } /** *调用反射执行类的实例化支持依赖注入 *@accesspublic *@paramstring$class类名 *@paramarray$vars变量 *@returnmixed */ publicfunctioninvokeClass($class,$vars=[]) { $reflect=new\ReflectionClass($class); $constructor=$reflect->getConstructor(); if($constructor){ $args=$this->bindParams($constructor,$vars); }else{ $args=[]; } return$reflect->newInstanceArgs($args); } /** *执行函数或者闭包方法支持参数调用 *@accesspublic *@paramstring|array|\Closure$function函数或者闭包 *@paramarray$vars变量 *@returnmixed */ publicfunctioninvokeFunction($function,$vars=[]) { $reflect=new\ReflectionFunction($function); $args=$this->bindParams($reflect,$vars); return$reflect->invokeArgs($args); }
简而言之,容器内部是通过反射类或闭包等来实现类的实例化。
门面实现:
以一个例子来分析:
facade\Config::get('app_debug');
我们来分析一下它的实现方式:
//thinkphp\library\facade\Config类 namespacethink\facade; usethink\Facade; classConfigextendsFacade { } //从源代码上看Config本身没有任何方法,它继承了Facade的方法,但Facade并没有get这个静态方法 //此时,系统自动触发了魔术方法:__callStatic(),Facade重写了此方法: publicstaticfunction__callStatic($method,$params) { returncall_user_func_array([static::createFacade(),$method],$params); } //可见,最后调用的是用户自定义函数:call_user_func_array([实例,方法],参数),为了获得Config实例,Facade又定义了一个获取对象的方法: /** *创建Facade实例 *@static *@accessprotected *@paramstring$class类名或标识 *@paramarray$args变量 *@parambool$newInstance是否每次创建新的实例 *@returnobject */ protectedstaticfunctioncreateFacade($class='',$args=[],$newInstance=false) { $class=$class?:static::class; $facadeClass=static::getFacadeClass(); if($facadeClass){ $class=$facadeClass; }elseif(isset(self::$bind[$class])){ $class=self::$bind[$class]; } if(static::$alwaysNewInstance){ $newInstance=true; } returnContainer::getInstance()->make($class,$args,$newInstance); } //其内部是通过容器来实例化对象 //因为在base.php中已经将think\Config类绑定到config这个标识 Container::getInstance()->bind([ 'config'=>Config::class ]) //在createFacade方法中,获取类的名称:$class=$class?:static::class;即得到config这个标识 //在容器的make方法中,根据config标识,找到绑定的think\Config类,并调用其动态方法get。 facade\Config::get('app_debug'); //最后调用的是: (newthink\Config())->get('app_debug');
简而言之,门面的实现是通过PHP的魔术方法__callStatic,再配合容器来实现动态类的静态化调用。
更多关于thinkPHP相关内容感兴趣的读者可查看本站专题:《ThinkPHP入门教程》、《thinkPHP模板操作技巧总结》、《ThinkPHP常用方法总结》、《codeigniter入门教程》、《CI(CodeIgniter)框架进阶教程》、《ZendFrameWork框架入门教程》及《PHP模板技术总结》。
希望本文所述对大家基于ThinkPHP框架的PHP程序设计有所帮助。