TP5源码解析之容器(一)

Bug制造机
• 阅读 1725

TP5的容器文件是thinkphp/library/think/Container.php,该类实现了三个接口,分别是:

  • ArrayAccess使容器支持数组的形式访问
  • IteratorAggregate使容器支持迭代
  • Countable使容器可以使用count()函数获取容器中实例的个数

该类中的方法较多,分两篇文章讲述,本篇分析如何绑定到容器、容器如何创建类的实例,主要涉及类中的几个属性$instance、$instances、$bind、$name,以及三个方法:bindTo()instance()make()

属性

  • $instance 容器对象实例,通过类方法getInstance()单例实现
  • $instances 容器中的对象实例,用于存放容器中的类、接口有直接实例的情况
  • $bind 容器绑定标识,保存了系统中常用的标识和对应类的映射关系,这里主要是为了开发者通过一个简单的标识就能快速定位到系统中要绑定的类
  • $name 容器标识别名,这里也是为了方便开发者通过自定义的标识,就快速定位到系统zhong要绑定的类

方法

  • bindTo($abstract, $concrete = null)
  1. 作用:批量绑定标识、闭包、对象、接口到容器
  2. 代码解析:
/**
 * 绑定一个类、闭包、实例、接口实现到容器
 * @access public
 * @param  string|array  $abstract    标识
 * @param  mixed         $concrete    实现
 * @return $this
 */
public function bindTo($abstract, $concrete = null)
{
    if (is_array($abstract)) {
        //如果第一个参数是数组,将传进来的数组与类中已有的$this->bind属性合并(传进来的数组会覆盖同名的数组项)
        $this->bind = array_merge($this->bind, $abstract);
    } elseif ($concrete instanceof Closure) {
        //如果第二个参数是一个闭包,绑定标识和闭包放入$this->bind
        $this->bind[$abstract] = $concrete;
    } elseif (is_object($concrete)) {
        //如果第二个参数是一个对象,并且第一个参数在属性 $this->bind 数组中已存在,先取出该标识对应的完整类名,然后将类名和类实例绑定,放入$this->instances 属性中
        if (isset($this->bind[$abstract])) {
            $abstract = $this->bind[$abstract];
        }
        $this->instances[$abstract] = $concrete;
    } else {
        //都没有匹配到,直接绑定标识和对应的实现,放入属性 $this->bind 
        $this->bind[$abstract] = $concrete;
    }

    return $this;
}
  • instance($abstract, $instance)
  1. 作用:该方法与bindTo()方法类似,但是更专一,主要是绑定接口和类实现到容器中,bindTo()中已经包含这部分代码,不再介绍
  2. 代码解析:
/**
 * 绑定一个类实例当容器
 * @access public
 * @param  string           $abstract    标识
 * @param  object|\Closure  $instance    实现
 * @return $this
 */
public function instance($abstract, $instance)
{
    if ($instance instanceof \Closure) {
        $this->bind[$abstract] = $instance;
    } else {
        if (isset($this->bind[$abstract])) {
            $abstract = $this->bind[$abstract];
        }

        $this->instances[$abstract] = $instance;
    }

    return $this;
}
  • make($abstract, $vars = [], $newInstance = false)
  1. 作用:创建类的实例
  2. 代码解析:
