资讯专栏INFORMATION COLUMN

Laravel核心解读--HTTP内核

chenjiang3 / 2029人阅读

摘要:终止程序终止中间件内核的方法会调用中间件的方法,调用完成后从请求进来到返回响应整个应用程序的生命周期就结束了。

Http Kernel

Http Kernel是Laravel中用来串联框架的各个核心组件来网络请求的,简单的说只要是通过public/index.php来启动框架的都会用到Http Kernel,而另外的类似通过artisan命令、计划任务、队列启动框架进行处理的都会用到Console Kernel, 今天我们先梳理一下Http Kernel做的事情。

内核绑定

既然Http Kernel是Laravel中用来串联框架的各个部分处理网络请求的,我们来看一下内核是怎么加载到Laravel中应用实例中来的,在public/index.php中我们就会看见首先就会通过bootstrap/app.php这个脚手架文件来初始化应用程序:

下面是 bootstrap/app.php 的代码,包含两个主要部分创建应用实例绑定内核至 APP 服务容器

singleton(
    IlluminateContractsHttpKernel::class,
    AppHttpKernel::class
);

$app->singleton(
    IlluminateContractsConsoleKernel::class,
    AppConsoleKernel::class
);

$app->singleton(
    IlluminateContractsDebugExceptionHandler::class,
    AppExceptionsHandler::class
);

return $app;

HTTP 内核继承自 IlluminateFoundationHttpKernel类,在 HTTP 内核中 内它定义了中间件相关数组, 中间件提供了一种方便的机制来过滤进入应用的 HTTP 请求和加工流出应用的HTTP响应。

 [
            AppHttpMiddlewareEncryptCookies::class,
            IlluminateCookieMiddlewareAddQueuedCookiesToResponse::class,
            IlluminateSessionMiddlewareStartSession::class,
            // IlluminateSessionMiddlewareAuthenticateSession::class,
            IlluminateViewMiddlewareShareErrorsFromSession::class,
            AppHttpMiddlewareVerifyCsrfToken::class,
            IlluminateRoutingMiddlewareSubstituteBindings::class,
        ],
        "api" => [
            "throttle:60,1",
            "bindings",
        ],
    ];
    /**
     * The application"s route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        "auth" => IlluminateAuthMiddlewareAuthenticate::class,
        "auth.basic" => IlluminateAuthMiddlewareAuthenticateWithBasicAuth::class,
        "bindings" => IlluminateRoutingMiddlewareSubstituteBindings::class,
        "can" => IlluminateAuthMiddlewareAuthorize::class,
        "guest" => AppHttpMiddlewareRedirectIfAuthenticated::class,
        "throttle" => IlluminateRoutingMiddlewareThrottleRequests::class,
    ];
}

在其父类 「IlluminateFoundationHttpKernel」 内部定义了属性名为 「bootstrappers」 的 引导程序 数组:

protected $bootstrappers = [
    IlluminateFoundationBootstrapLoadEnvironmentVariables::class,
    IlluminateFoundationBootstrapLoadConfiguration::class,
    IlluminateFoundationBootstrapHandleExceptions::class,
    IlluminateFoundationBootstrapRegisterFacades::class,
    IlluminateFoundationBootstrapRegisterProviders::class,
    IlluminateFoundationBootstrapBootProviders::class,
];

引导程序组中 包括完成环境检测、配置加载、异常处理、Facades 注册、服务提供者注册、启动服务这六个引导程序。

有关中间件和引导程序相关内容的讲解可以浏览我们之前相关章节的内容。

应用解析内核

在将应用初始化阶段将Http内核绑定至应用的服务容器后,紧接着在public/index.php中我们可以看到使用了服务容器的make方法将Http内核实例解析了出来:

$kernel = $app->make(IlluminateContractsHttpKernel::class);

在实例化内核时,将在 HTTP 内核中定义的中间件注册到了 路由器,注册完后就可以在实际处理 HTTP 请求前调用路由上应用的中间件实现过滤请求的目的:

namespace IlluminateFoundationHttp;
...
class Kernel implements KernelContract
{
    /**
     * Create a new HTTP kernel instance.
     *
     * @param  IlluminateContractsFoundationApplication  $app
     * @param  IlluminateRoutingRouter  $router
     * @return void
     */
    public function __construct(Application $app, Router $router)
    {
        $this->app = $app;
        $this->router = $router;

        $router->middlewarePriority = $this->middlewarePriority;

        foreach ($this->middlewareGroups as $key => $middleware) {
            $router->middlewareGroup($key, $middleware);
        }
        
        foreach ($this->routeMiddleware as $key => $middleware) {
            $router->aliasMiddleware($key, $middleware);
        }
    }
}

