下面的源码分析,我们可以从App和Http类的实例化过程中了解到类是如何自动实例化以及依赖注入是如何实现的。从入口文件开始访问ThinkPHP构建的站点时,框架首先从入口文件开始,然后进行应用初始化、路由解析、控制器调用、响应输出等操作。入口文件主要代码如下:当App实例化执行newApp()实例化时,会先调用其构造函数。构造函数实现项目各种基本路径的初始化,读取provider.php文件,将其类绑定合并到$bind成员变量中。provider.php文件默认内容如下:合并后,$bind成员变量的值如下:$bind的值是一组类ID到类的映射。从这个实现中也可以看出,我们不仅可以在provider.php文件中添加标识符到类的映射,还可以覆盖掉它原来的映射,即将一些核心类替换为自定义类。static::setInstance($this)的作用如图:think\App类的$instance成员变量指向think\App类的一个实例,即类本身保存了一个think\App类的实例本身。instance()方法的实现:getAlias方法:执行结果大概是这样的:Http类的实例化和依赖注入原理这里,$http=(newApp())->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类的实例:更多学习内容请访问从coder到architect的培训路径
