thinkphp5.1框架中容器(Container)和门面(Facade)的实现方法分析


Posted in PHP onAugust 05, 2019

本文实例讲述了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方法,在该方法里调用反射等实现类的实例化,过程如下:
public function make($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 ($concrete instanceof \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;
}
/**
 * 调用反射执行类的实例化 支持依赖注入
 * @access public
 * @param string  $class 类名
 * @param array   $vars 变量
 * @return mixed
 */
public function invokeClass($class, $vars = [])
{
  $reflect   = new \ReflectionClass($class);
  $constructor = $reflect->getConstructor();
  if ($constructor) {
    $args = $this->bindParams($constructor, $vars);
  } else {
    $args = [];
  }
  return $reflect->newInstanceArgs($args);
}
/**
 * 执行函数或者闭包方法 支持参数调用
 * @access public
 * @param string|array|\Closure $function 函数或者闭包
 * @param array         $vars   变量
 * @return mixed
 */
public function invokeFunction($function, $vars = [])
{
  $reflect = new \ReflectionFunction($function);
  $args  = $this->bindParams($reflect, $vars);
  return $reflect->invokeArgs($args);
}

简而言之,容器内部是通过反射类或闭包等来实现类的实例化。

门面实现:

以一个例子来分析:

facade\Config::get('app_debug');

我们来分析一下它的实现方式:

// thinkphp\library\facade\Config 类
namespace think\facade;
use think\Facade;
class Config extends Facade
{
}
// 从源代码上看 Config本身没有任何方法,它继承了Facade的方法,但Facade并没有get这个静态方法
// 此时,系统自动触发了魔术方法:__callStatic(),Facade重写了此方法:
public static function __callStatic($method, $params)
{
  return call_user_func_array([static::createFacade(), $method], $params);
}
// 可见,最后调用的是用户自定义函数:call_user_func_array([实例, 方法], 参数),为了获得Config实例,Facade又定义了一个获取对象的方法:
/**
 * 创建Facade实例
 * @static
 * @access protected
 * @param string  $class     类名或标识
 * @param array   $args      变量
 * @param bool   $newInstance  是否每次创建新的实例
 * @return object
 */
protected static function createFacade($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;
  }
  return Container::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');
// 最后调用的是:
(new think\Config())->get('app_debug');

简而言之,门面的实现是通过PHP的魔术方法 __callStatic,再配合容器来实现动态类的静态化调用。

希望本文所述对大家基于ThinkPHP框架的PHP程序设计有所帮助。

PHP 相关文章推荐
PHP读写文件的方法(生成HTML)
Nov 27 PHP
使用TinyButStrong模板引擎来做WEB开发
Mar 16 PHP
php的access操作类
Apr 09 PHP
php中的数组操作函数整理
Aug 18 PHP
探讨多键值cookie(php中cookie存取数组)的详解
Jun 06 PHP
ThinkPHP3.1新特性之对页面压缩输出的支持
Jun 19 PHP
9个经典的PHP代码片段分享
Dec 18 PHP
CI框架表单验证实例详解
Nov 21 PHP
PHP实现动态删除XML数据的方法示例
Mar 30 PHP
php高清晰度无损图片压缩功能的实现代码
Dec 09 PHP
PDO::query讲解
Jan 29 PHP
php实现多站点共用session实现单点登录的方法详解
Sep 18 PHP
RSA实现JS前端加密与PHP后端解密功能示例
Aug 05 #PHP
thinkPHP5框架接口写法简单示例
Aug 05 #PHP
ThinkPHP5+UEditor图片上传到阿里云对象存储OSS功能示例
Aug 05 #PHP
PHP各种常见经典算法总结【排序、查找、翻转等】
Aug 05 #PHP
php时间戳转换代码详解
Aug 04 #PHP
ThinkPHP5.1框架数据库链接和增删改查操作示例
Aug 03 #PHP
ThinkPHP5&5.1框架关联模型分页操作示例
Aug 03 #PHP
You might like
PHP 和 MySQL 基础教程(三)
2006/10/09 PHP
一篇入门的php Class 文章
2007/04/04 PHP
一个简单的网页密码登陆php代码
2012/07/17 PHP
PHP编辑器PhpStrom运行缓慢问题
2017/02/21 PHP
jquery中event对象属性与方法小结
2013/12/18 Javascript
JSON中双引号的轮回使用过程中一定要小心
2014/03/05 Javascript
javascript版2048小游戏
2015/03/18 Javascript
JS中多步骤多分步的StepJump组件实例详解
2016/04/01 Javascript
Angularjs中$http以post请求通过消息体传递参数的实现方法
2016/08/05 Javascript
javascript中获取元素标签中间的内容的实现方法
2016/10/08 Javascript
详解Vue.use自定义自己的全局组件
2017/06/14 Javascript
Angular.js中$resource高大上的数据交互详解
2017/07/30 Javascript
Node.js 使用流实现读写同步边读边写功能
2017/09/11 Javascript
对于js垃圾回收机制的理解
2017/09/14 Javascript
微信小程序 swiper组件构建轮播图的实例
2017/09/20 Javascript
element 结合vue 在表单验证时有值却提示错误的解决办法
2018/01/22 Javascript
微信小程序如何访问公众号文章
2019/07/08 Javascript
[06:45]2018DOTA2亚洲邀请赛 4.5 SOLO赛 Sccc vs Maybe
2018/04/06 DOTA
在Linux命令行终端中使用python的简单方法(推荐)
2017/01/23 Python
Python与Java间Socket通信实例代码
2017/03/06 Python
django rest framework之请求与响应(详解)
2017/11/06 Python
Python Numpy中数据的常用保存与读取方法
2020/04/01 Python
python3发送request请求及查看返回结果实例
2020/04/30 Python
HTML5实现无刷新修改URL的方法
2019/11/14 HTML / CSS
植村秀加拿大官网:Shu Uemura加拿大
2019/09/03 全球购物
会计实习生自我鉴定
2013/12/12 职场文书
申报职称专业技术个人的自我评价
2013/12/12 职场文书
班队活动设计方案
2014/01/30 职场文书
房屋继承公证书
2014/04/10 职场文书
中文专业求职信
2014/06/20 职场文书
社区志愿者活动方案
2014/08/18 职场文书
毕业证明模板
2015/06/19 职场文书
公司周年庆典致辞
2015/07/30 职场文书
获奖感言范文
2015/07/31 职场文书
2016年圣诞节活动总结范文
2016/04/01 职场文书
Python实战之大鱼吃小鱼游戏的实现
2022/04/01 Python