🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[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 框架路由示例