【thinkphp6源码分析四】 贯穿流程的Web应用管理类--Http类

前面讲了那么多 实际都只是在分析在分析入口文件index.php代码的第一句

$http = (new App())->http;

甚至连第一句都没完  只是分析了第一句的前半部分  下面我们看下第一句的后半部分  也就是http [tp6\vendor\topthink\framework\src\think\Http.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  也就是 think\http

  (7) 那么这里就相当于 $app->make("think\http")

(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;
    }
View Code

 

它的第一句$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     }
View Code

我们可以详细看下这个加载流程

(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);
            }
        }
    }
View Code

可以在看到

这里首先加载了 app目录下的common.php  --》这就是为什么common.php里面写的全局函数 我们可以直接使用的原因

然后加载了think目录下的helper函数 位置为\tp6\vendor\topthink\framework\src\helper.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;
View Code

系统又通过make函数创建了一个request对象

这个request对象 我们倒是相对比较熟悉

比如在控制器里面 我们会通过$request->params()来获取get或post过来的参数

所有的请求  都会通过request来进行一系列的封装和使用

好吧 必须得去了解下request了...想想这玩意就不会那么简单..既然这样. .那么下一章见吧

 

 




 



 

 

 

 

 
 


 

 

 

posted on 2021-03-18 20:57  转瞬千年  阅读(433)  评论(0编辑  收藏  举报

导航