>  기사  >  PHP 프레임워크  >  Laravel의 HTTP 커널에 대한 자세한 분석

Laravel의 HTTP 커널에 대한 자세한 분석

不言
不言앞으로
2018-11-12 14:06:384115검색

이 글은 Laravel의 HTTP 코어에 대한 자세한 분석을 제공합니다. 이는 특정 참조 가치가 있습니다. 도움이 필요한 친구들이 참고할 수 있기를 바랍니다.

Http Kernel

Http Kernel은 Laravel에서 프레임워크의 핵심 구성 요소를 연결하여 네트워크 요청을 하는 데 사용됩니다. 간단히 말해서 public/index.php를 통해 프레임워크가 시작되는 한 사용됩니다. Http 커널과 artisan 명령을 통한 기타 처리, 예약된 작업 및 대기열 시작 프레임워크는 콘솔 커널을 사용합니다. 오늘은 먼저 Http 커널이 수행하는 작업을 정리하겠습니다. public/index.php来启动框架的都会用到Http Kernel,而另外的类似通过artisan命令、计划任务、队列启动框架进行处理的都会用到Console Kernel, 今天我们先梳理一下Http Kernel做的事情。

内核绑定

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

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

<?php
// 第一部分: 创建应用实例
$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.&#39;/../&#39;)
);

// 第二部分: 完成内核绑定
$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;

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

<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
    /**
     * The application&#39;s global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array
     */
    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \App\Http\Middleware\TrustProxies::class,
    ];
    /**
     * The application&#39;s route middleware groups.
     *
     * @var array
     */
    protected $middlewareGroups = [
        &#39;web&#39; => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
        'api' => [
            'throttle:60,1',
            'bindings',
        ],
    ];
    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used inpidually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    ];
}

在其父类 「IlluminateFoundationHttpKernel」 内部定义了属性名为 「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,
];

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

应用解析内核

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

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

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

namespace Illuminate\Foundation\Http;
...
class Kernel implements KernelContract
{
    /**
     * Create a new HTTP kernel instance.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @param  \Illuminate\Routing\Router  $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 = Illuminate\Http\Request::capture()
);

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

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

namespace Illuminate\Foundation\Http;

class Kernel implements KernelContract
{
    /**
     * Handle an incoming HTTP request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    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 Events\RequestHandled($request, $response)
        );
        return $response;
    }
}

handle 方法接收一个请求对象,并最终生成一个响应对象。其实handle方法我们已经很熟悉了在讲解很多模块的时候都是以它为出发点逐步深入到模块的内部去讲解模块内的逻辑的,其中sendRequestThroughRouter方法在服务提供者和中间件都提到过,它会加载在内核中定义的引导程序来引导启动应用然后会将使用Pipeline对象传输HTTP请求对象流经框架中定义的HTTP中间件们和路由中间件们来完成过滤请求最终将请求传递给处理程序(控制器方法或者路由中的闭包)由处理程序返回相应的响应。

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 = [
             'Illuminate\Foundation\Bootstrap\DetectEnvironment',
             'Illuminate\Foundation\Bootstrap\LoadConfiguration',
             'Illuminate\Foundation\Bootstrap\ConfigureLogging',
             'Illuminate\Foundation\Bootstrap\HandleExceptions',
             'Illuminate\Foundation\Bootstrap\RegisterFacades',
             'Illuminate\Foundation\Bootstrap\RegisterProviders',
             'Illuminate\Foundation\Bootstrap\BootProviders',
            ];*/
            $this->app->bootstrapWith($this->bootstrappers());
    }
}

发送响应

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

//public/index.php
$response = $kernel->handle(
    $request = Illuminate\Http\Request::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);
//Illuminate\Foundation\Http\Kernel
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

커널 바인딩

Laravel에서는 네트워크 요청을 처리하기 위해 프레임워크의 다양한 부분을 연결하는 데 HTTP 커널이 사용되므로, Laravel의 public/index.php에서 애플리케이션 인스턴스에 커널이 로드되는 방식을 살펴보겠습니다. 먼저 스캐폴딩 파일 bootstrap/app.php를 통해 애플리케이션이 초기화되는 것을 볼 수 있습니다.