namespace Illuminate/Routing;
class Router implements RegistrarContract, BindingRegistrar
{
    /**
     * Register a group of middleware.
     *
     * @param  string  $name
     * @param  array  $middleware
     * @return $this
     */
    public function middlewareGroup($name, array $middleware)
    {
        $this->middlewareGroups[$name] = $middleware;

        return $this;
    }
    
    /**
     * Register a short-hand name for a middleware.
     *
     * @param  string  $name
     * @param  string  $class
     * @return $this
     */
    public function aliasMiddleware($name, $class)
    {
        $this->middleware[$name] = $class;

        return $this;
    }
}
处理HTTP请求

通过服务解析完成Http内核实例的创建后就可以用HTTP内核实例来处理HTTP请求了

//public/index.php
$response = $kernel->handle(
    $request = IlluminateHttpRequest::capture()
);

在处理请求之前会先通过IlluminateHttpRequestcapture() 方法以进入应用的HTTP请求的信息为基础创建出一个 Laravel Request请求实例,在后续应用剩余的生命周期中Request请求实例就是对本次HTTP请求的抽象,关于Laravel Request请求实例的讲解可以参考以前的章节。

将HTTP请求抽象成Laravel Request请求实例后,请求实例会被传导进入到HTTP内核的handle方法内部,请求的处理就是由handle方法来完成的。

namespace IlluminateFoundationHttp;

class Kernel implements KernelContract
{
    /**
     * Handle an incoming HTTP request.
     *
     * @param  IlluminateHttpRequest  $request
     * @return IlluminateHttpResponse
     */
    public function handle($request)
    {
        try {
            $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 EventsRequestHandled($request, $response)
        );

        return $response;
    }
}

handle 方法接收一个请求对象,并最终生成一个响应对象。其实handle方法我们已经很熟悉了在讲解很多模块的时候都是以它为出发点逐步深入到模块的内部去讲解模块内的逻辑的,其中sendRequestThroughRouter方法在服务提供者和中间件都提到过,它会加载在内核中定义的引导程序来引导启动应用然后会将使用Pipeline对象传输HTTP请求对象流经框架中定义的HTTP中间件们和路由中间件们来完成过滤请求最终将请求传递给处理程序(控制器方法或者路由中的闭包)由处理程序返回相应的响应。关于handle方法的注解我直接引用以前章节的讲解放在这里,具体更详细的分析具体是如何引导启动应用以及如何将传输流经各个中间件并到达处理程序的内容请查看服务提供器、中间件还有路由这三个章节。

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

    Facade::clearResolvedInstance("request");

    $this->bootstrap();

    return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
}
    
/*引导启动Laravel应用程序
1. DetectEnvironment  检查环境
2. LoadConfiguration  加载应用配置
3. ConfigureLogging   配置日至
4. HandleException    注册异常处理的Handler
5. RegisterFacades    注册Facades 
6. RegisterProviders  注册Providers 
7. BootProviders      启动Providers
*/
public function bootstrap()
{
    if (! $this->app->hasBeenBootstrapped()) {
    /**依次执行$bootstrappers中每一个bootstrapper的bootstrap()函数
        $bootstrappers = [
             "IlluminateFoundationBootstrapDetectEnvironment",
             "IlluminateFoundationBootstrapLoadConfiguration",
             "IlluminateFoundationBootstrapConfigureLogging",
             "IlluminateFoundationBootstrapHandleExceptions",
             "IlluminateFoundationBootstrapRegisterFacades",
             "IlluminateFoundationBootstrapRegisterProviders",
             "IlluminateFoundationBootstrapBootProviders",
            ];*/
            $this->app->bootstrapWith($this->bootstrappers());
    }
}
发送响应

经过上面的几个阶段后我们最终拿到了要返回的响应,接下来就是发送响应了。

//public/index.php
$response = $kernel->handle(
    $request = IlluminateHttpRequest::capture()
);

// 发送响应
$response->send();

发送响应由 IlluminateHttpResponsesend()方法完成父类其定义在父类SymfonyComponentHttpFoundationResponse中。

public function send()
{
    $this->sendHeaders();// 发送响应头部信息
    $this->sendContent();// 发送报文主题

    if (function_exists("fastcgi_finish_request")) {
        fastcgi_finish_request();
    } elseif (!in_array(PHP_SAPI, array("cli", "phpdbg"), true)) {
        static::closeOutputBuffers(0, true);
    }
    return $this;
}

关于Response对象的详细分析可以参看我们之前讲解Laravel Response对象的章节。

终止应用程序

