10
13

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 3 years have passed since last update.

laravelでweb.phpが読み込まれるまでの道のり

Posted at

はじめに

laravelではルーティングの定義をroutes/web.phpなどに記載しますが、どのようにそれが読み込まれてどこに情報が保持されるのかをソースコードを読んで追っていきたいと思います。

Routeファサード

Routeファサードで解決されるクラスIlluminate/Routing/Router.php

public function __construct(Dispatcher $events, Container $container = null)
{
    $this->events = $events;
    $this->routes = new RouteCollection;
    $this->container = $container ?: new Container;
}

routerのバインドを書いているサービスプロバイダーIlluminate/Routing/RoutingServiceProviderはlaravelのboot時に Illuminate/Foundation/Application.phpのコンストラクタで登録されている。

Let's Go

Illuminate/Foundation/Http/Kernel::sendRequestThroughRouter()内の$this->bootstrap()config/app.phpに記述されているサービスプロバイダーが登録 and 起動されます。

ルートに関わるサービスプロバイダーはApp\Providers\RouteServiceProviderで、このクラスはIlluminate/Foundation/Support/Providers/RouteServiceProviderを継承していて、$this->bootstrap()で継承元のboot()が呼ばれます。

public function boot()
{

    //...
    
    $this->loadRoutes();

    $this->app->booted(function () {
        $this->app['router']->getRoutes()->refreshNameLookups();
        $this->app['router']->getRoutes()->refreshActionLookups();
    });
    
}

$this->loadRoutes()app/Providers/RouteServiceProvider::map()をコールします。

public function map()
{
    $this->mapApiRoutes();

    $this->mapWebRoutes();
}

これらのメソッドでroutes/web.phproutes/api.phpを読み込んでいます。
(ルート定義を別ファイルに書きたい場合はここに追加すればできます)

protected function mapWebRoutes()
{
    Route::middleware('web')
        ->namespace($this->namespace)
        ->group(base_path('routes/web.php'));
}

RouteファサードはIlluminate/Routing/Routerで解決するので、Illuminate/Routing/Router::middleware()が呼ばれます。
しかし、Illuminate/Routing/Router::middleware()メソッドはないので、__call()が呼ばれます。

public function __call($method, $parameters)
{

    //...
    
    if ($method === 'middleware') {
        return (new RouteRegistrar($this))->attribute($method, is_array($parameters[0]) ? $parameters[0] : $parameters);
    }

    //...
}

Illuminate/Routing/RouteRegistrar::attribute('middleware', 'web')として実行されます。

public function attribute($key, $value)
{

    //...

    $this->attributes[Arr::get($this->aliases, $key, $key)] = $value;

    return $this;
}

$this->attributes['middleware'] = 'web'のように登録されます。

namespace()もほぼ同様なので割愛します。

->group(base_path('routes/web.php'))を見ていきます。

Illuminate/Routing/RouteRegistrar::group()が呼ばれています。

public function group($callback)
{
    $this->router->group($this->attributes, $callback);
}

先程登録した$this->attributesとパスをIlluminate/Routing/Router::group()に委譲しています。

public function group(array $attributes, $routes)
{
    $this->updateGroupStack($attributes);

    $this->loadRoutes($routes);

    array_pop($this->groupStack);
}

$this->updateGroupStack()$this->groupStackというプロパティに$attributesを格納しています。
プロパティの中身

いよいよ$this->loadRoutes()でルートを読み込んでいきます。

protected function loadRoutes($routes)
{
    (new RouteFileRegistrar($this))->register($routes);
    
}
public function register($routes)
{
    $router = $this->router;

    require $routes;
}

requireでbase_path('routes/web.php')を読み込んでいます。

ルート定義では基本的にRouteファサードを使っていると思いますが、$router変数にIlluminate/Routing/Routerを格納しているので$router->get()のように書くこともできるようです。

routes/web.phpの中身がどのように読み込まれていくか下記のroutes/web.phpを例に見ていきます。

