2017_01_03_02_路由
路由:http://hostname/index.php?r=site/say&message=Hello+World
當入口腳本在調用 yii\web\Application::run() 方法時,它進行的第一個操作就是解析輸入的請求,然後實例化對應的控制器操作處理這個請求。 該過程就被稱爲引導路由(routing)。
message 被作爲一個參數傳給 actionSay() 方法,當省略它時,參數將使用默認值代替。
上面 URL 中的參數 r 代表路由,是整個應用級的, 指向特定操作的獨立 ID。
路由格式是 控制器 ID/操作 ID。應用接受請求的時候會檢查參數, 使用控制器 ID 去確定哪個控制器應該被用來處理請求。 然後相應控制器將使用操作 ID 去確定哪個操作方法將被用來做具體工作。 上述例子中,路由 site/say 將被解析至 SiteController 控制器和其中的 say 操作。 因此 SiteController::actionSay() 方法將被調用處理請求。
注意:與操作一樣,一個應用中控制器同樣有唯一的 ID。 控制器 ID 和操作 ID 使用同樣的命名規則。 控制器的類名源自於控制器 ID,移除了連字符 ,每個單詞首字母大寫,並加上 Controller 後綴。 例子:控制器 ID post-comment 相當於控制器類名 PostCommentController。
終端用戶通過所謂的路由尋找到操作,路由是包含以下部分的字符串:
模型ID: 僅存在於控制器屬於非應用的模塊;
控制器ID: 同應用(或同模塊如果爲模塊下的控制器) 下唯一標識控制器的字符串;
操作ID: 同控制器下唯一標識操作的字符串。
路由使用如下格式:
ControllerID/ActionID
如果屬於模塊下的控制器,使用如下格式:
ModuleID/ControllerID/ActionID
如果用戶的請求地址爲 http://hostname/index.php?r=site/index, 會執行site 控制器的index 操作。
Yii爲開發者提供了路由和URL管理組件:
所謂路由是指URL中用於標識用於處理用戶請求的module, controller, action的部分, 一般情況下由 r 查詢參數來指定。 如 http://www.digpage.com/index.php?r=post/view&id=100 , 表示這個請求將由PostController 的 actionView來處理。
同時,Yii也提供了一種美化URL的功能,使得上面的URL可以用一個比較整潔、美觀的形式表現出來, 如 http://www.digpage.com/post/view/100 。 這個功能的實現是依賴於一個稱爲 urlManager 的應用組件。
使用 urlManager 開發者可以解析用戶的請求,並指派相應的module, controller和action來進行處理, 還可以根據預義的路由規則,生成需要的URL返回給用戶使用。 簡而言之,urlManger具有解析請求以便確定指派誰來處理請求和根據路由規則生成URL 2個功能。
===================例子===================================
當前只是理解如何使用路由,具體怎麼解析和生成路由,是由路由規則決定的
美化URL
一般情況下,Yii應用生成和接受形如 http://www.digpage.com/index.php?r=post/view&id=100 的URL。這個URL分成幾個部分:
表示主機信息的 http://www.digapge.com
表示入口腳本的 index.php
表示路由的 r=post/view
表示普通查詢參數的 id=100
其中,主機信息部分從URL來講,一般是不能少的。當然內部鏈接可以使用相對路徑,這種情況下看似 可以省略,但是User Agent最終發出Request時,也是包含主機信息的。換句話說,Web Server接收並 轉交給Yii處理的URL,是完整的、帶有主機信息的URL。
而入口腳本 index.php 我們知道,Web Server會將所有的請求都是交由其進行處理。 也就是說,Web Server應當視所有的URL爲請求 index.php 腳本。這在 :ref:install 部分我們 已經對Web Server進行過相應配置了。
Yii允許我們不在URL中出現入口腳本 index.php 。
其次,路由信息對於Yii應用而言也必不可少,表明應當使用哪個controller和action來處理請求, 否則Yii只能使用默認的路由來處理請求。這個形式比較固定,採用的是一種類似路徑的形式, 一般爲 module/controller/action 之類的。
如果將URL省略掉入口腳本,並將路由信息轉換成路徑,上面的URL就會變成: http://www.digpage.com/post/view?id=100
對於查詢參數 id=100 而言,這個URL請求的是編號爲100的一個POST, 並執行view操作。那麼我們可以再進一步改成 http://www.digpage.com/post/view/100
我們假如所請求的編號100的文章,其標題爲 Route , 那麼不妨使用用 http://www.digpage.com/post/view/Route 來訪問。
這樣的話,乾脆再加上 .html 好了。 變成 http://www.digpage.com/post/view/Route.html
Yii有專門的 yii\web\UrlManager 來進行處理,其中:
隱藏入口腳本可以通過 yii\web\UrlManager::showScriptName = false 來實現
路由的路徑化可以通過 yii\web\UrlManager::enablePrettyUrl = true 來實現
參數的路徑化可以通過路由規則來實現
假後綴(fake suffix) .html 可以通過 yii\web\UrlManager::suffix = '.html' 來實現
路由規則:
路由規則是指 urlManager 用於解析請求或生成URL的規則。 一個路由規則必須實現 yii\web\UrlRuleInterface 接口,這個接口定義了兩個方法:
用於解析請求的 yii\web\UrlRuleInterface::parseRequest()
用於生成URL的 yii\web\UrlRuleInterface::createUrl()
Yii中,使用 yii\web\UrlRule 來表示路由規則,一般這個類是足夠開發者使用的。 但是,如果開發者想自己實現解析請求或生成URL的邏輯,可以以這個類爲基類進行派生, 並重載 parseRuquest() 和 createUrl() 。
以下是配置文件中urlManager組件的路由規則配置部分,以幾個相對簡單、典型的路由規則的爲例, 先有個感性認識:
'rules' => [
// 爲路由指定了一個別名,以 post 的複數形式來表示 post/index 路由
'posts' => 'post/index',
// id 是命名參數,post/100 形式的URL,其實是 post/view&id=100
'post/<id:\d+>' => 'post/view',
// controller action 和 id 以命名參數形式出現
'<controller:(post|comment)>/<id:\d+>/<action:(create|update|delete)>'
=> '<controller>/<action>',
// 包含了 HTTP 方法限定,僅限於DELETE方法
'DELETE <controller:\w+>/<id:\d+>' => '<controller>/delete',
// 需要將 Web Server 配置成可以接收 *.digpage.com 域名的請求
'http://<user:\w+>.digpage.com/<lang:\w+>/profile' => 'user/profile',
]
只需大致瞭解上面這個數組用於爲urlManager聲明路由規則。
數組的鍵相當於請求(需要解析的或將要生成的),而元素的值則對應的路由, 即 controller/action 。
請求部分可稱爲pattern,路由部分則可稱爲route。
yii\web\UrlRule 的代碼:
class UrlRule extends Object implements UrlRuleInterface
{
// 用於 $mode 表示路由規則的2種工作模式:僅用於解析請求和僅用於生成URL。
// 任意不爲1或2的值均表示兩種模式同時適用,
// 一般未設定或爲0時即表示兩種模式均適用。
const PARSING_ONLY = 1;
const CREATION_ONLY = 2;
// 路由規則名稱
public $name;
// 用於解析請求或生成URL的模式,通常是正則表達式
public $pattern;
// 用於解析或創建URL時,處理主機信息的部分,如 http://www.digpage.com
public $host;
// 指向controller 和 action 的路由
public $route;
// 以一組鍵值對數組指定若干GET參數,在當前規則用於解析請求時,
// 這些GET參數會被注入到 $_GET 中去
public $defaults = [];
// 指定URL的後綴,通常是諸如 ".html" 等,
// 使得一個URL看起來好像指向一個靜態頁面。
// 如果這個值未設定,使用 UrlManager::suffix 的值。
public $suffix;
// 指定當前規則適用的HTTP方法,如 GET, POST, DELETE 等。
// 可以使用數組表示同時適用於多個方法。
// 如果未設定,表明當前規則適用於所有方法。
// 當然,這個屬性僅在解析請求時有效,在生成URL時是無效的。
public $verb;
// 表明當前規則的工作模式,取值可以是 0, PARSING_ONLY, CREATION_ONLY。
// 未設定時等同於0。
public $mode;
// 表明URL中的參數是否需要進行url編碼,默認是進行。
public $encodeParams = true;
// 用於生成新URL的模板
private $_template;
// 一個用於匹配路由部分的正則表達式,用於生成URL
private $_routeRule;
// 用於保存一組匹配參數的正則表達式,用於生成URL
private $_paramRules = [];
// 保存一組路由中使用的參數
private $_routeParams = [];
// 初始化
public function init() {...}
// 用於解析請求,由UrlRequestInterface接口要求
public function parseRequest($manager, $request) {...}
// 用於生成URL,由UrlRequestInterface接口要求
public function createUrl($manager, $route, $params) {...}
}
從上面代碼看, UrlRule 的屬性(可配置項)比較多。
要着重分析一下初始化函數 yii\web\UrlRule::init() ,來加深對這些屬性的理解:
public function init()
{
// 一個路由規則必定要有 pattern ,否則是沒有意義的,
// 一個什麼都沒規定的規定,要來何用?
if ($this->pattern === null) {
throw new InvalidConfigException('UrlRule::pattern must be set.');
}
// 不指定規則匹配後所要指派的路由,Yii怎麼知道將請求交給誰來處理?
// 不指定路由,Yii怎麼知道這個規則可以爲誰創建URL?
if ($this->route === null) {
throw new InvalidConfigException('UrlRule::route must be set.');
}
// 如果定義了一個或多個verb,說明規則僅適用於特定的HTTP方法。
// 既然是HTTP方法,那就要全部大寫。
// verb的定義可以是字符串(單一的verb)或數組(單一或多個verb)。
if ($this->verb !== null) {
if (is_array($this->verb)) {
foreach ($this->verb as $i => $verb) {
$this->verb[$i] = strtoupper($verb);
}
} else {
$this->verb = [strtoupper($this->verb)];
}
}
// 若未指定規則的名稱,那麼使用最能區別於其他規則的 $pattern
// 作爲規則的名稱
if ($this->name === null) {
$this->name = $this->pattern;
}
// 刪除 pattern 兩端的 "/",特別是重複的 "/",
// 在寫 pattern 時,雖然有正則的成分,但不需要在兩端加上 "/",
// 更不能加上 "#" 等其他分隔符
$this->pattern = trim($this->pattern, '/');
// 如果定義了 host ,將 host 部分加在 pattern 前面,作爲新的 pattern
if ($this->host !== null) {
// 寫入的host末尾如果已經包含有 "/" 則去掉,特別是重複的 "/"
$this->host = rtrim($this->host, '/');
$this->pattern = rtrim($this->host . '/' . $this->pattern, '/');
// 既未定義 host ,pattern 又是空的,那麼 pattern 匹配任意字符串。
// 而基於這個pattern的,用於生成的URL的template就是空的,
// 意味着使用該規則生成所有URL都是空的。
// 後續也無需再作其他初始化工作了。
} elseif ($this->pattern === '') {
$this->_template = '';
$this->pattern = '#^$#u';
return;
// pattern 不是空串,且包含有 '://',以此認定該pattern包含主機信息
} elseif (($pos = strpos($this->pattern, '://')) !== false) {
// 除 '://' 外,第一個 '/' 之前的內容就是主機信息
if (($pos2 = strpos($this->pattern, '/', $pos + 3)) !== false) {
$this->host = substr($this->pattern, 0, $pos2);
// '://' 後再無其他 '/',那麼整個 pattern 其實就是主機信息
} else {
$this->host = $this->pattern;
}
// pattern 不是空串,且不包含主機信息,兩端加上 '/' ,形成一個正則
} else {
$this->pattern = '/' . $this->pattern . '/';
}
// route 也要去掉兩頭的 '/'
$this->route = trim($this->route, '/');
// 從這裏往下,請結合流程圖來看
// route 中含有 <參數> ,則將所有參數提取成 [參數 => <參數>]
// 存入 _routeParams[],
// 如 ['controller' => '<controller>', 'action' => '<action>'],
// 留意這裏的短路判斷,先使用 strpos(),快速排除無需使用正則的情況
if (strpos($this->route, '<') !== false &&
preg_match_all('/<(\w+)>/', $this->route, $matches)) {
foreach ($matches[1] as $name) {
$this->_routeParams[$name] = "<$name>";
}
}
// 這個 $tr[] 和 $tr2[] 用於字符串的轉換
$tr = [
'.' => '\\.',
'*' => '\\*',
'$' => '\\$',
'[' => '\\[',
']' => '\\]',
'(' => '\\(',
')' => '\\)',
];
$tr2 = [];
// pattern 中含有 <參數名:參數pattern> ,
// 其中 ':參數pattern' 部分是可選的。
if (preg_match_all('/<(\w+):?([^>]+)?>/', $this->pattern, $matches,
PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
foreach ($matches as $match) {
// 獲取 “參數名”
$name = $match[1][0];
// 獲取 “參數pattern” ,如果未指定,使用 '[^\/]' ,
// 表示匹配除 '/' 外的所有字符
$pattern = isset($match[2][0]) ? $match[2][0] : '[^\/]+';
// 如果 defaults[] 中有同名參數,
if (array_key_exists($name, $this->defaults)) {
// $match[0][0] 是整個 <參數名:參數pattern> 串
$length = strlen($match[0][0]);
$offset = $match[0][1];
// pattern 中 <參數名:參數pattern> 兩頭都有 '/'
if ($offset > 1 && $this->pattern[$offset - 1] === '/'
&& $this->pattern[$offset + $length] === '/') {
// 留意這個 (?P<name>pattern) 正則,這是一個命名分組。
// 僅冠以一個命名供後續引用,使用上與直接的 (pattern) 沒有區別
// 見:http://php.net/manual/en/regexp.reference.subpatterns.php
$tr["/<$name>"] = "(/(?P<$name>$pattern))?";
} else {
$tr["<$name>"] = "(?P<$name>$pattern)?";
}
// defaults[]中沒有同名參數
} else {
$tr["<$name>"] = "(?P<$name>$pattern)";
}
// routeParams[]中有同名參數
if (isset($this->_routeParams[$name])) {
$tr2["<$name>"] = "(?P<$name>$pattern)";
// routeParams[]中沒有同名參數,則將 參數pattern 存入 _paramRules[] 中。
// 留意這裏是怎麼對 參數pattern 進行處理後再保存的。
} else {
$this->_paramRules[$name] = $pattern === '[^\/]+' ? '' :
"#^$pattern$#u";
}
}
}
// 將 pattern 中所有的 <參數名:參數pattern> 替換成 <參數名> 後作爲 _template
$this->_template = preg_replace('/<(\w+):?([^>]+)?>/', '<$1>', $this->pattern);
// 將 _template 中的特殊字符及字符串使用 tr[] 進行轉換,並作爲最終的pattern
$this->pattern = '#^' . trim(strtr($this->_template, $tr), '/') . '$#u';
// 如果指定了 routePrams 還要使用 tr2[] 對 route 進行轉換,
// 並作爲最終的 _routeRule
if (!empty($this->_routeParams)) {
$this->_routeRule = '#^' . strtr($this->route, $tr2) . '$#u';
}
}
先看init() 的前半部分,這些代碼提醒我們:
規則的 $pattern 和 $route 是必須配置的。
規則的名稱 $name 和主機信息 $host 在未配置的情況下,可以從 $pattern 來獲取。
$pattern 雖然含有正則的成分,但不需要在兩端加入 / ,更不能使用 # 等其他分隔符。 Yii會自動爲我們加上。
指定 $pattern 爲空串,可以使該規則匹配任意的URL。此時基於該規則所生成的所有URL也都是空串。
$pattern 中含有 :\\ 時,Yii會認爲其中包含了主機信息。此時就不應當再指定 host 。 否則,Yii會將 host 接在這個 pattern 前,作爲新的pattern。這會造成該pattern 兩段 :\\ , 而這顯然不是我們要的。
>>>>待續部分看截圖 路由.png<<<<<
當入口腳本在調用 yii\web\Application::run() 方法時,它進行的第一個操作就是解析輸入的請求,然後實例化對應的控制器操作處理這個請求。 該過程就被稱爲引導路由(routing)。
message 被作爲一個參數傳給 actionSay() 方法,當省略它時,參數將使用默認值代替。
上面 URL 中的參數 r 代表路由,是整個應用級的, 指向特定操作的獨立 ID。
路由格式是 控制器 ID/操作 ID。應用接受請求的時候會檢查參數, 使用控制器 ID 去確定哪個控制器應該被用來處理請求。 然後相應控制器將使用操作 ID 去確定哪個操作方法將被用來做具體工作。 上述例子中,路由 site/say 將被解析至 SiteController 控制器和其中的 say 操作。 因此 SiteController::actionSay() 方法將被調用處理請求。
注意:與操作一樣,一個應用中控制器同樣有唯一的 ID。 控制器 ID 和操作 ID 使用同樣的命名規則。 控制器的類名源自於控制器 ID,移除了連字符 ,每個單詞首字母大寫,並加上 Controller 後綴。 例子:控制器 ID post-comment 相當於控制器類名 PostCommentController。
終端用戶通過所謂的路由尋找到操作,路由是包含以下部分的字符串:
模型ID: 僅存在於控制器屬於非應用的模塊;
控制器ID: 同應用(或同模塊如果爲模塊下的控制器) 下唯一標識控制器的字符串;
操作ID: 同控制器下唯一標識操作的字符串。
路由使用如下格式:
ControllerID/ActionID
如果屬於模塊下的控制器,使用如下格式:
ModuleID/ControllerID/ActionID
如果用戶的請求地址爲 http://hostname/index.php?r=site/index, 會執行site 控制器的index 操作。
Yii爲開發者提供了路由和URL管理組件:
所謂路由是指URL中用於標識用於處理用戶請求的module, controller, action的部分, 一般情況下由 r 查詢參數來指定。 如 http://www.digpage.com/index.php?r=post/view&id=100 , 表示這個請求將由PostController 的 actionView來處理。
同時,Yii也提供了一種美化URL的功能,使得上面的URL可以用一個比較整潔、美觀的形式表現出來, 如 http://www.digpage.com/post/view/100 。 這個功能的實現是依賴於一個稱爲 urlManager 的應用組件。
使用 urlManager 開發者可以解析用戶的請求,並指派相應的module, controller和action來進行處理, 還可以根據預義的路由規則,生成需要的URL返回給用戶使用。 簡而言之,urlManger具有解析請求以便確定指派誰來處理請求和根據路由規則生成URL 2個功能。
===================例子===================================
當前只是理解如何使用路由,具體怎麼解析和生成路由,是由路由規則決定的
美化URL
一般情況下,Yii應用生成和接受形如 http://www.digpage.com/index.php?r=post/view&id=100 的URL。這個URL分成幾個部分:
表示主機信息的 http://www.digapge.com
表示入口腳本的 index.php
表示路由的 r=post/view
表示普通查詢參數的 id=100
其中,主機信息部分從URL來講,一般是不能少的。當然內部鏈接可以使用相對路徑,這種情況下看似 可以省略,但是User Agent最終發出Request時,也是包含主機信息的。換句話說,Web Server接收並 轉交給Yii處理的URL,是完整的、帶有主機信息的URL。
而入口腳本 index.php 我們知道,Web Server會將所有的請求都是交由其進行處理。 也就是說,Web Server應當視所有的URL爲請求 index.php 腳本。這在 :ref:install 部分我們 已經對Web Server進行過相應配置了。
Yii允許我們不在URL中出現入口腳本 index.php 。
其次,路由信息對於Yii應用而言也必不可少,表明應當使用哪個controller和action來處理請求, 否則Yii只能使用默認的路由來處理請求。這個形式比較固定,採用的是一種類似路徑的形式, 一般爲 module/controller/action 之類的。
如果將URL省略掉入口腳本,並將路由信息轉換成路徑,上面的URL就會變成: http://www.digpage.com/post/view?id=100
對於查詢參數 id=100 而言,這個URL請求的是編號爲100的一個POST, 並執行view操作。那麼我們可以再進一步改成 http://www.digpage.com/post/view/100
我們假如所請求的編號100的文章,其標題爲 Route , 那麼不妨使用用 http://www.digpage.com/post/view/Route 來訪問。
這樣的話,乾脆再加上 .html 好了。 變成 http://www.digpage.com/post/view/Route.html
Yii有專門的 yii\web\UrlManager 來進行處理,其中:
隱藏入口腳本可以通過 yii\web\UrlManager::showScriptName = false 來實現
路由的路徑化可以通過 yii\web\UrlManager::enablePrettyUrl = true 來實現
參數的路徑化可以通過路由規則來實現
假後綴(fake suffix) .html 可以通過 yii\web\UrlManager::suffix = '.html' 來實現
路由規則:
路由規則是指 urlManager 用於解析請求或生成URL的規則。 一個路由規則必須實現 yii\web\UrlRuleInterface 接口,這個接口定義了兩個方法:
用於解析請求的 yii\web\UrlRuleInterface::parseRequest()
用於生成URL的 yii\web\UrlRuleInterface::createUrl()
Yii中,使用 yii\web\UrlRule 來表示路由規則,一般這個類是足夠開發者使用的。 但是,如果開發者想自己實現解析請求或生成URL的邏輯,可以以這個類爲基類進行派生, 並重載 parseRuquest() 和 createUrl() 。
以下是配置文件中urlManager組件的路由規則配置部分,以幾個相對簡單、典型的路由規則的爲例, 先有個感性認識:
'rules' => [
// 爲路由指定了一個別名,以 post 的複數形式來表示 post/index 路由
'posts' => 'post/index',
// id 是命名參數,post/100 形式的URL,其實是 post/view&id=100
'post/<id:\d+>' => 'post/view',
// controller action 和 id 以命名參數形式出現
'<controller:(post|comment)>/<id:\d+>/<action:(create|update|delete)>'
=> '<controller>/<action>',
// 包含了 HTTP 方法限定,僅限於DELETE方法
'DELETE <controller:\w+>/<id:\d+>' => '<controller>/delete',
// 需要將 Web Server 配置成可以接收 *.digpage.com 域名的請求
'http://<user:\w+>.digpage.com/<lang:\w+>/profile' => 'user/profile',
]
只需大致瞭解上面這個數組用於爲urlManager聲明路由規則。
數組的鍵相當於請求(需要解析的或將要生成的),而元素的值則對應的路由, 即 controller/action 。
請求部分可稱爲pattern,路由部分則可稱爲route。
yii\web\UrlRule 的代碼:
class UrlRule extends Object implements UrlRuleInterface
{
// 用於 $mode 表示路由規則的2種工作模式:僅用於解析請求和僅用於生成URL。
// 任意不爲1或2的值均表示兩種模式同時適用,
// 一般未設定或爲0時即表示兩種模式均適用。
const PARSING_ONLY = 1;
const CREATION_ONLY = 2;
// 路由規則名稱
public $name;
// 用於解析請求或生成URL的模式,通常是正則表達式
public $pattern;
// 用於解析或創建URL時,處理主機信息的部分,如 http://www.digpage.com
public $host;
// 指向controller 和 action 的路由
public $route;
// 以一組鍵值對數組指定若干GET參數,在當前規則用於解析請求時,
// 這些GET參數會被注入到 $_GET 中去
public $defaults = [];
// 指定URL的後綴,通常是諸如 ".html" 等,
// 使得一個URL看起來好像指向一個靜態頁面。
// 如果這個值未設定,使用 UrlManager::suffix 的值。
public $suffix;
// 指定當前規則適用的HTTP方法,如 GET, POST, DELETE 等。
// 可以使用數組表示同時適用於多個方法。
// 如果未設定,表明當前規則適用於所有方法。
// 當然,這個屬性僅在解析請求時有效,在生成URL時是無效的。
public $verb;
// 表明當前規則的工作模式,取值可以是 0, PARSING_ONLY, CREATION_ONLY。
// 未設定時等同於0。
public $mode;
// 表明URL中的參數是否需要進行url編碼,默認是進行。
public $encodeParams = true;
// 用於生成新URL的模板
private $_template;
// 一個用於匹配路由部分的正則表達式,用於生成URL
private $_routeRule;
// 用於保存一組匹配參數的正則表達式,用於生成URL
private $_paramRules = [];
// 保存一組路由中使用的參數
private $_routeParams = [];
// 初始化
public function init() {...}
// 用於解析請求,由UrlRequestInterface接口要求
public function parseRequest($manager, $request) {...}
// 用於生成URL,由UrlRequestInterface接口要求
public function createUrl($manager, $route, $params) {...}
}
從上面代碼看, UrlRule 的屬性(可配置項)比較多。
要着重分析一下初始化函數 yii\web\UrlRule::init() ,來加深對這些屬性的理解:
public function init()
{
// 一個路由規則必定要有 pattern ,否則是沒有意義的,
// 一個什麼都沒規定的規定,要來何用?
if ($this->pattern === null) {
throw new InvalidConfigException('UrlRule::pattern must be set.');
}
// 不指定規則匹配後所要指派的路由,Yii怎麼知道將請求交給誰來處理?
// 不指定路由,Yii怎麼知道這個規則可以爲誰創建URL?
if ($this->route === null) {
throw new InvalidConfigException('UrlRule::route must be set.');
}
// 如果定義了一個或多個verb,說明規則僅適用於特定的HTTP方法。
// 既然是HTTP方法,那就要全部大寫。
// verb的定義可以是字符串(單一的verb)或數組(單一或多個verb)。
if ($this->verb !== null) {
if (is_array($this->verb)) {
foreach ($this->verb as $i => $verb) {
$this->verb[$i] = strtoupper($verb);
}
} else {
$this->verb = [strtoupper($this->verb)];
}
}
// 若未指定規則的名稱,那麼使用最能區別於其他規則的 $pattern
// 作爲規則的名稱
if ($this->name === null) {
$this->name = $this->pattern;
}
// 刪除 pattern 兩端的 "/",特別是重複的 "/",
// 在寫 pattern 時,雖然有正則的成分,但不需要在兩端加上 "/",
// 更不能加上 "#" 等其他分隔符
$this->pattern = trim($this->pattern, '/');
// 如果定義了 host ,將 host 部分加在 pattern 前面,作爲新的 pattern
if ($this->host !== null) {
// 寫入的host末尾如果已經包含有 "/" 則去掉,特別是重複的 "/"
$this->host = rtrim($this->host, '/');
$this->pattern = rtrim($this->host . '/' . $this->pattern, '/');
// 既未定義 host ,pattern 又是空的,那麼 pattern 匹配任意字符串。
// 而基於這個pattern的,用於生成的URL的template就是空的,
// 意味着使用該規則生成所有URL都是空的。
// 後續也無需再作其他初始化工作了。
} elseif ($this->pattern === '') {
$this->_template = '';
$this->pattern = '#^$#u';
return;
// pattern 不是空串,且包含有 '://',以此認定該pattern包含主機信息
} elseif (($pos = strpos($this->pattern, '://')) !== false) {
// 除 '://' 外,第一個 '/' 之前的內容就是主機信息
if (($pos2 = strpos($this->pattern, '/', $pos + 3)) !== false) {
$this->host = substr($this->pattern, 0, $pos2);
// '://' 後再無其他 '/',那麼整個 pattern 其實就是主機信息
} else {
$this->host = $this->pattern;
}
// pattern 不是空串,且不包含主機信息,兩端加上 '/' ,形成一個正則
} else {
$this->pattern = '/' . $this->pattern . '/';
}
// route 也要去掉兩頭的 '/'
$this->route = trim($this->route, '/');
// 從這裏往下,請結合流程圖來看
// route 中含有 <參數> ,則將所有參數提取成 [參數 => <參數>]
// 存入 _routeParams[],
// 如 ['controller' => '<controller>', 'action' => '<action>'],
// 留意這裏的短路判斷,先使用 strpos(),快速排除無需使用正則的情況
if (strpos($this->route, '<') !== false &&
preg_match_all('/<(\w+)>/', $this->route, $matches)) {
foreach ($matches[1] as $name) {
$this->_routeParams[$name] = "<$name>";
}
}
// 這個 $tr[] 和 $tr2[] 用於字符串的轉換
$tr = [
'.' => '\\.',
'*' => '\\*',
'$' => '\\$',
'[' => '\\[',
']' => '\\]',
'(' => '\\(',
')' => '\\)',
];
$tr2 = [];
// pattern 中含有 <參數名:參數pattern> ,
// 其中 ':參數pattern' 部分是可選的。
if (preg_match_all('/<(\w+):?([^>]+)?>/', $this->pattern, $matches,
PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
foreach ($matches as $match) {
// 獲取 “參數名”
$name = $match[1][0];
// 獲取 “參數pattern” ,如果未指定,使用 '[^\/]' ,
// 表示匹配除 '/' 外的所有字符
$pattern = isset($match[2][0]) ? $match[2][0] : '[^\/]+';
// 如果 defaults[] 中有同名參數,
if (array_key_exists($name, $this->defaults)) {
// $match[0][0] 是整個 <參數名:參數pattern> 串
$length = strlen($match[0][0]);
$offset = $match[0][1];
// pattern 中 <參數名:參數pattern> 兩頭都有 '/'
if ($offset > 1 && $this->pattern[$offset - 1] === '/'
&& $this->pattern[$offset + $length] === '/') {
// 留意這個 (?P<name>pattern) 正則,這是一個命名分組。
// 僅冠以一個命名供後續引用,使用上與直接的 (pattern) 沒有區別
// 見:http://php.net/manual/en/regexp.reference.subpatterns.php
$tr["/<$name>"] = "(/(?P<$name>$pattern))?";
} else {
$tr["<$name>"] = "(?P<$name>$pattern)?";
}
// defaults[]中沒有同名參數
} else {
$tr["<$name>"] = "(?P<$name>$pattern)";
}
// routeParams[]中有同名參數
if (isset($this->_routeParams[$name])) {
$tr2["<$name>"] = "(?P<$name>$pattern)";
// routeParams[]中沒有同名參數,則將 參數pattern 存入 _paramRules[] 中。
// 留意這裏是怎麼對 參數pattern 進行處理後再保存的。
} else {
$this->_paramRules[$name] = $pattern === '[^\/]+' ? '' :
"#^$pattern$#u";
}
}
}
// 將 pattern 中所有的 <參數名:參數pattern> 替換成 <參數名> 後作爲 _template
$this->_template = preg_replace('/<(\w+):?([^>]+)?>/', '<$1>', $this->pattern);
// 將 _template 中的特殊字符及字符串使用 tr[] 進行轉換,並作爲最終的pattern
$this->pattern = '#^' . trim(strtr($this->_template, $tr), '/') . '$#u';
// 如果指定了 routePrams 還要使用 tr2[] 對 route 進行轉換,
// 並作爲最終的 _routeRule
if (!empty($this->_routeParams)) {
$this->_routeRule = '#^' . strtr($this->route, $tr2) . '$#u';
}
}
先看init() 的前半部分,這些代碼提醒我們:
規則的 $pattern 和 $route 是必須配置的。
規則的名稱 $name 和主機信息 $host 在未配置的情況下,可以從 $pattern 來獲取。
$pattern 雖然含有正則的成分,但不需要在兩端加入 / ,更不能使用 # 等其他分隔符。 Yii會自動爲我們加上。
指定 $pattern 爲空串,可以使該規則匹配任意的URL。此時基於該規則所生成的所有URL也都是空串。
$pattern 中含有 :\\ 時,Yii會認爲其中包含了主機信息。此時就不應當再指定 host 。 否則,Yii會將 host 接在這個 pattern 前,作爲新的pattern。這會造成該pattern 兩段 :\\ , 而這顯然不是我們要的。
>>>>待續部分看截圖 路由.png<<<<<
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.