www.gusucode.com > OpenSNS PHP开源社区交友系统 v2.8.1源码程序 > os/ThinkPHP/Library/Think/Route.class.php
<?php // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK IT ] // +---------------------------------------------------------------------- // | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st <liu21st@gmail.com> // +---------------------------------------------------------------------- namespace Think; /** * ThinkPHP路由解析类 */ class Route { // 路由检测 public static function check() { $depr = C('URL_PATHINFO_DEPR'); $regx = preg_replace('/\.' . __EXT__ . '$/i', '', trim($_SERVER['PATH_INFO'], $depr)); // 分隔符替换 确保路由定义使用统一的分隔符 if ('/' != $depr) { $regx = str_replace($depr, '/', $regx); } // URL映射定义(静态路由) $maps = C('URL_MAP_RULES'); if (isset($maps[$regx])) { $var = self::parseUrl($maps[$regx]); $_GET = array_merge($var, $_GET); return true; } // 动态路由处理 $routes = C('URL_ROUTE_RULES'); if (!empty($routes)) { foreach ($routes as $rule => $route) { if (is_numeric($rule)) { // 支持 array('rule','adddress',...) 定义路由 $rule = array_shift($route); } if (is_array($route) && isset($route[2])) { // 路由参数 $options = $route[2]; if (isset($options['ext']) && __EXT__ != $options['ext']) { // URL后缀检测 continue; } if (isset($options['method']) && REQUEST_METHOD != $options['method']) { // 请求类型检测 continue; } // 自定义检测 if (!empty($options['callback']) && is_callable($options['callback'])) { if (false === call_user_func($options['callback'])) { continue; } } if (!empty($options['request_args'])) { $key = key($options['request_args']); if (I(REQUEST_METHOD . '.' .$key ) != $options['request_args'][$key]) { continue; } } } if (0 === strpos($rule, '/') && preg_match($rule, $regx, $matches)) { // 正则路由 if ($route instanceof \Closure) { // 执行闭包 $result = self::invokeRegx($route, $matches); // 如果返回布尔值 则继续执行 return is_bool($result) ? $result : exit; } else { return self::parseRegex($matches, $route, $regx); } } else { // 规则路由 $len1 = substr_count($regx, '/'); $len2 = substr_count($rule, '/'); if ($len1 >= $len2 || strpos($rule, '[')) { if ('$' == substr($rule, -1, 1)) { // 完整匹配 if ($len1 != $len2) { continue; } else { $rule = substr($rule, 0, -1); } } $match = self::checkUrlMatch($regx, $rule); if (false !== $match) { if ($route instanceof \Closure) { // 执行闭包 $result = self::invokeRule($route, $match); // 如果返回布尔值 则继续执行 return is_bool($result) ? $result : exit; } else { return self::parseRule($rule, $route, $regx); } } } } } } return false; } // 检测URL和规则路由是否匹配 private static function checkUrlMatch($regx, $rule) { $m1 = explode('/', $regx); $m2 = explode('/', $rule); $var = array(); foreach ($m2 as $key => $val) { if (0 === strpos($val, '[:')) { $val = substr($val, 1, -1); } if (':' == substr($val, 0, 1)) { // 动态变量 if ($pos = strpos($val, '|')) { // 使用函数过滤 $val = substr($val, 1, $pos - 1); } if (strpos($val, '\\')) { $type = substr($val, -1); if ('d' == $type) { if (isset($m1[$key]) && !is_numeric($m1[$key])) return false; } $name = substr($val, 1, -2); } elseif ($pos = strpos($val, '^')) { $array = explode('-', substr(strstr($val, '^'), 1)); if (in_array($m1[$key], $array)) { return false; } $name = substr($val, 1, $pos - 1); } else { $name = substr($val, 1); } $var[$name] = isset($m1[$key]) ? $m1[$key] : ''; } elseif (0 !== strcasecmp($val, $m1[$key])) { return false; } } // 成功匹配后返回URL中的动态变量数组 return $var; } // 解析规范的路由地址 // 地址格式 [控制器/操作?]参数1=值1&参数2=值2... private static function parseUrl($url) { $var = array(); if (false !== strpos($url, '?')) { // [控制器/操作?]参数1=值1&参数2=值2... $info = parse_url($url); $path = explode('/', $info['path']); parse_str($info['query'], $var); } elseif (strpos($url, '/')) { // [控制器/操作] $path = explode('/', $url); } else { // 参数1=值1&参数2=值2... parse_str($url, $var); } if (isset($path)) { $var[C('VAR_ACTION')] = array_pop($path); if (!empty($path)) { $var[C('VAR_CONTROLLER')] = array_pop($path); } if (!empty($path)) { $var[C('VAR_MODULE')] = array_pop($path); } } return $var; } // 解析规则路由 // '路由规则'=>'[控制器/操作]?额外参数1=值1&额外参数2=值2...' // '路由规则'=>array('[控制器/操作]','额外参数1=值1&额外参数2=值2...') // '路由规则'=>'外部地址' // '路由规则'=>array('外部地址','重定向代码') // 路由规则中 :开头 表示动态变量 // 外部地址中可以用动态变量 采用 :1 :2 的方式 // 'news/:month/:day/:id'=>array('News/read?cate=1','status=1'), // 'new/:id'=>array('/new.php?id=:1',301), 重定向 private static function parseRule($rule, $route, $regx) { // 获取路由地址规则 $url = is_array($route) ? $route[0] : $route; // 获取URL地址中的参数 $paths = explode('/', $regx); // 解析路由规则 $matches = array(); $rule = explode('/', $rule); foreach ($rule as $item) { $fun = ''; if (0 === strpos($item, '[:')) { $item = substr($item, 1, -1); } if (0 === strpos($item, ':')) { // 动态变量获取 if ($pos = strpos($item, '|')) { // 支持函数过滤 $fun = substr($item, $pos + 1); $item = substr($item, 0, $pos); } if ($pos = strpos($item, '^')) { $var = substr($item, 1, $pos - 1); } elseif (strpos($item, '\\')) { $var = substr($item, 1, -2); } else { $var = substr($item, 1); } $matches[$var] = !empty($fun) ? $fun(array_shift($paths)) : array_shift($paths); } else { // 过滤URL中的静态变量 array_shift($paths); } } if (0 === strpos($url, '/') || 0 === strpos($url, 'http')) { // 路由重定向跳转 if (strpos($url, ':')) { // 传递动态参数 $values = array_values($matches); $url = preg_replace_callback('/:(\d+)/', function ($match) use ($values) { return $values[$match[1] - 1]; }, $url); } header("Location: $url", true, (is_array($route) && isset($route[1])) ? $route[1] : 301); exit; } else { // 解析路由地址 $var = self::parseUrl($url); // 解析路由地址里面的动态参数 $values = array_values($matches); foreach ($var as $key => $val) { if (0 === strpos($val, ':')) { $var[$key] = $values[substr($val, 1) - 1]; } } $var = array_merge($matches, $var); // 解析剩余的URL参数 if (!empty($paths)) { preg_replace_callback('/(\w+)\/([^\/]+)/', function ($match) use (&$var) { $var[strtolower($match[1])] = strip_tags($match[2]); }, implode('/', $paths)); } // 解析路由自动传入参数 if (is_array($route) && isset($route[1])) { if (is_array($route[1])) { $params = $route[1]; } else { parse_str($route[1], $params); } $var = array_merge($var, $params); } $_GET = array_merge($var, $_GET); } return true; } // 解析正则路由 // '路由正则'=>'[控制器/操作]?参数1=值1&参数2=值2...' // '路由正则'=>array('[控制器/操作]?参数1=值1&参数2=值2...','额外参数1=值1&额外参数2=值2...') // '路由正则'=>'外部地址' // '路由正则'=>array('外部地址','重定向代码') // 参数值和外部地址中可以用动态变量 采用 :1 :2 的方式 // '/new\/(\d+)\/(\d+)/'=>array('News/read?id=:1&page=:2&cate=1','status=1'), // '/new\/(\d+)/'=>array('/new.php?id=:1&page=:2&status=1','301'), 重定向 private static function parseRegex($matches, $route, $regx) { // 获取路由地址规则 $url = is_array($route) ? $route[0] : $route; $url = preg_replace_callback('/:(\d+)/', function ($match) use ($matches) { return $matches[$match[1]]; }, $url); if (0 === strpos($url, '/') || 0 === strpos($url, 'http')) { // 路由重定向跳转 header("Location: $url", true, (is_array($route) && isset($route[1])) ? $route[1] : 301); exit; } else { // 解析路由地址 $var = self::parseUrl($url); // 处理函数 foreach ($var as $key => $val) { if (strpos($val, '|')) { list($val, $fun) = explode('|', $val); $var[$key] = $fun($val); } } // 解析剩余的URL参数 $regx = substr_replace($regx, '', 0, strlen($matches[0])); if ($regx) { preg_replace_callback('/(\w+)\/([^\/]+)/', function ($match) use (&$var) { $var[strtolower($match[1])] = strip_tags($match[2]); }, $regx); } // 解析路由自动传入参数 if (is_array($route) && isset($route[1])) { if (is_array($route[1])) { $params = $route[1]; } else { parse_str($route[1], $params); } $var = array_merge($var, $params); } $_GET = array_merge($var, $_GET); } return true; } // 执行正则匹配下的闭包方法 支持参数调用 static private function invokeRegx($closure, $var = array()) { $reflect = new \ReflectionFunction($closure); $params = $reflect->getParameters(); $args = array(); array_shift($var); foreach ($params as $param) { if (!empty($var)) { $args[] = array_shift($var); } elseif ($param->isDefaultValueAvailable()) { $args[] = $param->getDefaultValue(); } } return $reflect->invokeArgs($args); } // 执行规则匹配下的闭包方法 支持参数调用 static private function invokeRule($closure, $var = array()) { $reflect = new \ReflectionFunction($closure); $params = $reflect->getParameters(); $args = array(); foreach ($params as $param) { $name = $param->getName(); if (isset($var[$name])) { $args[] = $var[$name]; } elseif ($param->isDefaultValueAvailable()) { $args[] = $param->getDefaultValue(); } } return $reflect->invokeArgs($args); } }