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程序设计有所帮助。