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不会运行。