响应发送后,HTTP内核会调用terminable中间件做一些后续的处理工作。比如,Laravel 内置的「session」中间件会在响应发送到浏览器之后将会话数据写入存储器中。

// public/index.php
// 终止程序
$kernel->terminate($request, $response);
//IlluminateFoundationHttpKernel
public function terminate($request, $response)
{
    $this->terminateMiddleware($request, $response);
    $this->app->terminate();
}

// 终止中间件
protected function terminateMiddleware($request, $response)
{
    $middlewares = $this->app->shouldSkipMiddleware() ? [] : array_merge(
        $this->gatherRouteMiddleware($request),
        $this->middleware
    );
    foreach ($middlewares as $middleware) {
        if (! is_string($middleware)) {
            continue;
        }
        list($name, $parameters) = $this->parseMiddleware($middleware);
        $instance = $this->app->make($name);
        if (method_exists($instance, "terminate")) {
            $instance->terminate($request, $response);
        }
    }
}

Http内核的terminate方法会调用teminable中间件的terminate方法,调用完成后从HTTP请求进来到返回响应整个应用程序的生命周期就结束了。

总结

本节介绍的HTTP内核起到的主要是串联作用,其中设计到的初始化应用、引导应用、将HTTP请求抽象成Request对象、传递Request对象通过中间件到达处理程序生成响应以及响应发送给客户端。这些东西在之前的章节里都有讲过,并没有什么新的东西,希望通过这篇文章能让大家把之前文章里讲到的每个点串成一条线,这样对Laravel整体是怎么工作的会有更清晰的概念。

本文已经收录在系列文章Laravel源码学习里。

也欢迎关注我的公众号 网管叨bi叨 ,最近正在筹备准备分享一些日常工作里学到和总结的技术知识,也会分享一些见闻和学习英语的方法。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/29622.html

相关文章

  • Laravel核心解读--Console内核

    摘要:其中设置请求是唯一区别于内核的一个引导程序。和命令行脚本的规范一样,如果执行命令任务程序成功会返回抛出异常退出则返回。严格遵循了面向对象程序设计的原则。 Console内核 上一篇文章我们介绍了Laravel的HTTP内核,详细概述了网络请求从进入应用到应用处理完请求返回HTTP响应整个生命周期中HTTP内核是如何调动Laravel各个核心组件来完成任务的。除了处理HTTP请求一个健壮...

    Barry_Ng 评论0 收藏0
  • Laravel核心解读--完结篇

    摘要:过去一年时间写了多篇文章来探讨了我认为的框架最核心部分的设计思路代码实现。为了大家阅读方便,我把这些源码学习的文章汇总到这里。数据库算法和数据结构这些都是编程的内功,只有内功深厚了才能解决遇到的复杂问题。 过去一年时间写了20多篇文章来探讨了我认为的Larave框架最核心部分的设计思路、代码实现。通过更新文章自己在软件设计、文字表达方面都有所提高,在刚开始决定写Laravel源码分析地...

    laoLiueizo 评论0 收藏0
  • Laravel核心解读--Contracts契约

    摘要:的契约是一组定义框架提供的核心服务的接口,例如我们在介绍用户认证的章节中到的用户看守器契约和用户提供器契约以及框架自带的模型所实现的契约。接口与团队开发当你的团队在开发大型应用时,不同的部分有着不同的开发速度。 Contracts Laravel 的契约是一组定义框架提供的核心服务的接口, 例如我们在介绍用户认证的章节中到的用户看守器契约IllumninateContractsAuth...

    Prasanta 评论0 收藏0
  • Laravel核心解读 -- Response

    摘要:设置生成对象后就要执行对象的方法了,该方法定义在类中,其主要目的是对进行微调使其能够遵从协议。最后会把完整的响应发送给客户端。本文已经收录在系列文章源码学习里,欢迎访问阅读。 Response 前面两节我们分别讲了Laravel的控制器和Request对象,在讲Request对象的那一节我们看了Request对象是如何被创建出来的以及它支持的方法都定义在哪里,讲控制器时我们详细地描述了...

    TigerChain 评论0 收藏0
  • Laravel核心解读 -- Request

    摘要:根据提供的超级全局数组来创建实例上面的代码有一处需要额外解释一下,自开始内建的可以通过命令行解释器来启动,例如但是内建有一个是将和这两个请求首部存储到了和中,为了统一内建服务器和真正的中的请求首部字段所以在这里做了特殊处理。 Request 很多框架都会将来自客户端的请求抽象成类方便应用程序使用,在Laravel中也不例外。IlluminateHttpRequest类在Laravel框...

    K_B_Z 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<