Lumen 在處理跟 Request 相關的程式,都放在 RoutesRequests 這個 trait 裡,包括今天要看的 dispatch()
。
public function dispatch($request = null) { list($method, $pathInfo) = $this->parseIncomingRequest($request);
try { $this->boot();
return $this->sendThroughPipeline($this->middleware, function () use ($method, $pathInfo) { // 取得所有 route 並看看對應的 method + pathInfo 會不會找得到 if (isset($this->router->getRoutes()[$method.$pathInfo])) { // 找得到就處理它 return $this->handleFoundRoute([true, $this->router->getRoutes()[$method.$pathInfo]['action'], []]); }
return $this->handleDispatcherResponse( $this->createDispatcher()->dispatch($method, $pathInfo) ); }); } catch (Exception $e) { return $this->prepareResponse($this->sendExceptionToHandler($e)); } catch (Throwable $e) { return $this->prepareResponse($this->sendExceptionToHandler($e)); } }
|
這段程式碼,除了 FastRoute 外,其他都是之前有分析過的橋段,如 boot()
裡,會對所有 [ServiceProvider][Day 05] 呼叫 boot()
;或是 [Pipeline][Day 07] 操作等。
這段程式碼,其中有幾個地方是我們可以再深入追下去的,如:
parseIncomingRequest()
如何解析出 method 或 pathInfo,以及後面為何 $this->router->getRoutes()
有可能會找不到
handleFoundRoute()
如何處理找到的 route
createDispatcher()
如何產生 FastRoute,以及 handleDispatcherResponse()
如何使用
sendExceptionToHandler()
如何產生
prepareResponse()
如何建置 Response
parseIncomingRequest()
protected function parseIncomingRequest($request) { if (! $request) { $request = LumenRequest::capture(); }
$this->instance(Request::class, $this->prepareRequest($request));
return [$request->getMethod(), '/'.trim($request->getPathInfo(), '/')]; }
protected function prepareRequest(SymfonyRequest $request) { if (! $request instanceof Request) { $request = Request::createFromBase($request); }
$request->setUserResolver(function ($guard = null) { return $this->make('auth')->guard($guard)->user(); })->setRouteResolver(function () { return $this->currentRoute; });
return $request; }
|
從原始碼可以知道,從 Request 即可拿到很多資訊了。
handleFoundRoute()
有了 $routeInfo
,就能拿來做處理,它的長像如下:
$routeInfo = [true, $this->router->getRoutes()[$method.$pathInfo]['action'], []]
|
而 handleFoundRoute()
的原始碼:
protected function handleFoundRoute($routeInfo) { $this->currentRoute = $routeInfo;
$this['request']->setRouteResolver(function () { return $this->currentRoute; });
$action = $routeInfo[1];
if (isset($action['middleware'])) { $middleware = $this->gatherMiddlewareClassNames($action['middleware']);
return $this->prepareResponse($this->sendThroughPipeline($middleware, function () { return $this->callActionOnArrayBasedRoute($this['request']->route()); })); }
return $this->prepareResponse( $this->callActionOnArrayBasedRoute($routeInfo) ); }
|
這裡使用另一組 Pipeline 的方法與 Laravel 是一樣的,可以參考[解析 Middleware 的實作細節][Day 20]。
而最後實際呼叫的方法寫在 callActionOnArrayBasedRoute()
裡。
protected function callActionOnArrayBasedRoute($routeInfo) { $action = $routeInfo[1];
if (isset($action['uses'])) { return $this->prepareResponse($this->callControllerAction($routeInfo)); }
foreach ($action as $value) { if ($value instanceof Closure) { $closure = $value->bindTo(new RoutingClosure); break; } }
try { return $this->prepareResponse($this->call($closure, $routeInfo[2])); } catch (HttpResponseException $e) { return $e->getResponse(); } }
|
今天先告一段落,明天繼續看剩下三個方法。