前面讲了那么多 实际都只是在分析在分析入口文件index.php代码的第一句
$http = (new App())->http;
甚至连第一句都没完 只是分析了第一句的前半部分 下面我们看下第一句的后半部分 也就是http [tp6vendor opthinkframeworksrc hinkHttp.php]这个类
首先看这个类的构造函数
public function __construct(App $app) { $this->app = $app; $this->routePath = $this->app->getRootPath() . 'route' . DIRECTORY_SEPARATOR; }
这里能看到 构造函数是需要带入一个 $app 参数的
那么这里实际就有一个一开始遗留的问题 为什么 (new App())->http; 这样写 能得到http这个类实例呢? 按照常规思路
我们得到http这个实例 应该 new Http(App $app) 这样才对呀
对于这个问题 如果前三章思路捋顺的话 这个问题可以自己找到答案 这里 我们再过一次这个流程
(1)首先 new App 得到 App对象实例 我们定义这个实例为$app 即 $app = new App();
(2) (new App())->http 则是获取 这个实例的http的属性 这个属性 我们找了APP类没有 它的父类也没有
(3)所以 需要找到魔术方法 __get()
(4) 追溯__get的流程 最终是调用了make方法
(5)make方法传入http 也就是$app->make("http")
(6) 由于http 可以在 $bind里面找到对应的别名 Http::class 也就是 thinkhttp
(7) 那么这里就相当于 $app->make("thinkhttp")
(8)根据上章的讲解和最后的例子 可以知道 这时候 就相当于 new Http()了
这样 就得到http这个类了
【这里实际还是有个疑问 正常 我们应该得到 new Http(App $app) 这样才对呀 构造函数里面必须带入$app这个参数 关于这一点 具体还是要解析make用法 后面会详细讲到 】
构造函数第二句 比较简单 也是定义了一个类似全局变量的东西 $this->routePath = $this->app->getRootPath() . 'route' . DIRECTORY_SEPARATOR; 这里是定义了路由(route)的目录
关于http类 它的方法不是很多 但每一个都相当抽象 针对这种抽象的功能 我建议我们还是从所见记得的实际例子出发 那样会更好理解一点
至于实际例子 index.php文件 的第二句就是了
$response = $http->run();这里可以看到 系统调用了http类的run方法 那么我们就从run方法入手 来了解这个类
run方法如下

