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在字符断点处截断文字的实现代码
Apr 21 PHP
基于ubuntu下nginx+php+mysql安装配置的具体操作步骤
Apr 28 PHP
php 获取SWF动画截图示例代码
Feb 10 PHP
php可生成缩略图的文件上传类实例
Dec 17 PHP
php封装的mysqli类完整实例
Oct 18 PHP
Yii2实现log输出到file及database的方法
Nov 12 PHP
PHP批量获取网页中所有固定种子链接的方法
Nov 18 PHP
thinkPHP多表查询及分页功能实现方法示例
Jul 03 PHP
php获取用户真实IP和防刷机制的实例代码
Nov 28 PHP
PHP实现二维数组按照指定的字段进行排序算法示例
Apr 23 PHP
PHP xpath提取网页数据内容代码解析
Jul 16 PHP
PHP读取文件或采集时解决中文乱码
Mar 09 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实现图片添加水印功能
2014/02/13 PHP
destoon二次开发常用数据库操作
2014/06/21 PHP
PHP数据库连接mysql与mysqli对比分析
2016/01/04 PHP
JavaScript取得鼠标绝对位置程序代码介绍
2012/09/16 Javascript
关于jQuery object and DOM element
2013/04/15 Javascript
js字符串截取函数substr substring slice使用对比
2013/11/27 Javascript
浮动的div自适应居中显示的js代码
2013/12/23 Javascript
JS获取随机数和时间转换的简单实例
2016/07/10 Javascript
jQuery实现的分页功能示例
2017/01/22 Javascript
jQuery实现淡入淡出的模态框
2017/02/09 Javascript
微信小程序实战之自定义抽屉菜单(7)
2017/04/18 Javascript
ES6新特性八:async函数用法实例详解
2017/04/21 Javascript
jQuery、layer实现弹出层的打开、关闭功能
2017/06/28 jQuery
webpack 单独打包指定JS文件的方法
2018/02/22 Javascript
JavaScript函数节流和函数去抖知识点学习
2018/07/31 Javascript
ElementUI radio组件选中小改造
2019/08/12 Javascript
浅谈JavaScript窗体Window.ShowModalDialog使用
2020/07/22 Javascript
vue 实现锚点功能操作
2020/08/10 Javascript
[01:03:33]Alliance vs TNC 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/18 DOTA
浅析使用Python操作文件
2017/07/31 Python
Python中的Django基本命令实例详解
2018/07/15 Python
python实现简单日期工具类
2019/04/24 Python
Django REST Framework序列化外键获取外键的值方法
2019/07/26 Python
Python基础之函数基本用法与进阶详解
2020/01/02 Python
Django 5种类型Session使用方法解析
2020/04/29 Python
一款利用css3的鼠标经过动画显示详情特效的实例教程
2014/12/29 HTML / CSS
基于HTML5 FileSystem API的使用介绍
2013/04/24 HTML / CSS
印度尼西亚电子产品购物网站:Kliknklik
2018/06/05 全球购物
Guess美国官网:美国知名服装品牌
2019/04/08 全球购物
广州御银科技股份有限公司试卷(C++)
2016/11/04 面试题
装潢设计实习自我鉴定
2013/09/19 职场文书
写给妈妈的感谢信
2015/01/22 职场文书
纯html+css实现打字效果
2021/08/02 HTML / CSS
36个正则表达式(开发效率提高80%)
2021/11/17 Javascript
alibaba seata服务端具体实现
2022/02/24 Java/Android
Windows Server 2022 超融合部署(图文教程)
2022/06/25 Servers