/**
     * 创建类的实例
     * @access public
     * @param  string        $abstract       类名或者标识
     * @param  array|true    $vars           变量
     * @param  bool          $newInstance    是否每次创建新的实例
     * @return object
     */
    public function make($abstract, $vars = [], $newInstance = false)
    {
        if (true === $vars) {
            // 总是创建新的实例化对象
            $newInstance = true;
            $vars        = [];
        }
        
        //首先判断标识别名数组中是否包含该标识,如果存在,取出对应的值,当作新的标识
        $abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract;

        //容器中的对象实例数组中是否包含该标识,如果不存在,并且不需要一个新的实例,直接返回该标识对应的实例
        if (isset($this->instances[$abstract]) && !$newInstance) {
            return $this->instances[$abstract];
        }
       
        if (isset($this->bind[$abstract])) {
            //如果容器标识数组中存在该标识,判断对应的值是否是闭包,
            // 是:利用php的反射机制调用闭包函数
            // 否:将该标识和值放入别名数组中,递归调用自己,直到$this->bind 属性数组没有标识。其实正常情况下,这里的值应该就是类名了,但是因为在 bindTo 数组绑定时直接进行了合并,所以存在这种可能:bindTo(['myapp' => 'app']),make 的时候传入的参数是:myapp,如果是这种情况,那就需要先根据 myapp 标识取出 app 标识,再根据 app 标识获取对应的类:App::class
            $concrete = $this->bind[$abstract];

            if ($concrete instanceof Closure) {
                $object = $this->invokeFunction($concrete, $vars);
            } else {
                $this->name[$abstract] = $concrete;
                return $this->make($concrete, $vars, $newInstance);
            }
        } else {
            //等到 $this->bind 属性中匹配不到值的时候,就会走到这里,调用 php 的反射类 invokeClass,获取类实例 
            $object = $this->invokeClass($abstract, $vars);
        }
        //如果 make 的时候不要求产生一个新的对象,可以将类实例放入容器中的对象实例数组中,下次调用就可以直接获取
        if (!$newInstance) {
            $this->instances[$abstract] = $object;
        }
        //返回生成的类实例
        return $object;
    }
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
3年前
spring源码
SpringIOC容器源码解析系列,建议大家按顺序阅读,欢迎讨论(_spring源码均为4.1.6.RELEASE版本_)1.Spring源码IOC容器(一)构建简单IOC容器(https://my.oschina.net/u/2377110/blog/902073)2.Spring源码IOC容器(二)Bean的定位解析注
Easter79 Easter79
3年前
spring源码解析
前言上篇我们介绍了spring容器加载的方式,并重点介绍了基于xml配置解析和注解扫描两种容器加载的方式,封装和注册beandefinition的过程。今天我们分享BeanDefinition注册后的另一个重要过程bean的实例化过程的源码。容器加载流程!spring源码解析spring容器加载源码(bean实
沸腾的木马 沸腾的木马
2年前
关于dorker安装的wordpress无法上传主题和插件的解决办法
首先,容器里居然没有php.ini这个文件。第二,容器里的配置文件无法编辑解决办法在php官网下载php.ini这个文件,修改后通过主机copy到容器里1、要查找容器的名称或ID,可以使用以下命令:dockerps这将显示正在运行的容器的列表,类似于以下示
Stella981 Stella981
3年前
Kubernetes源码探疑:Pod IP泄露排查及解决
作者:陈绥来源:UCloud(ID:ucloud\_tech)UK8S是UCloud推出的Kubernetes容器云产品,完全兼容原生API,为用户提供一站式云上Kubernetes服务。我们团队自研了CNI(Container Network Interface)网络插件,深度集成VPC,使UK8S容器应用拥有与云主机间等同的网络性能(目前
Easter79 Easter79
3年前
Spring系列(五) 容器初始化过程源码
IoC/DI的概念容器是Spring的核心之一(另一个核心是AOP).有了容器,IOC才可能实现.什么使IoC?IoC就是将类自身管理的与其由依赖关系的对象的创建/关联和管理交予容器实现,容器按照配置(比如xml文件)来组织应用对象的创建和关联.什么使DI?DI是IoC的实现方式,由容器
Stella981 Stella981
3年前
Docker 容器健康检查
!DOCKERHEALTHCHECK(https://imgblog.csdnimg.cn/20210120134741738.jpg)Docker容器健康检查指的是在Dockerfile中使用HEALTHCHECK指令对容器的运行状态进行检查,并在dockerps的STATUS栏显示healthy/unhealt
Wesley13 Wesley13
3年前
1. 容器化部署一套云服务 第一讲 Jenkins(Docker + Jenkins + Yii2 + 云服务器))
容器化部署一套云服务系列1\.容器化部署一套云服务之Jenkins(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.cnblogs.com%2Fjackson0714%2Fp%2Fdeploy1.html)一、购买服务器服务器!caeef00
十月飞翔 十月飞翔
3年前
Docker cmd
1.复制从主机复制到容器sudodockercphostpathcontainerID:containerpath从容器复制到主机sudodockercpcontainerID:containerpathhostpath容器ID查看:dockerpsa
十月飞翔 十月飞翔
3年前
Docker
复制从主机复制到容器sudodockercphostpathcontainerID:containerpath从容器复制到主机sudodockercpcontainerID:containerpathhostpath容器ID查看:dockerpsa1.isthedockerdaemonrunning?解决办法:$systemc
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Bug制造机
Bug制造机
Lv1
江南可采莲,莲叶何田田!
文章
4
粉丝
0
获赞
0