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中使用Oracle数据库(5)
Oct 09 PHP
php查看session内容的函数
Aug 27 PHP
PHP求最大子序列和的算法实现
Jun 24 PHP
phpadmin如何导入导出大数据文件及php.ini参数修改
Feb 18 PHP
php模拟ping命令(php exec函数的使用方法)
Oct 25 PHP
linux系统下php安装mbstring扩展的二种方法
Jan 20 PHP
WebQQ最新登陆协议的用法
Dec 22 PHP
PHP如何实现跨域
May 30 PHP
PHP实现递归目录的5种方法
Oct 27 PHP
基于swoole实现多人聊天室
Jun 14 PHP
PHP抽象类与接口的区别详解
Mar 21 PHP
Laravel 登录后清空COOKIE的操作方法
Oct 14 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
收音机术语解释
2021/03/01 无线电
我的论坛源代码(四)
2006/10/09 PHP
php foreach循环中使用引用的问题
2013/11/06 PHP
CI框架源码解读之URI.php中_fetch_uri_string()函数用法分析
2016/05/18 PHP
PHP基于自定义类随机生成姓名的方法示例
2017/08/05 PHP
PHP操作MySQL中BLOB字段的方法示例【存储文本与图片】
2017/09/15 PHP
页面中js执行顺序
2009/11/09 Javascript
JavaScript写的一个自定义弹出式对话框代码
2010/01/17 Javascript
node.js中的fs.linkSync方法使用说明
2014/12/15 Javascript
js函数与php函数的区别实例浅析
2015/01/12 Javascript
JQuery中两个ul标签的li互相移动实现方法
2015/05/18 Javascript
AngularJS 使用 UI Router 实现表单向导
2016/01/29 Javascript
jQuery实现表格行和列的动态添加与删除方法【测试可用】
2016/08/01 Javascript
BootStrap fileinput.js文件上传组件实例代码
2017/02/20 Javascript
详解Node.js实现301、302重定向服务
2017/04/07 Javascript
angularjs利用directive实现移动端自定义软键盘的示例
2017/09/20 Javascript
Angularjs添加排序查询功能的实例代码
2017/10/24 Javascript
小程序接口的promise化的实现方法
2019/12/11 Javascript
Python设置Socket代理及实现远程摄像头控制的例子
2015/11/13 Python
Python写的一个定时重跑获取数据库数据
2016/12/28 Python
Django的信号机制详解
2017/05/05 Python
python调用系统ffmpeg实现视频截图、http发送
2018/03/06 Python
python对html过滤处理的方法
2018/10/21 Python
Python 列表推导式需要注意的地方
2020/10/23 Python
检测浏览器是否支持html5视频的代码
2013/03/28 HTML / CSS
phpquery中文手册
2021/03/18 PHP
印尼网上商店:Alfacart.com
2019/03/11 全球购物
巴黎欧莱雅法国官网:L’Oreal Paris
2019/04/30 全球购物
北承题目(C++)
2012/05/16 面试题
如何进行Linux分区优化
2016/09/13 面试题
国外软件测试工程师面试题
2016/12/09 面试题
拾金不昧表扬信范文
2014/01/11 职场文书
《再别康桥》教学反思
2014/02/12 职场文书
个人四风对照检查材料
2014/09/26 职场文书
会计工作总结范文2014
2014/12/23 职场文书
MySQL磁盘碎片整理实例演示
2022/04/03 MySQL