本項のテーマ & 前置き
Laravel5.6のコードを読んでいきます。
LaravelのHttpKernelでミドルウェアとコントローラが実行されている箇所をつぶさに見ていきます。本項は後編ですので、途中からです。前編を読んでる前提で話を進めていきますので、まだの方はまずはそちらをどうぞ(宣伝)
Laravelコードリーディング - Pipelineを使ったミドルウェアとコントローラーの実行箇所 前編
Let's コードリーディング!!
まずはあらすじです。
前編はPipelineクラスのthenメソッドの引数の正体を追っていく途中で終わっています。
protected function sendRequestThroughRouter($request)
{
~~~ 略
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());
}
protected function dispatchToRouter()
{
return function ($request) {
$this->app->instance('request', $request);
return $this->router->dispatch($request);
};
}
dispatchToRouterメソッドの$this->router
というのは、このKernelクラスにコンストラクタインジェクションされた依存オブジェクトで、Illuminate\Routing\Routerクラスです。
前編ではここまででした。後編ではこのRouterクラスからはじめましょう。
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));
}
findRouteメソッドは $request
からIlluminate\Routing\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)
);
}
prepareResponseメソッドはIlluminate\Http\Response(もしくはIlluminate\Http\JsonResponse)を作ります。引数になっているrunRouteWithinStackメソッドも追いましょう。
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()
);
});
}
なんか見慣れた光景が広がっています。LaravelはここでもClosure onionを作っているようです。
ここでもミドルウェアを実行しているようですが、ここで使われているのはApp\Http\Kernelに定義したmiddlewareGroupsです。デフォルトだとwebミドルウェアグループがここで展開され、Closure onionに組み込まれます。
Closure onionの中心にして最奥で実行されるのは$route->run()
です。$route
はfindRouteメソッドでインスタンス化したIlluminate\Routing\Routeクラスになります。
Routeクラス
さっそくrunメソッドをのぞいて見ましょう。
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();
}
}
ついにコントローラを実行しているらしいコードが見つかりました!ですがここはまだ終着点ではありません。引き続き、runControllerメソッドを確認してみましょう。
protected function runController()
{
return $this->controllerDispatcher()->dispatch(
$this, $this->getController(), $this->getControllerMethod()
);
}
$this->controllerDispatcher()
の返り値はIlluminate\Routing\ControllerDispatcherクラスです。コンテナにあったらそちらのインスタンスを使っています。
getControllerメソッドはControllerクラスのインスタンスを、getControllerMethodメソッドでは実行されるメソッド名(文字列)をそれぞれ返します。ここでようやく待望のControllerのインスタンスが登場しました。
では、ControllerDispatcherクラスのdispatchメソッドを確認してみましょう。
ControllerDispatcherクラス
public function dispatch(Route $route, $controller, $method)
{
$parameters = $this->resolveClassMethodDependencies(
$route->parametersWithoutNulls(), $controller, $method
);
if (method_exists($controller, 'callAction')) {
return $controller->callAction($method, $parameters);
}
return $controller->{$method}(...array_values($parameters));
}
$controller
の$method
を確かに呼び出してます。ようやく目的地に到着しました!
終わりに
長かった(小並感)
間違えあったら指摘おなしゃーす。