Home  >  Article  >  PHP Framework  >  Detailed analysis of HTTP kernel in Laravel

Detailed analysis of HTTP kernel in Laravel

不言
不言forward
2018-11-12 14:06:384071browse

This article brings you a detailed analysis of the HTTP core in Laravel. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

Http Kernel

Http Kernel is used in Laravel to connect the core components of the framework to make network requests. Simply put, as long as it is through public/index .php will use the Http Kernel to start the framework, and other processes such as artisan commands, scheduled tasks, and queue startup frameworks will use the Console Kernel. Today we will sort out Http What Kernel does.

Kernel binding

Since Http Kernel is used in Laravel to connect various parts of the framework to process network requests, let's take a look at how the kernel is loaded into the application instance in Laravel. In public/index.php we will see that the application will be initialized through the scaffolding file bootstrap/app.php first:

The following is the code of bootstrap/app.php, which contains two main parts to create Application instance and binding the kernel to the APP service container

<?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 kernel inherits from the IlluminateFoundationHttpKernel class. It defines a middleware-related array in the HTTP kernel. Middleware provides a convenient mechanism to filter incoming applications. HTTP requests and processing of HTTP responses out of the application.

<?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,
    ];
}

In its parent class "IlluminateFoundationHttpKernel", a bootstrap array with the attribute name "bootstrappers" is defined internally:

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,
];

The bootstrap group includes completion of environment detection, configuration loading, exception handling, Facades registration, service provider registration, and service startup are the six bootstrap procedures.

Application parsing core

After binding the Http core to the application's service container during the application initialization phase, we can see the use of the service container in public/index.php The make method parses out the HTTP kernel instance:

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

When instantiating the kernel, the middleware defined in the HTTP kernel is registered to the router. After registration, the route can be called before actually processing the HTTP request. The application middleware implements the purpose of filtering requests:

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;
    }
}

Processing HTTP requests

After completing the creation of the HTTP core instance through service resolution, you can use the HTTP core instance to process HTTP requests

//public/index.php
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

Before processing the request, a Laravel Request request instance will be created based on the information of the HTTP request entering the application through the capture() method of Illuminate\Http\Request. The Request request instance will be used in the remaining life cycle of the subsequent application. It is the abstraction of this HTTP request

After abstracting the HTTP request into a Laravel Request request instance, the request instance will be transmitted into the handle method of the HTTP kernel, and the request processing is completed by the handle method.

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;
    }
}

The handle method receives a request object and finally generates a response object. In fact, we are already very familiar with the handle method. When explaining many modules, we use it as a starting point to gradually go deep into the module to explain the logic within the module. Among them, the sendRequestThroughRouter method has been mentioned in both service providers and middleware. The boot program defined in the kernel will be loaded to boot the application, and then the Pipeline object will be used to transmit the HTTP request object and flow through the HTTP middleware and routing middleware defined in the framework to complete the filtering of the request and finally pass the request to the handler ( A controller method or a closure in a route) with the corresponding response returned by the handler.

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());
    }
}

Send response

After going through the above stages, we finally got the response to be returned, and the next step is to send the response.

//public/index.php
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

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

Sending a response is completed by the send() method of the Illuminate\Http\Response parent class which is defined in the parent class Symfony\Component\HttpFoundation\Response middle.

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;
}

For detailed analysis of the Response object, please refer to our previous chapter explaining the Laravel Response object.

Terminate the application

After the response is sent, the HTTP kernel will call the terminablemiddleware to do some subsequent processing. For example, Laravel's built-in "session" middleware writes session data to memory after the response is sent to the browser.

// 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);
        }
    }
}

The terminate method of the Http kernel will call the terminate method of the teminable middleware. After the call is completed, the entire HTTP request comes in and returns the response. The application life cycle is over.

Summary

The HTTP kernel introduced in this section mainly plays a serial role. It is designed to initialize applications, guide applications, abstract HTTP requests into Request objects, and pass Request objects through the middle The file arrival handler generates a response and the response is sent to the client.

The above is the detailed content of Detailed analysis of HTTP kernel in Laravel. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:segmentfault.com. If there is any infringement, please contact admin@php.cn delete