1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

laravel リクエストが送られコントローラーが実行されるまでの旅路

Posted at

概要

  • laravelアプリケーションに何かしらのリクエストが送られコントローラーの処理が実行されるまでの旅路を追ってみた。

旅路

  1. public/index.phpが実行され初期段階の処理が実行される。(autoloadやら)

  2. 下記のように$kernel->handle()が実行される。

    public/index.php
    $app = require_once __DIR__.'/../bootstrap/app.php';
    
    $kernel = $app->make(Kernel::class);
    
    $response = $kernel->handle(
        $request = Request::capture()
    )->send();
    
  3. handle()はインターフェースの先に実装がある。

    • インターフェース vendor/laravel/framework/src/Illuminate/Contracts/Http/Kernel.php
    • 実装 vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php
    • 実装しているクラスを継承しているクラス app/Http/Kernel.php
  4. handle()関数の実装部分で下記が実行されている。

    vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php
    $response = $this->sendRequestThroughRouter($request);
    
  5. 同クラス内のsendRequestThroughRouter()関数では下記のような処理が実行されている。

    vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php
    return (new Pipeline($this->app))
                ->send($request)
                ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                ->then($this->dispatchToRouter());
    
  6. 上記メソッドチェーンのthen()の引数として$this->dispatchToRouter()が実行され、当該関数の中で下記が実行されている。

    vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php
    return $this->router->dispatch($request);
    
  7. 上記処理でdispatch()関数が実行される。

    vendor/laravel/framework/src/Illuminate/Routing/Router.php
    return $this->dispatchToRoute($request);
    
  8. 上記処理でdispatchToRoute()関数が実行される。

    vendor/laravel/framework/src/Illuminate/Routing/Router.php
    return $this->runRoute($request, $this->findRoute($request));
    
  9. 上記処理で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;
    }
    
  10. 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)
        );
    }
    
  11. 上記処理の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()
                        ));
    }
    
  12. 上記の処理で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();
        }
    }
    
  13. 上記の処理で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()
        );
    }
    
  14. 上記の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));
    }
    
  15. 上記のmethod_exists($controller, 'callAction')はユーザーが定義したコントローラーがApp\Http\Controllers\Controllerを継承している場合、更にそのControllerクラスがIlluminate\Routing\Controllerを継承しているはずなのでtrueになる。

  16. 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));
    }
    

参考文献

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?