[TOC]
* * * * *
## 1 路由
### 1 路由流程概览
![](https://box.kancloud.cn/2016-03-17_56ea7a2e4c156.jpg)
### 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(自动加载)