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中的登陆login
Jan 18 PHP
php学习 函数 课件
Jun 15 PHP
php相当简单的分页类
Oct 02 PHP
php 小乘法表实现代码
Jul 16 PHP
PHP仿博客园 个人博客(1) 数据库与界面设计
Jul 05 PHP
php中cookie实现二级域名可访问操作的方法
Nov 11 PHP
php实现数组按指定KEY排序的方法
Mar 30 PHP
php使用指定字符列表生成随机字符串的方法
Apr 18 PHP
php简单实现单态设计模式的方法分析
Jul 28 PHP
PHP中的浅复制与深复制的实例详解
Oct 26 PHP
php post json参数的传递和接收处理方法
May 31 PHP
thinkPHP5框架实现多数据库连接,跨数据连接查询操作示例
May 29 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
介绍几个array库的新函数 php
2006/12/29 PHP
Discuz! Passport 通行证整合
2008/03/27 PHP
openPNE常用方法分享
2011/11/29 PHP
php格式化时间戳显示友好的时间实现思路及代码
2014/10/23 PHP
PHP递归遍历指定文件夹内的文件实现方法
2016/11/15 PHP
phpcms配置列表页以及获得文章发布时间
2017/07/04 PHP
php实现文章评论系统
2019/02/18 PHP
var与Javascript变量隐式声明
2009/09/17 Javascript
js操作ajax返回的json的注意问题!
2010/02/23 Javascript
IE关闭时判断及AJAX注销案例学习
2013/02/18 Javascript
Jquery和JS用外部变量获取Ajax返回的参数值的方法实例(超简单)
2013/06/17 Javascript
JQuery为页面Dom元素绑定事件及解除绑定方法
2014/04/23 Javascript
Jquery节点遍历next与nextAll方法使用示例
2014/07/22 Javascript
node.js中的fs.writeFileSync方法使用说明
2014/12/14 Javascript
jQuery Ajax 实例代码 ($.ajax、$.post、$.get)
2016/04/29 Javascript
jquery实现弹窗功能(窗口居中显示)
2017/02/27 Javascript
layui(1.0.9)文件上传upload,前后端的实例代码
2019/09/26 Javascript
Python logging模块学习笔记
2014/05/24 Python
简单的Apache+FastCGI+Django配置指南
2015/07/22 Python
Python基础语法(Python基础知识点)
2016/02/28 Python
基于python爬虫数据处理(详解)
2017/06/10 Python
Python可变和不可变、类的私有属性实例分析
2019/05/31 Python
使用Python和OpenCV检测图像中的物体并将物体裁剪下来
2019/10/30 Python
python实现在一个画布上画多个子图
2020/01/19 Python
python爬虫实现POST request payload形式的请求
2020/04/30 Python
使用Html5、CSS实现文字阴影效果
2018/01/17 HTML / CSS
JD Sports马来西亚:英国领先的运动鞋和运动服饰零售商
2018/03/13 全球购物
GIVENCHY纪梵希官方旗舰店:高定彩妆与贵族护肤品
2018/04/16 全球购物
Java中各种基本数据类型的默认值都是什么
2016/12/22 面试题
单位成立周年感言
2014/01/26 职场文书
市优秀教师事迹材料
2014/02/05 职场文书
数控专业大学毕业生职业规划范文
2014/02/06 职场文书
运动会广播稿150字
2014/02/19 职场文书
化工工艺设计求职信
2014/06/25 职场文书
检讨书模板大全
2015/05/07 职场文书
2016开学第一课心得体会
2016/01/23 职场文书