在路由載入配置檔案web.php的時候,用到了一個方法mapWebRoutes。
在這個方法中他用到了middleware方法和namespace方法。這兩個方法是給Group新增屬性的方法。
我們分別來看看他的邏輯。便於後面分析。
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
這裡首先Route是一個Facade呼叫。Facade返回Router物件。(並不是Route物件)。
在Router物件中不存在middleware和namespace方法。所以他會去呼叫魔術方法__call
方法來處理。
__call方法如下:
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]);
}
*.巨集機制:
簡單說就是可以對一個類動態的新增外部方法,而這個方法並不用在類內部實現。在類不變的情況下新增方法。舉例:
Route::macro('middleware', function(){
var_dump("test");
return "test for middleware";
});
在不更改Router類的前提下,對Router類新增了一個方法middleware
,方法體就是後面的閉包函式。
然後我們使用下面的程式碼就可以呼叫。但是我們並沒有更改Router物件中的原始碼。
Route::middleware();
1.回到原始碼,這裡
if (static::hasMacro($method)) {
return $this->macroCall($method, $parameters);
}
首先檢視有沒有提前設定好的巨集。如果存在就可以觸發返回。不再執行下面的邏輯。
簡單說,就是Router類觸發的方法如果不在類中出現,那就去巨集集合中檢視是否提前設定了這個方法,然後觸發。
2.第二步:
if ($method === 'middleware') {
return (new RouteRegistrar($this))->attribute($method, is_array($parameters[0]) ? $parameters[0] : $parameters);
}
如果call的方法是middleware
。(只針對middleware方法)
新建一個RouteRegistrar
物件後呼叫他的attribute方法把middleware
作為key,middleware的引數作為value存入RouteRegistrar
物件的attributes
陣列中
is_array($parameters[0]) ? $parameters[0] : $parameters
針對middleware方法可以傳入陣列引數,也可以傳入字串作為引數。
可以看下attribute方法。最後返回的是RouteRegistrar物件。
public function attribute($key, $value)
{
if (! in_array($key, $this->allowedAttributes)) {
throw new InvalidArgumentException("Attribute [{$key}] does not exist.");
}
$this->attributes[Arr::get($this->aliases, $key, $key)] = $value;
return $this;
}
2.1 首先判斷當前的方法名是否在陣列 allowedAttributes
中存在。
allowedAttributes方法如下,就是限制了只有這幾個方法可以進一步執行邏輯。否則丟擲異常。
這幾個方法也是group提供的幾個引數方法。
protected $allowedAttributes = [
'as', 'domain', 'middleware', 'name', 'namespace', 'prefix', 'where',
];
2.2 Arr::get($this->aliases, $key, $key)
叢aliases陣列中找到欄位為$key
的值,如果找不到就用預設值 $key
替代。aliases是一個別名陣列,通過別名尋找真實的方法名。
把這個返回值作為id,第二個引數作為value存入attributes陣列中。
簡單說就是:
先尋找方法名的別名,然後得到了方法名的返回值作為id,方法的引數作為value存入attributes陣列。
3.第三步
return (new RouteRegistrar($this))->attribute($method, $parameters[0]);
針對其他的方法。和第二步類同,呼叫RouteRegistrar
的attribute
方法。唯一的區別就是因為middleware的引數可以是陣列和字串,其他的方法只是字串。所以單獨把middleware提出來單獨處理。
總結下:
除非提前動態繫結了middleware方法給router物件。
否則如果是middleware方法,就是把其方法名和引數儲存到RouterRegister物件的attributes陣列中。
其他方法也是如此。
然後我們回到mapWebRoutes這個方法:
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
在儲存了middleware和namespace方法的引數後。
最後執行了RouteRegistrar的group方法,這個group就是呼叫了router物件group方法,把前面得到的attributes屬性當做引數傳給router的group方法。
public function group($callback)
{
$this->router->group($this->attributes, $callback);
}
router的group方法使用require引入
$callback
的路徑或者觸發$callback
方法基於$callback
是字串還是閉包。不再詳述。 (group方法邏輯參考前面group方法分析章節。)
總結:
分析到這裡我們可以發現。整個web.php檔案,laravel把他當做了一整個group。在我們的例子中,這個配置檔案的middleware是web。namespace是$this->namespace
變數值, 預設值是:protected $namespace = 'App\Http\Controllers';
本作品採用《CC 協議》,轉載必須註明作者和本文連結