Route::get('/', [HogeController::class, 'index']);
Route::post('/', [HogeController::class, 'create']);

まずはget()から見ていきます。

public function get($uri, $action = null)
{
    return $this->addRoute(['GET', 'HEAD'], $uri, $action);
}
public function addRoute($methods, $uri, $action)
{
    return $this->routes->add($this->createRoute($methods, $uri, $action));
}

$this->createRoute()がなにを返すのかを見ていきましょう。

protected function createRoute($methods, $uri, $action)
{
    
    //...

    $route = $this->newRoute(
        $methods, $this->prefix($uri), $action
    );;

    return $route;
}
protected function newRoute($methods, $uri, $action)
{
    return (new Route($methods, $uri, $action))
                ->setRouter($this)
                ->setContainer($this->container);
}

$this->createRoute()は最終的にIlluminate/Routing/Routeを返すようです。

コンストラクタは、

public function __construct($methods, $uri, $action)
{
    $this->uri = $uri;
    $this->methods = (array) $methods;
    $this->action = $this->parseAction($action);

    if (in_array('GET', $this->methods) && ! in_array('HEAD', $this->methods)) {
        $this->methods[] = 'HEAD';
    }

    if (isset($this->action['prefix'])) {
        $this->prefix($this->action['prefix']);
    }
}

Route::get('/', [HogeController::class, 'index'])の例だと、

$this->uri = '/' (prefixがあればprefix付き)
$this->method = ['GET', 'HEAD']
$this->action = ['uses' => 'HogeController@method', 'controller' => 'HogeController@method']
ルーティングの書き方

生成されたIlluminate/Routing/Routeクラスは$this->routes->add()Illuminate/Routing/RouteCollectionに配列として保存されます。

このようにしてルートファイルに定義されたルートが保存されていきます。

Route::post()methodが変わるだけでRoute::get()と変わらないので割愛します。

これでやっと$this->loadRoutes()が終わりapp/Providers/RouteServiceProvider::map()内の->group()に戻ってこれます。

public function group(array $attributes, $routes)
{
    $this->updateGroupStack($attributes);

    $this->loadRoutes($routes);

    array_pop($this->groupStack);
}

最後に用済みとなった$this->groupStackは消されて終わります。

このサイクルをデフォルトではroutes/web.phproutes/api.phpの2回行います。

参考

####クラスの関係

Illuminate/Routing/Router -> Routeファサードの実態。
Illuminate/Routing/Route -> 1つ1つのルートのuri, action, methodなどを保持。
Illuminate/Routing/RouteCollection -> Routeクラスを配列としてまとめて保持する。
Illuminate/Routing/RouteRegistrar.php -> map()で呼ばれる。prefix, nameなどを登録していく。

プロパティの中身

Array
(
    [0] => Array
        (
            [middleware] => Array
                (
                    [0] => web
                    [1] => auth:admin
                )

            [prefix] => admin
            [as] => admin.
            [namespace] => App\Http\Controllers
        )

    [1] => Array
        (
            [middleware] => Array
                (
                    [0] => web
                    [1] => auth:admin
                )

            [prefix] => admin/book
            [as] => admin.book.
            [namespace] => App\Http\Controllers
            [where] => Array
                (
                )

        )

)
Array
(
    [0] => Array
        (
            [middleware] => Array
                (
                    [0] => web
                    [1] => auth:user
                )

            [prefix] => user
            [as] => user.
            [namespace] => App\Http\Controllers
        )

    [1] => Array
        (
            [middleware] => Array
                (
                    [0] => web
                    [1] => auth:user
                )

            [prefix] => user/book
            [as] => user.book.
            [namespace] => App\Http\Controllers
            [where] => Array
                (
                )

        )

)

ルーティングの書き方

Route::get('/', 'HogeController@method')
Route::get('/', ['uses' => 'HogeController@method'])
Route::get('/', [ HogeController::class => 'method'])

最終的に以下の形にパースされます。

[
    'uses' => 'HogeController@method',
    'controller' => 'HogeController@method',
]
10
13
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
10
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?