本文主要介绍了一个 Http 请求在 Laravel 中是怎样处理的。
public/index.php
所有 Laravel 程序均起始于 public/index.php
文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?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 方法。具体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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
进行处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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
,然后引导相关的引导程序类,具体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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, ];
public function bootstrap() { if (! $this->app->hasBeenBootstrapped()) { $this->app->bootstrapWith($this->bootstrappers()); } }
protected function bootstrappers() { return $this->bootstrappers; }
|
引导完毕之后,通过管道(pipeline)进行请求处理。关于管道的原理会在后面的文章中进行分析,这里你可以理解为循环使用中间件来处理请求。当所有的中间件处理完之后,交由dispatchToRouter
进行下一步处理。
匹配路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| protected function dispatchToRouter() { return function ($request) { $this->app->instance('request', $request);
return $this->router->dispatch($request); }; }
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); }
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 的其他功能原理。