Laravel 请求生命周期 
                                                    
                        
                    
                    
  
                    
                    本文主要介绍了一个 Http 请求在 Laravel 中是怎样处理的。
public/index.php
所有 Laravel 程序均起始于 public/index.php 文件。
<?php
define('LARAVEL_START', microtime(true));
require __DIR__.'/../vendor/autoload.php';
$app = require_once __DIR__.'/../bootstrap/app.php';
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);
$response->send();
$kernel->terminate($request, $response);index 里的代码很简单,首先定义了一个常量 LARAVEL_START ,接着引入了 composer 库中的 autoloader。随后加载 Laravel 的 Application 实例。接着到了最重要的部分,通过 Http Kernel 处理请求,最后将响应进行返回。
处理请求
在这里我们具体关注 Http Kernel 是怎样处理请求的。在 Illuminate\Foundation\Http\Kernel 中我们看到有 handle 方法。具体如下:
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;
}在上面的代码中,首先我们对 Http 方法确保能够进行覆盖处理,具体的用途参考这里,接着 Laravel 将请求通过sendRequestThroughRouter 进行处理。
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());
}
在这里我们实例化 \Illuminate\Http\Request,然后引导相关的引导程序类,具体如下:
protected $bootstrappers = [
    \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class, // 加载env环境变量
    \Illuminate\Foundation\Bootstrap\LoadConfiguration::class, // 加载配置文件
    \Illuminate\Foundation\Bootstrap\HandleExceptions::class, // 异常处理
    \Illuminate\Foundation\Bootstrap\RegisterFacades::class, // 注册 Facades
    \Illuminate\Foundation\Bootstrap\RegisterProviders::class, // 注册 Providers
    \Illuminate\Foundation\Bootstrap\BootProviders::class, // 启动 Providers
];
public function bootstrap()
{
    if (! $this->app->hasBeenBootstrapped()) {
        $this->app->bootstrapWith($this->bootstrappers());
    }
}
protected function bootstrappers()
{
    return $this->bootstrappers;
}引导完毕之后,通过管道(pipeline)进行请求处理。关于管道的原理会在后面的文章中进行分析,这里你可以理解为循环使用中间件来处理请求。当所有的中间件处理完之后,交由dispatchToRouter进行下一步处理。
匹配路由
// Kernel.php
protected function dispatchToRouter()
{
    return function ($request) {
        $this->app->instance('request', $request);
        return $this->router->dispatch($request);
    };
}
// Illuminate\Routing\Router
public function dispatch(Request $request)
{
    $this->currentRequest = $request;
    return $this->dispatchToRoute($request);
}
public function dispatchToRoute(Request $request)
{
    return $this->runRoute($request, $this->findRoute($request));
}
protected function findRoute($request)
{
    $this->current = $route = $this->routes->match($request);
    $this->container->instance(Route::class, $route);
    return $route;
}
protected function runRoute(Request $request, Route $route)
{
    $request->setRouteResolver(function () use ($route) {
        return $route;
    });
    $this->events->dispatch(new Events\RouteMatched($route, $request));
    return $this->prepareResponse($request,
        $this->runRouteWithinStack($route, $request)
    );
}
protected function runRouteWithinStack(Route $route, Request $request)
{
    $shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
                            $this->container->make('middleware.disable') === true;
    $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route);
    return (new Pipeline($this->container))
                    ->send($request)
                    ->through($middleware)
                    ->then(function ($request) use ($route) {
                        return $this->prepareResponse(
                            $request, $route->run()
                        );
                    });
}
public function prepareResponse($request, $response)
{
    return static::toResponse($request, $response);
}
public static function toResponse($request, $response)
{
    if ($response instanceof Responsable) {
        $response = $response->toResponse($request);
    }
    if ($response instanceof PsrResponseInterface) {
        $response = (new HttpFoundationFactory)->createResponse($response);
    } elseif (! $response instanceof SymfonyResponse &&
               ($response instanceof Arrayable ||
                $response instanceof Jsonable ||
                $response instanceof ArrayObject ||
                $response instanceof JsonSerializable ||
                is_array($response))) {
        $response = new JsonResponse($response);
    } elseif (! $response instanceof SymfonyResponse) {
        $response = new Response($response);
    }
    if ($response->getStatusCode() === Response::HTTP_NOT_MODIFIED) {
        $response->setNotModified();
    }
    return $response->prepare($request);
}
// Illuminate\Routing\Route
public function run()
{
    $this->container = $this->container ?: new Container;
    try {
        if ($this->isControllerAction()) {
            return $this->runController();
        }
        return $this->runCallable();
    } catch (HttpResponseException $e) {
        return $e->getResponse();
    }
}在上面的代码中,我们首先要找到匹配的路由,即findRoute方法。其中 match 方法的大致逻辑为:
- 获取到当前程序中的所有路由地址
- 对比 uri,http 方法,scheme 和 host
- 返回符合条件的第一个路由
- 如果未找到抛出异常
如果找到了匹配的路由,触发路由匹配事件,随后在 runRouteWithinStack 中,我们继续使用管道处理。接着在 $route->run() 中返回结果(根据是控制器方法还是回调方法分别进行处理)。最后根据不同的响应类型和状态设置不同的 Header 和 请求结果。
至此 Laravel 的请求生命周期基本结束。以后的文章会继续研究 Laravel 的其他功能原理。
本作品采用《CC 协议》,转载必须注明作者和本文链接
 
           Epona 的个人博客
 Epona 的个人博客
         
             
                     
                     
           
           关于 LearnKu
                关于 LearnKu
               
                     
                     
                     粤公网安备 44030502004330号
 粤公网安备 44030502004330号 
 
推荐文章: