[TOC]
* * * * *
## 1 路由
### 1 路由流程概覽

### 2 路由的意義
>[info] url作為一種輸入的數據
> 通過路由解析,
> 匹配到應用業務控制器
### 3 框架路由解析
框架路由主要關注**url的path_info部分規則解析**。
**將url中的path_info解析到對應模塊/控制器/操作**
使用**think\Route.php注冊url的path_info到對應業務的映射規則**
路由配置文件application/module/**route.php**
**配置url的path_info到對應業務的映射規則**
客戶端發送url請求服務器的框架時
**在think\App.php的run()方法中調用App::route()**
根據**route.php配置文件**與使用**think\Route.php注冊的映射規則**
將**url**的path_info解析到\application的應用業務**\模塊\控制器\操作**
## 2 路由規則配置convention.php,route.php
### 全局配置參數
~~~
thinkphp/convention.php:
path_info字符串標志,path_info兼容內容,path_info分隔符
'var_pathinfo' => 's',
'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
'pathinfo_depr' => '/',
系統變量request_uri,系統變量base_url
'url_request_uri' => 'REQUEST_URI',
'base_url' => $_SERVER["SCRIPT_NAME"],
偽靜態后綴,普通方式參數?,禁止訪問后綴
'url_html_suffix' => '.html',
'url_common_param' => false,
'url_deny_suffix' => 'ico|png|gif|jpg',
路由開啟與關閉,強制路由開啟與關閉,模塊映射
'url_route_on' => true,
'url_route_must' => false,
'url_module_map' => [],
域名部署開啟與關閉,根域名
'url_domain_deploy' => false,
'url_domain_root' => '',
控制器自動轉換開啟與關閉,操作自動轉換開啟與關閉
'url_controller_convert' => true,
'url_action_convert' => true,
~~~
### 應用路由配置
~~~
application/route.php:
return [
name變量規則定義
'__pattern__' => [
'name' => '\w+',
],
hello/:id映射規則定義
'[hello]' => [
':id' => ['index/hello', ['method' => 'get'], ['id' => '\d+']],
':name' => ['index/hello', ['method' => 'post']],
],
];
~~~
### 自定義路由配置簡單示例
~~~
return [
'hello/:id'=>'index/hello',
]
~~~
index.php/hello/2 映射到 默認模塊的Index.php的hello()
~~~
'blog/:id' => ['Blog/read', ['method' => 'get'], ['id' => '\d+']],
'blog/:name' => ['Blog/read', ['method' => 'post']],
~~~
index.php/blog/2 映射到 默認模塊的Blog.php的read()
## 3 框架路由解析App.php
### 1 App::run() 路由解析入口
~~~
App::run()
if (empty(self::$dispatch['type'])) {
self::route($config);
}
~~~
調用self::route()根據配置參數$config解析url
### 2 App::route() Url路由解析
~~~
self::parsePathinfo($config);
~~~
調用**App::parsePathinfo()**根據配置參數$config解析url
~~~
if (empty($_SERVER['PATH_INFO'])) {
$_SERVER['PATH_INFO'] = '';
define('__INFO__', '');
define('__EXT__', '');
}
~~~
獲取path_info失敗時,執行如上語句。
獲取path_info成功時,執行如下語句。
`$_SERVER['PATH_INFO'] = trim($_SERVER['PATH_INFO'], '/');`
去除path_info的首尾的'/'字符
`define('__INFO__', $_SERVER['PATH_INFO']);`
定義全局常量`__INFO__`為path_info
`define('__EXT__', strtolower(pathinfo($_SERVER['PATH_INFO'], PATHINFO_EXTENSION)));`
定義全局常量`__EXT__ `為path_info后綴
~~~
if ($config['url_deny_suffix'] && preg_match('/\.(' . $config['url_deny_suffix'] . ')$/i', __INFO__)) {
throw new Exception('url suffix deny');
}
~~~
這里的$config['url_deny_suffix']為convention.php中的
`'url_deny_suffix' => 'ico|png|gif|jpg',`
如果匹配成功,則拋出異常,禁止訪問。
`$_SERVER['PATH_INFO'] = preg_replace($config['url_html_suffix'] ? '/\.(' . trim($config['url_html_suffix'], '.') . ')$/i' : '/\.' . __EXT__ . '$/i', '', __INFO__);`
url后綴處理,好復雜的感覺。
`$depr = $config['pathinfo_depr'];`
這里的$config['pathinfo_depr']為convention.php中的
`'pathinfo_depr' => '/',`
`$result = false;`
下面的路由檢查結果保存變量。
`if (APP_ROUTE_ON && !empty($config['url_route_on'])){} `
`APP_ROUTE_ON` 在base.php中定義為true,表示開啟路由
$config['url_route_on']為convention.php中的
`'url_route_on' => true,`
定義了2個開關??
開啟路由時
~~~
if (!empty($config['route'])) {
Route::register($config['route']);
}
~~~
檢查是否有配置路由,
如果有調用**Route::register()**進行路由規則注冊
`$result = Route::check($_SERVER['PATH_INFO'], $depr, !IS_CLI ? $config['url_domain_deploy'] : false);`
調用**Route::check()**進行path_info的分析。
~~~
if (APP_ROUTE_MUST && false === $result && $config['url_route_must']) {
throw new Exception('route not define ');
}
~~~
檢查是否開啟強制路由解析,
如果獲取解析結果失敗,在強制路由下,拋出異常
~~~
if (false === $result) {
$result = Route::parseUrl($_SERVER['PATH_INFO'], $depr);
}
~~~
沒有開啟路由檢查時,調用**Route::parseUrl()**解析path_info
`self::dispatch($result);`
將路由解析解析注冊到調度類型$dispatch中
### 3 App::parsePathinfo() path_info解析
~~~
if (isset($_GET[$config['var_pathinfo']])) {
$_SERVER['PATH_INFO'] = $_GET[$config['var_pathinfo']];
unset($_GET[$config['var_pathinfo']]);
}
~~~
這里$config['var_pathinfo']是全局配置convention.php中的
`'var_pathinfo' => 's',`
獲取$_GET['s'],即index.php?s=module/controller/action/中的s對應值
~~~
elseif (IS_CLI) {
$_SERVER['PATH_INFO'] = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : '';
}
~~~
CLI模式下index.php module/controller/action/params/
設置path_info的信息為 module/controller/action/params/
`APP_HOOK && Hook::listen('path_info');`
path_info解析前回調處理
~~~
if (!isset($_SERVER['PATH_INFO'])) {
foreach ($config['pathinfo_fetch'] as $type) {
if (!empty($_SERVER[$type])) {
$_SERVER['PATH_INFO'] = (0 === strpos($_SERVER[$type], $_SERVER['SCRIPT_NAME'])) ?
substr($_SERVER[$type], strlen($_SERVER['SCRIPT_NAME'])) : $_SERVER[$type];
break;
}
}
}
~~~
這里的$config['pathinfo_fetch']為convention.php的
`'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],`
如果path_info獲取失敗,則查找$config['pathinfo_fetch']信息
將查找到的信息添加到path_info中。
>[info] App::parsePathinfo()主要用來獲取path_info
> 并保存到$_SERVER['PATH_INFO'],然后返回self::route()
### 4 App::dispatch() 注冊調度類型
`self::$dispatch = $dispatch;`
設置調度類型信息。
在**App::run()**下面根據調度類型進行應用調度
## 4 路由解析函數Route.php
### 1 Route::check() 路由檢查入口
`public static function check($url, $depr = '/', $checkDomain = false)`
> $url:待解析url
> $depr:url分隔符
> $checkDomain:是否進行域名解析
~~~
if ($checkDomain) {
self::checkDomain();
}
~~~
如果開啟域名解析,進行調用**Route::checkDomain()**域名解析處理
~~~
if ('/' != $depr) {
$url = str_replace($depr, '/', $url);
}
~~~
url分隔符替換為"/"
~~~
if (empty($url)) {
$url = '/';
}
~~~
$url為空時定位到根目錄
**下面包含多處返回結果。**
>[info] 1 將$url映射返回到App::route()
~~~
if (isset(self::$map[$url])) {
return self::parseUrl(self::$map[$url], $depr);
}
~~~
返回Route::$map中注冊的$url對應的映射信息。
如果存在調用**Route::parseUrl()**解析Url,
**將解析結果[$type=>module,$method=>操作方法]返回給App::route()**
**將解析結果調度類型為module,調度業務結構返回給App::route()**
~~~
$rules = self::$rules[REQUEST_METHOD];
if (!empty(self::$rules['*'])) {
$rules = array_merge(self::$rules['*'], $rules);
}
~~~
獲取注冊的路由規則到$rules
>[info] 2 返回url綁定結果
~~~
$return = self::checkUrlBind($url, $rules);
if ($return) {
return $return;
}
~~~
調用**Route::checkUrlBind()**解析url
如果返回了解析結果
**將相關解析結果的調度類型$type,調度方法$method**
**返回到App::route()中**
`if (!empty($rules)) {}`
如果$rules 路由映射存在。
`foreach ($rules as $rule => $val) {}`
遍歷路由映射規則。
~~~
$option = $val['option'];
$pattern = $val['pattern'];
~~~
獲取映射規則的選項$option與模式信息$pattern。
~~~
if (!self::checkOption($option, $url)) {
continue;
}
~~~
調用**Route::checkOption()**對$url中的參數進行檢查。
參數合法的解析解析本條規則,
否則跳出本條規則,解析下條規則
`if (!empty($val['routes'])) {}`
如果規則中存在路由映射,說明是分組路由。
對分組路由中的路由規則進行檢查
~~~
if (0 !== strpos($url, $rule)) {
continue;
}
~~~
檢查$url是否存在本地規則映射$rule中
查找失敗時解析下條映射規則。
`foreach ($val['routes'] as $key => $route) {}`
遍歷二級路由下的路由規則
~~~
if (is_numeric($key)) {
$key = array_shift($route);
}
~~~
返回數字形式$key
`$url1 = substr($url, strlen($rule) + 1);`
$url匹配截取
~~~
if (is_array($route)) {
$option1 = $route[1];
if (!self::checkOption($option1, $url)) {
continue;
}
$pattern = array_merge($pattern, isset($route[2]) ? $route[2] : []);
$route = $route[0];
$option = array_merge($option, $option1);
}
~~~
如果$route為數組,則進行參數檢查,
并解析模式,路由,選項參數到$pattern,$route,$option
>[info] 3 返回分組路由解析結果
~~~
$result = self::checkRule($key, $route, $url1, $pattern, $option);
if (false !== $result) {
return $result;
}
~~~
調用**Route::checkRule()**對分組路由規則進行檢查
并返回規則檢查結果
>[info] 4 返回簡單路由解析結果
~~~
else {
if (is_numeric($rule)) {
$rule = array_shift($val);
}
$route = !empty($val['route']) ? $val['route'] : '';
$result = self::checkRule($rule, $route, $url, $pattern, $option);
if (false !== $result) {
return $result;
}
}
~~~
調用**Route::checkRule()**對單條路由規則進行檢查
并返回規則檢查結果
>[info]5 返回false到App::run()
`return false;`
如果映射規則失敗,返回false到App::run()
### 2 Route::checkDomain() 域名解析
`$rules = self::$domain;`
獲取注冊的域名規則
`if (!empty($rules)) {}`
如果有注冊的域名規則,則進行解析
~~~
if (isset($rules[$_SERVER['HTTP_HOST']])) {
$rule = $rules[$_SERVER['HTTP_HOST']];
}
~~~
根據$_SERVER['HTTP_HOST']]獲取域名規則的規則
`$rootDomain = Config::get('url_domain_root');`
獲取失敗時,根據配置url_domain_root,獲取根域名
~~~
if ($rootDomain) {
$domain = explode('.', rtrim(stristr($_SERVER['HTTP_HOST'], $rootDomain, true), '.'));
} else {
$domain = explode('.', $_SERVER['HTTP_HOST'], -2);
}
~~~
以"."分割域名信息為數組。
`if (!empty($domain)) {}`
獲取根域名成功,繼續解析
~~~
$subDomain = implode('.', $domain);
self::$subDomain = $subDomain;
~~~
獲取子域名字符串
`$domain2 = array_pop($domain);`
2級域名字符串
~~~
if ($domain) {
$domain3 = array_pop($domain);
}
~~~
3級域名字符串
~~~
if ($subDomain && isset($rules[$subDomain])) {
$rule = $rules[$subDomain];
}
~~~
子域名對應規則
~~~
elseif (isset($rules['*.' . $domain2]) && !empty($domain3)) {
$rule = $rules['*.' . $domain2];
$panDomain = $domain3;
}
~~~
泛三級域名獲取。
~~~
elseif (isset($rules['*']) && !empty($domain2)) {
if ('www' != $domain2) {
$rule = $rules['*'];
$panDomain = $domain2;
}
}
~~~
泛二級域名獲取
`if (!empty($rule)) {}`
獲取域名規則成功時
~~~
if ($rule instanceof \Closure) {
$reflect = new \ReflectionFunction($rule);
self::$bind = $reflect->invokeArgs([]);
return;
}
~~~
域名規則閉包檢查,并調用
~~~
if (strpos($rule, '?')) {
$array = parse_url($rule);
$result = $array['path'];
parse_str($array['query'], $params);
if (isset($panDomain)) {
$pos = array_search('*', $params);
if (false !== $pos) {
// 泛域名作為參數
$params[$pos] = $panDomain;
}
}
$_GET = array_merge($_GET, $params);
}
~~~
域名規則中包含?字符,合并生成$_GET參數
并將規則的path解析結果保存到$result
~~~
if (0 === strpos($result, '\\')) {
self::$bind = ['type' => 'namespace', 'namespace' => $result];
}
~~~
`\\` 綁定類型設置為命名空間。
~~~
elseif (0 === strpos($result, '@')) {
self::$bind = ['type' => 'class', 'class' => substr($result, 1)];
}
~~~
`@`綁定類型設置為類。
~~~
elseif (0 === strpos($result, '[')) {
self::$bind = ['type' => 'group', 'group' => substr($result, 1, -1)];
}
~~~
`[` 綁定類型設置為規則組。
~~~
else {
self::$bind = ['type' => 'module', 'module' => $result];
}
~~~
其他綁定類型設置為module
>[info] Route::checkDomain()根據self::domain解析域名到
> self::$subDomain 子域名字符串
> self::$bind 綁定類型在Route::checkUrlBind()設置不同調度類型。
**域名解析完成后返回Route::check()**
### 3 Route::parseUrl() Url映射解析
`public static function parseUrl($url, $depr = '/')`
> $url:待解析$url
> $depr:解析$url時使用的分隔符
~~~
if (isset(self::$bind['module'])) {
$url = self::$bind['module'] . '/' . $url;
}
~~~
綁定模塊時將$url添加到module后面組成module/$url
~~~
if ('/' != $depr) {
$url = str_replace($depr, '/', $url);
}
~~~
再次替換分隔符,
`$result = self::parseRoute($url, true);`
調用**Route::parseRoute()**
進入**[模塊/控制器/操作?]參數1=值1&參數2=值2...**路由解析
~~~
if (!empty($result['var'])) {
$_GET = array_merge($result['var'], $_GET);
}
~~~
將解析得到的$var合并到$_GET中
` return ['type' => 'module', 'module' => $result['route']];`
返回調度類型$type為module,應用業務$module為$result['route']
>[info] Route::parseUrl()將url解析為模塊/控制器/操作,
> 并合并參數到$_GET中。
**并將調度類型,應用業務的結果返回到Route::check()**
### 4 Route::checkUrlBind() Url綁定類型解析
`private static function checkUrlBind(&$url, &$rules)`
> $url:待解析$url引用
> $rules:待解析$rules規則引用
`if (!empty(self::$bind['type'])) {}`
檢查是否存在綁定類型
這里的self::$bind在域名解析**Route::checkDomain()**生成。
`APP_DEBUG && Log::record('[ BIND ] ' . var_export(self::$bind, true), 'info');`
記錄綁定信息。
`switch (self::$bind['type']) {}`
根據綁定類型進行處理
~~~
case 'class':
$array = explode('/', $url, 2);
if (!empty($array[1])) {
self::parseUrlParams($array[1]);
}
return ['type' => 'method', 'method' => [self::$bind['class'], $array[0] ?: Config::get('default_action')], 'params' => []];
~~~
**class類型綁定**
解析url參數,返回調度類型$type為method,調度業務$method。
這里的method對應App::run()中的調度類型。
~~~
case 'namespace':
$array = explode('/', $url, 3);
$class = !empty($array[0]) ? $array[0] : Config::get('default_controller');
$method = !empty($array[1]) ? $array[1] : Config::get('default_action');
if (!empty($array[2])) {
self::parseUrlParams($array[2]);
}
return ['type' => 'method', 'method' => [self::$bind['namespace'] . '\\' . $class, $method], 'params' => []];
~~~
**namespace類型綁定**
解析url參數,返回調度類型型$type為method,調度業務$method
這里的method對應App::run()中的調度類型
~~~
case 'module':
$url = self::$bind['module'] . '/' . $url;
break;
~~~
**module類型綁定**
將self::$bind['module]合并到$url
~~~
case 'group':
$key = self::$bind['group'];
if (array_key_exists($key, $rules)) {
$rules = [$key => $rules[self::$bind['group']]];
}
~~~
**group類型綁定**
將路由規則合并到$rule。
### 5 Route::checkOption() 規則中參數有效性檢查
~~~
if ((isset($option['method']) && false === stripos($option['method'], REQUEST_METHOD))
|| (isset($option['ext']) && false === stripos($option['ext'], __EXT__)) // 偽靜態后綴檢測
|| (isset($option['domain']) && !in_array($option['domain'], [$_SERVER['HTTP_HOST'], self::$subDomain])) // 域名檢測
|| (!empty($option['https']) && !self::isSsl()) // https檢測
|| (!empty($option['before_behavior']) && false === Hook::exec($option['before_behavior'], $url)) // 行為檢測
|| (!empty($option['callback']) && is_callable($option['callback']) && false === call_user_func($option['callback'])) // 自定義檢測
)
~~~
>[info] method選項參數
> ext 選項參數
> domain 選項參數
> https 選項參數進行isSsl()檢測
> before_behavior 選項參數檢查 并調用Hook::exec()調度行為
> callback 選項參數檢查 調用callback回調
### 6 Route::checkRule() 路由規則檢查
`private static function checkRule($rule, $route, $url, $pattern, $option)`
> $rule:待解析規則$rule
> $route:待解析路由$route
> $url:待解析$url
> $pattern:待解析模式參數$pattern
> $option:待解析選項參數$option
~~~
if (isset($pattern['__url__']) && !preg_match('/^' . $pattern['__url__'] . '/', $url)) {
return false;
}
~~~
`__url__`模式匹配檢查
~~~
if ($depr = Config::get('url_params_depr')) {
$url = str_replace($depr, '/', $url);
$rule = str_replace($depr, '/', $rule);
}
~~~
$url中參數分隔符替換為配置分隔符
~~~
$len1 = substr_count($url, '/');
$len2 = substr_count($rule, '/');
~~~
獲取$url,$rule中"/"符號個數
`if ($len1 >= $len2 || strpos($rule, '[')) {}`
$rule是$url一部分,或者$rule中包含"["時才進行進一步解析
~~~
if ('$' == substr($rule, -1, 1)) {
if ($len1 != $len2) {
return false;
} else {
$rule = substr($rule, 0, -1);
}
}
~~~
$rule 以'$'結尾時 完整匹配
并獲取除"$"外的$rule
~~~
$pattern = array_merge(self::$pattern, $pattern);
~~~
合并模式參數$pattern到Route::$pattern
`if (false !== $match = self::match($url, $rule, $pattern)){}`
調用Route::match()進行$url與$rule匹配檢查。
如果參數檢測成功時
~~~
if (!empty($option['after_behavior'])) {
Hook::exec($option['after_behavior'], $route);
}
~~~
如果選項$option中包含after_behavior
運行選項中的after_behavior對應的標簽信息。
~~~
if ($route instanceof \Closure) {
return ['type' => 'function', 'function' => $route, 'params' => $match];
}
~~~
如果對應的$route是個閉包,
返回調度類型$type為function,調度業務$function
`return self::parseRule($rule, $route, $url);`
$option選項參數中沒有after_behavior并且$route不是閉包時
調用**Route::parseRule()**解析路由規則
### 7 Route::match() Url和規則路由匹配
`private static function match($url, $rule, $pattern)`
> $url:待匹配$url
> $rule:待匹配$rule
> $pattern:待匹配$rule中的模式參數$pattern
~~~
$m1 = explode('/', $url);
$m2 = explode('/', $rule);
~~~
使用"/"分割$url,$rule
~~~
if (0 === strpos($val, '[:')) {
$val = substr($val, 1, -1);
}
~~~
$rule第一個字符串為"[:"時,可選參數。
`if (0 === strpos($val, ':')) {}`
$rule第一個字符串為":"時,url變量。
`$name = substr($val, 1);`
獲取變量名稱$name
~~~
if (isset($m1[$key]) && isset($pattern[$name]) && !preg_match('/^' . $pattern[$name] . '$/', $m1[$key])) {
return false;
}
~~~
對$url,$rule,$pattern中的$name進行匹配檢查
`$var[$name] = $m1[$key];`
獲取$url參數到$val
~~~
elseif (0 !== strcasecmp($val, $m1[$key])) {
return false;
}
~~~
其他情況,完全對比
`return $var;`
返回匹配成功的參數信息
### 8 Route::parseRule() 規則解析
`private static function parseRule($rule, $route, $pathinfo)`
> $rule:待解析規則
> $route:待解析路由
> $pathinfo:待解析$pathinfo
~~~
$paths = explode('/', $pathinfo);
~~~
分隔$pathinfo參數為數組
`$url = is_array($route) ? $route[0] : $route;`
獲取$route中的路由$url信息
`$rule = explode('/', $rule);`
分隔$rule規則信息
~~~
foreach ($rule as $item) {
$fun = '';
if (0 === strpos($item, '[:')) {
$item = substr($item, 1, -1);
}
if (0 === strpos($item, ':')) {
$var = substr($item, 1);
$matches[$var] = array_shift($paths);
} else {
array_shift($paths);
}
}
~~~
對$rule規則的變量名進行過濾處理
~~~
foreach ($matches as $key => $val) {
if (false !== strpos($url, ':' . $key)) {
$url = str_replace(':' . $key, $val, $url);
unset($matches[$key]);
}
}
~~~
替換$url中的參數
if (0 === strpos($url, '/') || 0 === strpos($url, 'http')) {
$result = ['type' => 'redirect', 'url' => $url, 'status' => (is_array($route) && isset($route[1])) ? $route[1] : 301];
}
**"/"或者"http"開頭的$url,解析為redirect調度類型**
elseif (0 === strpos($url, '\\')) {
$result = ['type' => 'method', 'method' => is_array($route) ? [$url, $route[1]] : $url, 'params' => $matches];
}
**"\\"開頭的$url,解析為method調度類型**
~~~
elseif (0 === strpos($url, '@')) {
$result = ['type' => 'controller', 'controller' => substr($url, 1), 'params' => $matches];
}
~~~
**"@"開頭的$url,解析為controller調度類型**
else {}
其他情況手動解析$url
`$result = self::parseRoute($url);`
調用**Route::parseRoute()**解析$url
`$var = array_merge($matches, $result['var']);`
合并解析結果的$result['var']與參數信息$matches到$var
`self::parseUrlParams(implode('/', $paths), $var);`
調用Route::parseUrlParams()解析$url中的參數
`$result = ['type' => 'module', 'module' => $result['route']];`
解析結果為module調度類型
~~~
Config::set('url_controller_convert', false);
Config::set('url_action_convert', false);
~~~
關閉控制器和操作的自動轉換
` return $result;`
返回包含調度類型$type,調度業務$app的$result。
### 9 Route::parseRoute() 路由規則地址解析
~~~
if (false !== strpos($url, '?')) {
$info = parse_url($url);
$path = explode('/', $info['path'], 4);
parse_str($info['query'], $var);
} elseif (strpos($url, '/')) {
$path = explode('/', $url, 4);
} elseif (false !== strpos($url, '=')) {
parse_str($url, $var);
} else {
$path = [$url];
}
~~~
>[info] [模塊/控制器/操作?]參數1=值1&參數2=值2...
路由格式解析
保存信息到$info,$path,$var中。
~~~
if (!empty($path[3])) {
preg_replace_callback('/([^\/]+)\/([^\/]+)/', function ($match) use (&$var) {
$var[strtolower($match[1])] = strip_tags($match[2]);
}, array_pop($path));
}
~~~
如果存在$path,
首先解析模塊/控制器/操作后面的$path[3]參數。
~~~
if ($reverse) {
$module = APP_MULTI_MODULE ? array_shift($path) : null;
$controller = !empty($path) ? array_shift($path) : null;
$action = !empty($path) ? array_shift($path) : null;
}
~~~
如果$reverse為true,則順序解析為模塊,控制器,操作
~~~
$action = array_pop($path);
$controller = !empty($path) ? array_pop($path) : null;
$module = APP_MULTI_MODULE && !empty($path) ? array_pop($path) : null;
~~~
如果$reverse為false,則倒序解析為模塊,控制器,操作
~~~
if ('[rest]' == $action) {
$action = REQUEST_METHOD;
}
~~~
rest操作解析
`$route = [$module, $controller, $action];`
合并模塊/控制器/操作到$route
`return ['route' => $route, 'var' => $var];`
返回解析得到的模塊/控制器/路由$route和參數$var
### 10 Route::parseUrlParams() 解析Url參數
`private static function parseUrlParams($url, $var)`
> $url:待解析參數的$url
> $var:待解析參數
~~~
if ($url) {
preg_replace_callback('/(\w+)\/([^\/]+)/', function ($match) use (&$var) {
$var[strtolower($match[1])] = strip_tags($match[2]);
}, $url);
}
~~~
匹配$url中的參數到$var
`$_GET = array_merge($var, $_GET);`
合并參數$var到$_GET
## 5 路由規則注冊Route.php
### Route::map() 注冊$url到路由$route的映射規則
`public static function map($map = '', $route = '')`
### Route::pattern() 注冊$url參數變量規則
`public static function pattern($name = '', $rule = '')`
### Route::domain() 注冊$url域名解析規則
`public static function domain($domain = '', $rule = '')`
### Route::bind() 路由綁定和獲取
`public static function bind($type, $bind = '')`
### Route::set() 上述調用的屬性設置與獲取
`private static function setting($var, $name = '', $value = '')`
## 6 Url生成
## 7 框架路由示例
- 更新記錄
- 概述
- 文件索引
- 函數索引
- 章節格式
- 框架流程
- 前:章節說明
- 主:(index.php)入口
- 主:(start.php)框架引導
- 主:(App.php)應用啟動
- 主:(App.php)應用調度
- C:(Controller.php)應用控制器
- M:(Model.php)數據模型
- V:(View.php)視圖對象
- 附:(App.php)應用啟動
- 附:(base.php)全局變量
- 附:(common.php)模式配置
- 附:(convention.php)全局配置
- 附:(Loader.php)自動加載器
- 附:(Build.php)自動生成
- 附:(Hook.php)監聽回調
- 附:(Route.php)全局路由
- 附:(Response.php)數據輸出
- 附:(Log.php)日志記錄
- 附:(Exception.php)異常處理
- 框架工具
- 另:(helper.php)輔助函數
- 另:(Cache.php)數據緩存
- 另:(Cookie.php)cookie操作
- 另:(Console.php)控制臺
- 另:(Debug.php)開發調試
- 另:(Error.php)錯誤處理
- 另:(Url.php)Url操作文件
- 另:(Loader.php)加載器實例化
- 另:(Input.php)數據輸入
- 另:(Lang.php)語言包管理
- 另:(ORM.php)ORM基類
- 另:(Process.php)進程管理
- 另:(Session.php)session操作
- 另:(Template.php)模板解析
- 框架驅動
- D:(\config)配置解析
- D:(\controller)控制器擴展
- D:(\model)模型擴展
- D:(\db)數據庫驅動
- D:(\view)模板解析
- D:(\template)模板標簽庫
- D:(\session)session驅動
- D:(\cache)緩存驅動
- D:(\console)控制臺
- D:(\process)進程擴展
- T:(\traits)Trait目錄
- D:(\exception)異常實現
- D:(\log)日志驅動
- 使用范例
- 服務器與框架的安裝
- 控制器操作
- 數據模型操作
- 視圖渲染控制
- MVC開發初探
- 模塊開發
- 入口文件定義全局變量
- 運行模式開發
- 框架配置
- 自動生成應用
- 事件與插件注冊
- 路由規則注冊
- 輸出控制
- 多種應用組織
- 綜合應用
- tp框架整合后臺auto架構快速開發
- 基礎原理
- php默認全局變量
- php的魔術方法
- php命名空間
- php的自動加載
- php的composer
- php的反射
- php的trait機制
- php設計模式
- php的系統時區
- php的異常錯誤
- php的輸出控制
- php的正則表達式
- php的閉包函數
- php的會話控制
- php的接口
- php的PDO
- php的字符串操作
- php的curl
- 框架心得
- 心:整體結構
- 心:配置詳解
- 心:加載器詳解
- 心:輸入輸出詳解
- 心:url路由詳解
- 心:模板詳解
- 心:模型詳解
- 心:日志詳解
- 心:緩存詳解
- 心:控制臺詳解
- 框架更新
- 4.20(驗證類,助手函數)
- 4.27(新模型Model功能)
- 5.4(新數據庫驅動)
- 7.28(自動加載)