LoginSignup
7
5

More than 5 years have passed since last update.

Pipelineを使ったミドルウェアとコントローラーの実行箇所 後編 - Laravelコードリーディング

Last updated at Posted at 2018-05-31

本項のテーマ & 前置き

Laravel5.6のコードを読んでいきます。
LaravelのHttpKernelでミドルウェアとコントローラが実行されている箇所をつぶさに見ていきます。本項は後編ですので、途中からです。前編を読んでる前提で話を進めていきますので、まだの方はまずはそちらをどうぞ(宣伝)

Laravelコードリーディング - Pipelineを使ったミドルウェアとコントローラーの実行箇所 前編

Let's コードリーディング!!

まずはあらすじです。
前編はPipelineクラスのthenメソッドの引数の正体を追っていく途中で終わっています。

vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php

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

vendor/laravel/framework/src/Illuminate/Routing/Router.php

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クラスのオブジェクトを作っているだけです。

vendor/laravel/framework/src/Illuminate/Routing/Router.php

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メソッドも追いましょう。

vendor/laravel/framework/src/Illuminate/Routing/Router.php

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メソッドをのぞいて見ましょう。

vendor/laravel/framework/src/Illuminate/Routing/Route.php

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メソッドを確認してみましょう。

vendor/laravel/framework/src/Illuminate/Routing/Route.php

protected function runController()
{   
    return $this->controllerDispatcher()->dispatch(
        $this, $this->getController(), $this->getControllerMethod()
    );  
}

$this->controllerDispatcher()の返り値はIlluminate\Routing\ControllerDispatcherクラスです。コンテナにあったらそちらのインスタンスを使っています。

getControllerメソッドはControllerクラスのインスタンスを、getControllerMethodメソッドでは実行されるメソッド名(文字列)をそれぞれ返します。ここでようやく待望のControllerのインスタンスが登場しました。

では、ControllerDispatcherクラスのdispatchメソッドを確認してみましょう。

ControllerDispatcherクラス

vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php

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を確かに呼び出してます。ようやく目的地に到着しました!

終わりに

長かった(小並感)
間違えあったら指摘おなしゃーす。

7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5