Laravel5.5源码详解 -- Config 配置文件的加载

Laravel5.5源码详解 – Config 配置文件的加载细节

关于大框架的分析,网上已经有比较多的资料,那些资料大体上只告诉我们这个函数是干嘛的,那个函数是干嘛的,但具体如何走都没有介绍,所以我这里主要从细节看程序的具体流向。

首先从/public/index.php开始,程序正是从这里启动的。

$app = require_once __DIR__.'/../bootstrap/app.php';

这里,$app得到Bootstrap\app.php,其中Kernel类会被 绑定到容器container中。

<?php

$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

//----------Kernel就是这里被绑定的---------------------
$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);

$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);

$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

return $app;

随后,实现$Kernel,

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

Illuminate\Contracts\Http\Kernel只是一个接口,由其继承者Illuminate\Foundation\Http\Kernel实现。这里创建了,也正是这个类。

随后,

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

调用Kernel类的Handle函数,处理传送进来的request,代码和注释如下,

 public function handle($request)
    {
        try {
            $request->enableHttpMethodParameterOverride();

            //----------下面调用了sendRequestThroughRouter函数来发送request------------
            $response = $this->sendRequestThroughRouter($request);
        } catch (Exception $e) {
           $this->reportException($e);

            $response = $this->renderException($request, $e);
        } catch (Throwable $e) {
            $this->reportException($e = new FatalThrowableError($e));

            $response = $this->renderException($request, $e);
        }

        $this->app['events']->dispatch(
            new Events\RequestHandled($request, $response)
        );

        return $response;
    }

    protected function sendRequestThroughRouter($request)
    {
        $this->app->instance('request', $request);

        Facade::clearResolvedInstance('request');
        //------------这里开始调用bootrap函数配置参数。-----------------
        $this->bootstrap();

        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }


    public function bootstrap()
    {
        if (! $this->app->hasBeenBootstrapped()) {
            //------------直接调用app的bootstrapWith()-------------------
            $this->app->bootstrapWith($this->bootstrappers());
        }
    }

bootstrapWith会生成所需要的对象,其参数正是Illuminate\Foundation\Http\Kernel中的$bootstrappers中列出的类,第二条就是我们要关注的配置参数时要用到的类,

    protected $bootstrappers = [
        \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
        \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
        \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
        \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
        \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
        \Illuminate\Foundation\Bootstrap\BootProviders::class,
    ];    

$app中的配置函数bootstrapWith()函数的原型如下,

    public function bootstrapWith(array $bootstrappers)
    {
        $this->hasBeenBootstrapped = true;

        foreach ($bootstrappers as $bootstrapper) {
            $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);
            //------这里,在处理LoadConfiguration类时,会直接调用其中的bootstrap------
            $this->make($bootstrapper)->bootstrap($this);

            $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
        }
    }

上面我们看到,app会在生成LoadConfiguration对象后,直接调用其bootstrap()函数来进行相关配置。所以,最终的config 配置文件是由类 \Illuminate\Foundation\Bootstrap\LoadConfiguration::class 完成的,详细注释如下,

public function bootstrap(Application $app)
{
    $items = [];
    if (file_exists($cached = $app->getCachedConfigPath())) {
        //注意,这个$cached返回的是路径,
        //"D:\wamp64\www\laravel\larablog\bootstrap/cache/config.php"
        $items = require $cached;
        //items就相当于/bootstrap/cache/config.php里面的所有配置项
        $loadedFromCache = true;
    }
    //下面,new Repository($items)创建一个仓库,并把所有配置项作为参数传递进去,
    //然后绑定到$app->instances数组中的config上,说明config已经实例化。
    $app->instance('config', $config = new Repository($items));
    //此时,如果打印出$app,就会得到后面的那些内容,可以明显看到,
    //instances数组中添加了’config’ (已经刻意展开了该项)
    if (! isset($loadedFromCache)) {
        $this->loadConfigurationFiles($app, $config);
    }

   $app->detectEnvironment(function () use ($config) {
        return $config->get('app.env', 'production');
    });

    date_default_timezone_set($config->get('app.timezone', 'UTC'));

    mb_internal_encoding('UTF-8');
}

dd(#app)得到的结果,

Application {#2 ▼
  ...
  #instances: array:17 [▼
    ...
    "events" => Dispatcher {#26 ▶}
    "router" => Router {#25 ▶}
    "Illuminate\Contracts\Http\Kernel" => Kernel {#29 ▶}
    "request" => Request {#42 ▶}
    "config" => Repository {#24 ▶}
  ]
  ...
}

这个配置,在各个模块的理解中都要用到,比如在session的启动中getDefaultDriver()中拉出来用的配置。
当然,有兴趣的朋友可以继续研究loadConfigurationFiles。它主要是在缓存不存在时,才会被调用来加载配置文件。我在使用时,一般会通过php artisan config:cache命令来生成配置文件到bootstrap/cache/config.php,所以后面的loadConfigurationFiles不会运行。

posted @ 2017-12-15 15:39  SpaceVision  阅读(46)  评论(0编辑  收藏  举报