public function run(Request $request = null): Response { //初始化 $this->initialize(); //自动创建request对象 $request = $request ?? $this->app->make('request', [], true); $this->app->instance('request', $request); try { $response = $this->runWithRequest($request); } catch (Throwable $e) { $this->reportException($e); $response = $this->renderException($request, $e); } return $response; }
它的第一句$this->initialize() initialize这个单词是初始化的意思 可以推测这里是进行了一些初始化的流程
我们追溯这个函数 能看到 它最终是在app类里面的initialize方法

1 public function initialize() 2 { 3 $this->initialized = true; 4 5 $this->beginTime = microtime(true); 6 $this->beginMem = memory_get_usage(); 7 8 // 加载环境变量 9 if (is_file($this->rootPath . '.env')) { 10 $this->env->load($this->rootPath . '.env'); 11 } 12 13 $this->configExt = $this->env->get('config_ext', '.php'); 14 15 $this->debugModeInit(); 16 17 // 加载全局初始化文件 18 $this->load(); 19 20 // 加载框架默认语言包 21 $langSet = $this->lang->defaultLangSet(); 22 23 $this->lang->load($this->thinkPath . 'lang' . DIRECTORY_SEPARATOR . $langSet . '.php'); 24 25 // 加载应用默认语言包 26 $this->loadLangPack($langSet); 27 28 // 监听AppInit 29 $this->event->trigger(AppInit::class); 30 31 date_default_timezone_set($this->config->get('app.default_timezone', 'Asia/Shanghai')); 32 33 // 初始化 34 foreach ($this->initializers as $initializer) { 35 $this->make($initializer)->init($this); 36 } 37 38 return $this; 39 }
我们可以详细看下这个加载流程
(1)第一句 $this->initialized = true
这个是为了提高性能用, 方式就类似我们常见的缓存使用 if has then return xxx else set xxx 有初始化过 下次再运行到这里 就不用再继续运行 否则进行初始化
这里也能说明 这个函数 框架会在执行的过程中 重复使用多次(至少2次) 我们也可以留一下 后面哪些流程 会导致这里第二次执行
(2)定义一些开始标量 开始时间 程序 开始的内存情况 这种一般是为了统计框架性能使用
$this->beginTime = microtime(true);
$this->beginMem = memory_get_usage();
(3)加载环境变量 这也就是为什么 TP框架 支持再根目录 写个.env 就可以把.env里面的内容当做配置文件使用
if (is_file($this->rootPath . '.env')) {
$this->env->load($this->rootPath . '.env');
}
(4)定义一个叫ConfigExt的东西 暂时不知道哪里用到 从字面意思 应该是加载一些额外的配置文件类型,默认是加载php文件的配置
$this->configExt = $this->env->get('config_ext', '.php');
(5)初始化调试
$this->debugModeInit();
(6)加载全局初始化文件 这个细节需要看下load()这个函数
$this->load();

protected function load(): void { $appPath = $this->getAppPath(); if (is_file($appPath . 'common.php')) { include_once $appPath . 'common.php'; } include_once $this->thinkPath . 'helper.php'; $configPath = $this->getConfigPath(); $files = []; if (is_dir($configPath)) { $files = glob($configPath . '*' . $this->configExt); } foreach ($files as $file) { $this->config->load($file, pathinfo($file, PATHINFO_FILENAME)); } if (is_file($appPath . 'event.php')) { $this->loadEvent(include $appPath . 'event.php'); } if (is_file($appPath . 'service.php')) { $services = include $appPath . 'service.php'; foreach ($services as $service) { $this->register($service); } } }
可以在看到
这里首先加载了 app目录下的common.php --》这就是为什么common.php里面写的全局函数 我们可以直接使用的原因
然后加载了think目录下的helper函数 位置为 p6vendor opthinkframeworksrchelper.php] 这里是官方提供的一些助手函数,比如我们常用的打印函数 dump
然后遍历了config目录 把目录下的所有文件都加载进来 ---》这也就是config目录[项目根目录下的config文件夹]下 我们可以分开写多个的原因
这里也就知道了前面定义的那个configExt是干嘛的 configExt默认定义的是php 也就说这里只会加载config文件夹下面的php后缀文件
然后分别LoadEvent和register service 这种代码的目的 和一开始我们遇到的加载容器默认的provider作用一致
分别是加载默认的event事件 这里可以看到是加载与config同级目录下的event.php
加载service文件 这里可以看到是加载与config同级目录下的service.php
[关于event事件和service服务 这两个特性 后面会再详细讲解 目前暂时不提]
(7)接下来是加载框架的语言包 这是Tp框架可以很好的实现多语言的资本
// 加载框架默认语言包
$langSet = $this->lang->defaultLangSet();
$this->lang->load($this->thinkPath . 'lang' . DIRECTORY_SEPARATOR . $langSet . '.php');
// 加载应用默认语言包
$this->loadLangPack($langSet);
(8)监听一个叫APP::init的事件 这里的$this->event 就是第六条里面加载事件生成的 这里代码的意思
AppInit这个类我们可以看到 目前是空的,那么这里可以先理解为 在这里埋一个点 这个点的名字 叫appinit 至于这里要干嘛 还没想好 哪天想好了 可以很方便的在这个点插入某些业务
比如我想要在appinit的时候 写一个日志 那么就可以在AppInit这个类里面 写入相关的代码 这样的好处是 可以比较方便的扩展 并且不破坏框架原有的代码
$this->event->trigger(AppInit::class);
(9)接下来设置时区
date_default_timezone_set($this->config->get('app.default_timezone', 'Asia/Shanghai'));
(10)加载系统里面已经定义好的一些初始化类
foreach ($this->initializers as $initializer) {
$this->make($initializer)->init($this);
}
protected $initializers = [
Error::class,
RegisterService::class,
BootService::class,
];
这个$this->intializers我们可以看到 它包含三部分内容 字面意思是对它们三者进行初始化
这三者的细节后面我们会再详细了解 目前需要先捋清楚这个加载流程
上面这10个步骤 就是run()方法里面第一句$this->initialize() 初始化的内容
接下来

$request = $request ?? $this->app->make('request', [], true); $this->app->instance('request', $request); try { $response = $this->runWithRequest($request); } catch (Throwable $e) { $this->reportException($e); $response = $this->renderException($request, $e); } return $response;
系统又通过make函数创建了一个request对象
这个request对象 我们倒是相对比较熟悉
比如在控制器里面 我们会通过$request->params()来获取get或post过来的参数
所有的请求 都会通过request来进行一系列的封装和使用
好吧 必须得去了解下request了...想想这玩意就不会那么简单..既然这样. .那么下一章见吧