1. 程式人生 > >laravel5.5原始碼筆記(四、路由)

laravel5.5原始碼筆記(四、路由)

今天這篇博文來探索一下laravel的路由。在第一篇講laravel入口檔案的博文裡,我們就提到過laravel的路由是在application物件的初始化階段,通過provider來載入的。這個路由服務提供者註冊於vendor\laravel\framework\src\Illuminate\Foundation\Application.php的registerBaseServiceProviders方法

protected function registerBaseServiceProviders()
    {
        $this->register(new EventServiceProvider($this
)); $this->register(new LogServiceProvider($this)); $this->register(new RoutingServiceProvider($this)); }

可以看到這個方法對路由provider進行了註冊,我們最開始的博文也提到過,這個register方法實際上是運行了provider內部的register方法,現在來看一下這個provider都提供了些什麼vendor\laravel\framework\src\Illuminate\Routing\RoutingServiceProvider.php

    public function register()
    {
        $this->registerRouter();

        $this->registerUrlGenerator();

        $this->registerRedirector();

        $this->registerPsrRequest();

        $this->registerPsrResponse();

        $this->registerResponseFactory();

        
$this->registerControllerDispatcher(); }

 這個服務提供者類中將許多物件都新增到了laravel的容器中,其中最重要的就是第一個註冊的Router類了。Router中包含了我們寫在路由檔案中的get、post等各種方法,我們在路由檔案中所使用的Route::any()方法也是一個門面類,它所代理的物件便是Router。

看過了路由的初始化,再來看一下我們在路由檔案中所書寫的路由是在什麼時候載入到系統中的。在config/app.php檔案中的privders陣列中有一個名為RouteServiceProvider的服務提供者會跟隨laravel系統在載入配置的時候一起載入。這個檔案位於\app\Providers\RouteServiceProvider.php剛剛的Routing對路由服務進行了註冊,這裡的RouteServiceProvider就要通過剛剛載入的系統類來載入我們寫在routes路由資料夾中的路由了。

至於這個provider是何時開始啟動的,還記得我們第一篇部落格中介紹的Illuminate\Foundation\Bootstrap\BootProviders這個provider嗎?這個provider在註冊時便會將已經註冊過的provider,通過application中的boot方法,轉發到它們自身的boot方法來啟動了。

而RouteServiceProvider這個類的boot方法通過它父類boot方法繞了一圈後又運行了自己的mapWebRoutes方法。

//Illuminate\Foundation\Support\Providers\RouteServiceProvider.php

public function boot()
    {
        //設定路由中控制器的名稱空間
        $this->setRootControllerNamespace();
        //若路由已有快取則載入快取
        if ($this->app->routesAreCached()) {
            $this->loadCachedRoutes();
        } else {
            //這個方法啟動了子類中的map方法來載入路由
            $this->loadRoutes();

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

    protected function loadRoutes()
    {
        if (method_exists($this, 'map')) {
            //這裡又把視線拉回了子類,執行了子類中的map方法
            $this->app->call([$this, 'map']);
        }
    }    

這裡這個mapWebRoutes方法有點繞,它先是通過門面類將Route變成了Router物件,接著又呼叫了Router中不存在的方法middleware,通過php的魔術方法__call將執行物件變成了RouteRegistrar物件(\Illuminate\Routing\RouteRegistrar.php)在第三句呼叫group方法時,又將路由檔案的地址傳入了Router方法的group方法中。

    protected function mapWebRoutes()
    {
        //這裡的route門面指向依舊是router,middleware方法通過__call過載將物件指向了RouteRegistrar物件
        Route::middleware('web')
            //RouteRegistrar物件也載入了名稱空間
             ->namespace($this->namespace)
            //這裡RouteRegistrar物件中的group方法又將物件方法指向了Router中的group方法
             ->group(base_path('routes/web.php'));
    }
//Router類

    public function __call($method, $parameters)
    {
        if (static::hasMacro($method)) {
            return $this->macroCall($method, $parameters);
        }
        //在這裡通過過載例項化物件
        if ($method == 'middleware') {
            return (new RouteRegistrar($this))->attribute($method, is_array($parameters[0]) ? $parameters[0] : $parameters);
        }

        return (new RouteRegistrar($this))->attribute($method, $parameters[0]);
    }
//\Illuminate\Routing\RouteRegistrar.php
    public function group($callback)
    {
        $this->router->group($this->attributes, $callback);
    }
//Router類
    public function group(array $attributes, $routes)
    {
        //更新路由棧這個陣列
        $this->updateGroupStack($attributes);

        // Once we have updated the group stack, we'll load the provided routes and
        // merge in the group's attributes when the routes are created. After we
        // have created the routes, we will pop the attributes off the stack.
        $this->loadRoutes($routes);
        //出棧
        array_pop($this->groupStack);
    }

    protected function loadRoutes($routes)
    {
        //這裡判斷閉包是因為laravel的路由檔案中也允許我們使用group對路由進行分組
        if ($routes instanceof Closure) {
            $routes($this);
        } else {
            $router = $this;
            //傳入的$routes是一個檔案路徑,在這裡將其引入執行,在這裡就開始一條一條的匯入路由了
            require $routes;
        }
    }

繞了這麼一大圈終於把寫在routes資料夾中的路由檔案載入進laravel系統了。接下來的操作就比較簡單了。

先來看一下我的路由檔案中寫了些什麼。

路由檔案中只寫了兩個路由,在Route載入後通過dd(app()->router);打印出來看一下吧。