다음은 bootstrap/app.php의 코드입니다. 여기에는 애플리케이션 인스턴스를 생성하고 커널을 APP에 바인딩하는 두 가지 주요 부분이 포함되어 있습니다. 서비스 컨테이너🎜rrreee 🎜HTTP 커널은 IlluminateFoundationHttpKernel 클래스에서 상속됩니다. HTTP 커널에서는 미들웨어 관련 배열을 정의합니다. 미들웨어는 애플리케이션에 들어오는 HTTP 요청을 필터링하고 애플리케이션에서 HTTP 응답을 처리하는 편리한 메커니즘을 제공합니다. 🎜rrreee🎜상위 클래스 "IlluminateFoundationHttpKernel"에서는 속성 이름이 "bootstrappers"인 부트스트랩 배열이 정의됩니다. 🎜rrreee🎜부트스트랩 그룹에는 환경 감지 완료, 구성 로딩, 예외 처리, Facades 등록, 서비스 공급자 등록, 시작이 포함됩니다. 이 6개의 부트로더에 대한 서비스입니다. 🎜🎜애플리케이션 파싱 커널🎜🎜애플리케이션 초기화 단계에서 Http 커널을 애플리케이션의 서비스 컨테이너에 바인딩한 후 public/index.php에서 서비스 컨테이너 Out의 make 메소드를 사용하여 Http 커널 인스턴스가 파싱되는 것을 볼 수 있습니다. 🎜 rrreee🎜커널을 인스턴스화할 때 HTTP 커널에 정의된 미들웨어가 라우터에 등록됩니다. 등록 후 경로에 적용된 미들웨어를 호출하여 실제로 HTTP 요청을 처리하기 전에 요청을 필터링할 수 있습니다. 🎜rrreee 🎜HTTP 요청 처리🎜 🎜서비스 파싱을 통해 HTTP 코어 인스턴스 생성을 완료한 후 HTTP 코어 인스턴스를 사용하여 HTTP 요청을 처리할 수 있습니다🎜rrreee🎜요청을 처리하기 전에 먼저 IlluminateHttpRequest의 Capture() 메서드를 통해 애플리케이션의 HTTP 요청을 입력합니다. Laravel 요청 인스턴스는 후속 애플리케이션의 남은 수명 주기에서 HTTP 요청을 Laravel 요청 요청 인스턴스로 추상화한 후 생성됩니다. HTTP 커널의 핸들 메소드 내에서 요청 처리는 핸들 메소드에 의해 완료됩니다. 🎜rrreee🎜handle 메소드는 요청 객체를 수신하고 최종적으로 응답 객체를 생성합니다. 사실, 우리는 이미 많은 모듈을 설명할 때 핸들 메소드를 시작점으로 사용하여 모듈 내의 로직을 설명하기 위해 점차적으로 모듈에 대해 설명합니다. 서비스 공급자와 미들웨어 모두 커널에 정의된 부팅 프로그램이 로드되어 애플리케이션을 부팅한 다음 파이프라인 개체를 사용하여 HTTP 요청 개체를 전송하고 프레임워크에 정의된 HTTP 미들웨어 및 라우팅 미들웨어를 통해 흐름을 완료합니다. 요청을 필터링하고 마지막으로 핸들러가 반환한 해당 응답과 함께 요청을 핸들러(컨트롤러 메서드 또는 경로의 클로저)에 전달합니다. 🎜rrreee🎜응답 보내기🎜🎜위의 단계를 거쳐 드디어 응답을 돌려받을 수 있게 되었고, 다음 단계는 응답을 보내는 것입니다. 🎜rrreee🎜응답 보내기는 상위 클래스 SymfonyComponentHttpFoundationResponse에 정의된 IlluminateHttpResponsesend() 메서드에 의해 완료됩니다. 🎜rrreee🎜Response 객체에 대한 자세한 분석은 Laravel Response 객체를 설명하는 이전 장을 참조하세요. 🎜🎜애플리케이션 종료🎜🎜응답이 전송된 후 HTTP 커널은 종료 가능 미들웨어를 호출하여 후속 처리를 수행합니다. 예를 들어, Laravel에 내장된 "세션" 미들웨어는 응답이 브라우저로 전송된 후 세션 데이터를 메모리에 기록합니다. 🎜rrreeerrreee🎜Http 커널의 terminate 메서드는 호출이 완료된 후 terminate 미들웨어의 terminate 메서드를 호출합니다. HTTP 요청에서 반환 응답으로 응답합니다. 프로그램의 수명 주기가 끝났습니다. 🎜🎜요약🎜🎜이 섹션에서 소개하는 HTTP 커널은 주로 일련의 역할을 합니다. 애플리케이션을 초기화하고, 애플리케이션을 안내하고, HTTP 요청을 Request 객체로 추상화하고, Request 객체를 미들웨어에 전달하고, 핸들러에 도달하고, 응답을 생성하고, Sent에 응답하도록 설계되었습니다. 클라이언트에게. 🎜

위 내용은 Laravel의 HTTP 커널에 대한 자세한 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제