Laravel 源码学习笔记 4:kernel 实例解析 request 请求

上篇文章说到了从容器中解析出http的kernel实例,这篇文章讲kernel怎么处理请求。
在kernel中的handle

public function handle($request)
{
    try {
        //方法欺骗,csrf
        $request->enableHttpMethodParameterOverride();
        //发送请求,里面启动容器,经过路由中间件,管道
        $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;
}

其中,主要就是sendRequestThroughRouter方法

protected function sendRequestThroughRouter($request)
{
    //1.绑定请求对象到容器
    $this->app->instance('request', $request);
    //清除之前的request
    Facade::clearResolvedInstance('request');

    //3.启动容器 ,分别启动里面的 env config 异常 注册facede 注册provider 引导provider
    $this->bootstrap();

    //3.请求通过管道中间件,分发匹配路由
    return (new Pipeline($this->app))
                ->send($request)
                ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                ->then($this->dispatchToRouter());
}

这里做了三件事,如上图代码注释,启动容器这里要着重看下,点进bootstrap方法

public function bootstrap()
{
    if (! $this->app->hasBeenBootstrapped()) {
        $this->app->bootstrapWith($this->bootstrappers());
    }
}
protected $bootstrappers = [
    \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,//设置env文件
    \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,//配置文件信息读取,主要为了config方法
    \Illuminate\Foundation\Bootstrap\HandleExceptions::class,//异常错误级别的设置什么的
    \Illuminate\Foundation\Bootstrap\RegisterFacades::class,//注册别名,没太懂
    \Illuminate\Foundation\Bootstrap\RegisterProviders::class,//注册服务提供者
    \Illuminate\Foundation\Bootstrap\BootProviders::class,//启动引导服务提供者
];
Application.php 中
public function bootstrapWith(array $bootstrappers)
{
    $this->hasBeenBootstrapped = true;

    foreach ($bootstrappers as $bootstrapper) {
        $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);

        $this->make($bootstrapper)->bootstrap($this);

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

这个方法其实是继承自kernel接口的实现方法,这里的作用就是解析kernel类的bootstraper数组里的那些类,并运行他们的bootstrap方法,这里举个类的例子LoadEnvironmentVariables,从bootstrapWith方法中可以知道,解析出这个实例后运行bootstrap方法,看下

class LoadEnvironmentVariables
{
    /**
     * Bootstrap the given application.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public function bootstrap(Application $app)
    {
        if ($app->configurationIsCached()) {
            return;
        }
        $this->checkForSpecificEnvironmentFile($app);
        try {
            (new Dotenv($app->environmentPath(), $app->environmentFile()))->load();
        } catch (InvalidPathException $e) {
            //
        }
    }
    protected function checkForSpecificEnvironmentFile($app)
{
    //是否console请求
    if ($app->runningInConsole() && ($input = new ArgvInput)->hasParameterOption('--env')) {
        if ($this->setEnvironmentFilePath(
            $app, $app->environmentFile().'.'.$input->getParameterOption('--env')
        )) {
            return;
        }
    }
    if (! env('APP_ENV')) {
        return;
    }
    //设置app的env文件
    $this->setEnvironmentFilePath(
        $app, $app->environmentFile().'.'.env('APP_ENV')
    );
}
/**
 * Load a custom environment file.
 *
 * @param  \Illuminate\Contracts\Foundation\Application  $app
 * @param  string  $file
 * @return bool
 */
protected function setEnvironmentFilePath($app, $file)
{
    //设置app的env文件
    if (file_exists($app->environmentPath().'/'.$file)) {
        $app->loadEnvironmentFrom($file);

        return true;
    }
    return false;
}

可以看出,LoadEnvironmentVariables类的作用,就是设置好容器中environmentFile属性,这就完成了一个启动操作,bootstraper数组里的其他同理。
再来看bootstraper数组里的RegisterProviders引导,进入该类:

class RegisterProviders
{
    /**
     * Bootstrap the given application.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public function bootstrap(Application $app)
    {
        $app->registerConfiguredProviders();
    }
}

发现调用的是Application.php 的registerConfiguredProviders方法,根据方法名可以猜测这是注册config配置文件中的providers:

public function registerConfiguredProviders()
    {
        //从配置文件读取一堆的服务提供者
        $providers = Collection::make($this->config['app.providers'])
                        ->partition(function ($provider) {
                            return Str::startsWith($provider, 'Illuminate\\');
                        });
        $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);

//        dd($providers->collapse()->toArray());
        //获取到配置文件的服务提供者后,这里执行load,provider仓储类
        (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
                    ->load($providers->collapse()->toArray());
    }

读取完providers后,调用ProviderRepository的load方法

public function load(array $providers)
    {

        //缓存文件的内容
        $manifest = $this->loadManifest();

        // First we will load the service manifest, which contains information on all
        // service providers registered with the application and which services it
        // provides. This is used to know which services are "deferred" loaders.
        // 是否重新载入provider缓存文件
        if ($this->shouldRecompile($manifest, $providers)) {
            //重载后的的里面文件的数组信息
            $manifest = $this->compileManifest($providers);
        }
//        dd($manifest);
        // Next, we will register events to load the providers for each of the events
        // that it has requested. This allows the service provider to defer itself
        // while still getting automatically loaded when a certain event occurs.
        // 根据监听触发的延迟加载
        foreach ($manifest['when'] as $provider => $events) {
            $this->registerLoadEvents($provider, $events);
        }

        // We will go ahead and register all of the eagerly loaded providers with the
        // application so their services can be registered with the application as
        // a provided service. Then we will set the deferred service list on it..
        // eager立即加载的服务
        foreach ($manifest['eager'] as $provider) {
            $this->app->register($provider);
        }
        //把需要延迟加载的服务添加进app容器的deferredServices数组
        $this->app->addDeferredServices($manifest['deferred']);
    }

这里会对provider进行一个整理,如果有缓存的cache/services.php 文件会先去读,再去和传进来的provider对比,看是否进行更新,然后根据manifest数据的不同键名,对处在不同键名下的provider进行不同操作,比如在eager下会立即去注册该服务,在deferred下面是进行延迟加载,提升性能,而when下面的是监听,根据监听来触发注册deferred的服务(这个在后面会单独有文章来讲)。在这里laravel就会去注册一堆config配置里的provider,也就是为什么以前我们用扩展包的时候,会往config的provider数组加东西了(当然新的都不用了)。
$bootstrappers数组中的BootProviders引导也是一样,会对已经注册的provider执行他们的boot方法。
到这里,kernel的handle里的$this->bootstrap();就执行结束了,容器服务准备了,接下来

//请求通过管道中间件,分发匹配路由
return (new Pipeline($this->app))
            ->send($request)
            ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
            ->then($this->dispatchToRouter());

通过管道对象,让请求验证一下多个中间件,没问题后,再根据路由匹配,分发到匹配到的闭包或者控制器去执行业务代码,返回结果。

有什么不对或有疑问的地方请大佬们指正:)

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!