ThinkPHP6 核心分析之Http 类跟Request类的实例化

如下源码分析,咱们能够从 App,Http 类的实例化过程,了解类是如何实现自动实例化的,依赖注入是怎么实现的。

从入口文件出发

当访问一个 ThinkPHP 搭建的站点,框架最早是从入口文件开始的,而后才是应用初始化、路由解析、控制器调用和响应输出等操做。
 
入口文件主要代码以下:
 

App 实例化

执行 new App() 实例化时,首先会调用它的构造函数。
 
 
构造函数实现了项目各类基础路径的初始化,并读取了 provider.php 文件,将其类的绑定并入 $bind 成员变量,provider.php 文件默认内容以下:
 
 
合并后,$bind 成员变量的值以下:
 
 
$bind 的值是一组类的标识到类的映射。从这个实现也能够看出,咱们不只能够在 provider.php 文件中添加标识到类的映射,并且能够覆盖其原有的映射,也就是将某些核心类替换成本身定义的类。
 
static::setInstance($this) 实现的做用,如图:
 
 
think\App 类的 $instance 成员变量指向 think\App 类的一个实例,也就是类本身保存本身的一个实例。
instance() 方法的实现:
 
 
其中的getAlias方法:
 
 
执行结果大概是这样的:
 

Http 类的实例化以及依赖注入原理

这里,$http = (new App())->http,前半部分好理解,后半部分乍一看有点让人摸不着头脑,App 类并不存在 http 成员变量,这里何以大胆调用了一个不存在的东东呢?
 
原来,App 类继承自 Container 类,而 Container 类实现了__get() 魔术方法,在 PHP 中,当访问到的变量不存在,就会触发__get() 魔术方法。该方法的实现以下:
 
 
其实是调用get()方法:
 
 
然而,实际上,主要是make()方法:
 
 
然而,make()方法主要靠invokeClass()来实现类的实例化。该方法具体分析:
 
 
以上代码可看出,在一个类中,添加__make()方法,在类实例化时,会最早被调用。以上最值得一提的是bindParams()方法:
 
 
而这之中,又最值得一提的是getObjectParam()方法:
 
 
getObjectParam() 方法再一次光荣地调用 make() 方法,实例化一个类,而这个类,正是从 Http 的构造函数提取的参数,而这个参数又偏偏是一个类的实例 ——App 类的实例。到这里,程序不只经过 PHP 的反射类实例化了 Http 类,并且实例化了 Http 类的依赖 App 类。假如 App 类又依赖 C 类,C 类又依赖 D类…… 无论多少层,整个依赖链条依赖的类均可以实现实例化。
 
总的来讲,整个过程大概是这样的:须要实例化 Http 类 ==> 提取构造函数发现其依赖 App 类 ==> 开始实例化 App 类(若是发现还有依赖,则一直提取下去,直到天荒地老)==> 将实例化好的依赖(App 类的实例)传入 Http 类来实例化 Http 类。
 
这个过程,起个装逼的名字就叫作「依赖注入」,起个摸不着头脑的名字,就叫作「控制反转」。
 
这个过程,若是退回远古时代,要实例化 Http 类,大概是这样实现的(假若有不少层依赖):
 
 
这得有多累人。而现代 PHP,交给「容器」就行了。
 
另外,须要提的一点是 make 方法的 $vars 参数,它的形式能够是普通数组、关联数组,并且数组中元素的值能够是一个类的实例。$vars 参数的值最终将传递给要实例化的类的构造函数或者__make 方法中对应的参数。
 

Request 类的实例化

接上一面,获得Http类的一个实例后,程序接下来执行$response = $http->run();。其中run()方法代码以下:
 
 

从「request」标识找到要实例化的类

run() 方法的第一行经过容器类实例 app 调用 make() 方法并传入 Request 类的标识来实例化 Request 类。具体过程以下分析。
 
经过 make() 方法首先解析获得 request 标识对应的标识 think\Request, 进一步递归解析,又获得 app\Request 类 —— 这个才是最终要实例化的类。
 
app\Request 类对应的文件位于 app 目录下,代码以下:
 
 
实际上它啥事也没干,直接继承系统的 \think\Request。固然咱们也能够在这里对系统的 Request 类进行改写重构。

调用 invokeClass () 方法

从类的标识解析获得最终须要实例化的类(单例模式下,且该类还不存在实例)以后,程序调用 invokeClass () 方法,经过 PHP 的反射类实现类的实例化。因为 \think\Request 类存在__make() 方法,因此实例化以前首先调用该方法。__make() 方法代码以下:
 
 
__make()方法首先实例化think\Request类自身。think\Request类构造函数以下:
 
 
构造函数读取了 php://input 保存起来。接着,__make() 方法保存了一些请求相关的数据,最后返回一个 Request 类实例。最后的最后, make() 方法也成功获得该实例,整个过程跟 Http 类的实例化相似。该 Request 类实例部分红员变量如图:
 

保存「Request」类的实例到「$instances」数组

获得 Request 类的实例后,run() 方法接着将该实例保存到「$instance」数组,以便后面单例模式要用到时能够直接获取。$instances 数组的值如图,Request 类的实例已保存在里面:
 
相关文章
相关标签/搜索