概要
- laravelアプリケーションに何かしらのリクエストが送られコントローラーの処理が実行されるまでの旅路を追ってみた。
旅路
-
public/index.phpが実行され初期段階の処理が実行される。(autoloadやら)
-
下記のように
$kernel->handle()
が実行される。public/index.php$app = require_once __DIR__.'/../bootstrap/app.php'; $kernel = $app->make(Kernel::class); $response = $kernel->handle( $request = Request::capture() )->send();
-
handle()はインターフェースの先に実装がある。
- インターフェース
vendor/laravel/framework/src/Illuminate/Contracts/Http/Kernel.php
- 実装
vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php
- 実装しているクラスを継承しているクラス
app/Http/Kernel.php
- インターフェース
-
handle()関数の実装部分で下記が実行されている。
vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php$response = $this->sendRequestThroughRouter($request);
-
同クラス内のsendRequestThroughRouter()関数では下記のような処理が実行されている。
vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.phpreturn (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter());
-
上記メソッドチェーンのthen()の引数として
$this->dispatchToRouter()
が実行され、当該関数の中で下記が実行されている。vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.phpreturn $this->router->dispatch($request);
-
上記処理でdispatch()関数が実行される。
vendor/laravel/framework/src/Illuminate/Routing/Router.phpreturn $this->dispatchToRoute($request);
-
上記処理でdispatchToRoute()関数が実行される。
vendor/laravel/framework/src/Illuminate/Routing/Router.phpreturn $this->runRoute($request, $this->findRoute($request));
-
上記処理でrunRoute()関数の第二引数の値としてfindRoute()関数が実行される。ここではルート情報をfindして返している。
vendor/laravel/framework/src/Illuminate/Routing/Router.php/** * Find the route matching a given request. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Routing\Route */ protected function findRoute($request) { $this->events->dispatch(new Routing($request)); // イベントリスナーの呼び出し? $this->current = $route = $this->routes->match($request); // リクエスト情報から $route->setContainer($this->container); $this->container->instance(Route::class, $route); return $route; }
-
runRoute()関数が実行される。
vendor/laravel/framework/src/Illuminate/Routing/Router.php/** * Return the response for the given route. * * @param \Illuminate\Http\Request $request * @param \Illuminate\Routing\Route $route * @return \Symfony\Component\HttpFoundation\Response */ protected function runRoute(Request $request, Route $route) { $request->setRouteResolver(fn () => $route); $this->events->dispatch(new RouteMatched($route, $request)); return $this->prepareResponse($request, $this->runRouteWithinStack($route, $request) ); }
-
上記処理のprepareResponse()関数の第二引数でrunRouteWithinStack()関数が実行される。
vendor/laravel/framework/src/Illuminate/Routing/Router.php/** * Run the given route within a Stack "onion" instance. * * @param \Illuminate\Routing\Route $route * @param \Illuminate\Http\Request $request * @return mixed */ 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(fn ($request) => $this->prepareResponse( $request, $route->run() )); }
-
上記の処理でprepareResponse()関数の第二引数として$route->run()が実行される。
vendor/laravel/framework/src/Illuminate/Routing/Route.php/** * Run the route action and return the response. * * @return mixed */ 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(); } }
-
上記の処理でisControllerAction()関数が実行されルーティング先がコントローラーであるかどうかチェックされる。コントローラーだった場合runController()関数が実行される。
vendor/laravel/framework/src/Illuminate/Routing/Route.php/** * Run the route action and return the response. * * @return mixed * * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException */ protected function runController() { return $this->controllerDispatcher()->dispatch( $this, $this->getController(), $this->getControllerMethod() ); }
-
上記のdispatch()関数は第一引数にルート情報、第二引数にコントローラー、第三引数にコントローラーの関数名を受け取り下記下記を実行する。
- インターフェース
vendor/laravel/framework/src/Illuminate/Routing/Contracts/ControllerDispatcher.php
- 実装
vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php
vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php/** * Dispatch a request to a given controller and method. * * @param \Illuminate\Routing\Route $route * @param mixed $controller * @param string $method * @return mixed */ 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)); }
- インターフェース
-
上記の
method_exists($controller, 'callAction')
はユーザーが定義したコントローラーがApp\Http\Controllers\Controller
を継承している場合、更にそのControllerクラスがIlluminate\Routing\Controller
を継承しているはずなのでtrueになる。 -
callAction()関数が各引数を用いて実行される。こちらでコントローラーの関数が指定され引数を用いて実行されている。例えばコントローラーのfoo関数が実行される場合
$this->{$method}(...array_values($parameters))
が$this->foo([])
となり実行される。vendor/laravel/framework/src/Illuminate/Routing/Controller.php/** * Execute an action on the controller. * * @param string $method * @param array $parameters * @return \Symfony\Component\HttpFoundation\Response */ public function callAction($method, $parameters) { return $this->{$method}(...array_values($parameters)); }