PageRenderTime 42ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/ThinkPHP/Lib/Behavior/CheckRouteBehavior.class.php

https://bitbucket.org/zjut/labs
PHP | 212 lines | 168 code | 4 blank | 40 comment | 28 complexity | 0ce06eedc83cb914895fd34ef25c38e5 MD5 | raw file
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: liu21st <liu21st@gmail.com>
  10. // +----------------------------------------------------------------------
  11. defined('THINK_PATH') or exit();
  12. /**
  13. * 系统行为扩展:路由检测
  14. * @category Think
  15. * @package Think
  16. * @subpackage Behavior
  17. * @author liu21st <liu21st@gmail.com>
  18. */
  19. class CheckRouteBehavior extends Behavior {
  20. // 行为参数定义(默认值) 可在项目配置中覆盖
  21. protected $options = array(
  22. 'URL_ROUTER_ON' => false, // 是否开启URL路由
  23. 'URL_ROUTE_RULES' => array(), // 默认路由规则,注:分组配置无法替代
  24. );
  25. // 行为扩展的执行入口必须是run
  26. public function run(&$return){
  27. // 优先检测是否存在PATH_INFO
  28. $regx = trim($_SERVER['PATH_INFO'],'/');
  29. if(empty($regx)) return $return = true;
  30. // 是否开启路由使用
  31. if(!C('URL_ROUTER_ON')) return $return = false;
  32. // 路由定义文件优先于config中的配置定义
  33. $routes = C('URL_ROUTE_RULES');
  34. // 路由处理
  35. if(!empty($routes)) {
  36. $depr = C('URL_PATHINFO_DEPR');
  37. // 分隔符替换 确保路由定义使用统一的分隔符
  38. $regx = str_replace($depr,'/',$regx);
  39. foreach ($routes as $rule=>$route){
  40. if(0===strpos($rule,'/') && preg_match($rule,$regx,$matches)) { // 正则路由
  41. return $return = $this->parseRegex($matches,$route,$regx);
  42. }else{ // 规则路由
  43. $len1 = substr_count($regx,'/');
  44. $len2 = substr_count($rule,'/');
  45. if($len1>=$len2) {
  46. if('$' == substr($rule,-1,1)) {// 完整匹配
  47. if($len1 != $len2) {
  48. continue;
  49. }else{
  50. $rule = substr($rule,0,-1);
  51. }
  52. }
  53. $match = $this->checkUrlMatch($regx,$rule);
  54. if($match) return $return = $this->parseRule($rule,$route,$regx);
  55. }
  56. }
  57. }
  58. }
  59. $return = false;
  60. }
  61. // 检测URL和规则路由是否匹配
  62. private function checkUrlMatch($regx,$rule) {
  63. $m1 = explode('/',$regx);
  64. $m2 = explode('/',$rule);
  65. $match = true; // 是否匹配
  66. foreach ($m2 as $key=>$val){
  67. if(':' == substr($val,0,1)) {// 动态变量
  68. if(strpos($val,'\\')) {
  69. $type = substr($val,-1);
  70. if('d'==$type && !is_numeric($m1[$key])) {
  71. $match = false;
  72. break;
  73. }
  74. }elseif(strpos($val,'^')){
  75. $array = explode('|',substr(strstr($val,'^'),1));
  76. if(in_array($m1[$key],$array)) {
  77. $match = false;
  78. break;
  79. }
  80. }
  81. }elseif(0 !== strcasecmp($val,$m1[$key])){
  82. $match = false;
  83. break;
  84. }
  85. }
  86. return $match;
  87. }
  88. // 解析规范的路由地址
  89. // 地址格式 [分组/模块/操作?]参数1=值1&参数2=值2...
  90. private function parseUrl($url) {
  91. $var = array();
  92. if(false !== strpos($url,'?')) { // [分组/模块/操作?]参数1=值1&参数2=值2...
  93. $info = parse_url($url);
  94. $path = explode('/',$info['path']);
  95. parse_str($info['query'],$var);
  96. }elseif(strpos($url,'/')){ // [分组/模块/操作]
  97. $path = explode('/',$url);
  98. }else{ // 参数1=值1&参数2=值2...
  99. parse_str($url,$var);
  100. }
  101. if(isset($path)) {
  102. $var[C('VAR_ACTION')] = array_pop($path);
  103. if(!empty($path)) {
  104. $var[C('VAR_MODULE')] = array_pop($path);
  105. }
  106. if(!empty($path)) {
  107. $var[C('VAR_GROUP')] = array_pop($path);
  108. }
  109. }
  110. return $var;
  111. }
  112. // 解析规则路由
  113. // '路由规则'=>'[分组/模块/操作]?额外参数1=值1&额外参数2=值2...'
  114. // '路由规则'=>array('[分组/模块/操作]','额外参数1=值1&额外参数2=值2...')
  115. // '路由规则'=>'外部地址'
  116. // '路由规则'=>array('外部地址','重定向代码')
  117. // 路由规则中 :开头 表示动态变量
  118. // 外部地址中可以用动态变量 采用 :1 :2 的方式
  119. // 'news/:month/:day/:id'=>array('News/read?cate=1','status=1'),
  120. // 'new/:id'=>array('/new.php?id=:1',301), 重定向
  121. private function parseRule($rule,$route,$regx) {
  122. // 获取路由地址规则
  123. $url = is_array($route)?$route[0]:$route;
  124. // 获取URL地址中的参数
  125. $paths = explode('/',$regx);
  126. // 解析路由规则
  127. $matches = array();
  128. $rule = explode('/',$rule);
  129. foreach ($rule as $item){
  130. if(0===strpos($item,':')) { // 动态变量获取
  131. if($pos = strpos($item,'^') ) {
  132. $var = substr($item,1,$pos-1);
  133. }elseif(strpos($item,'\\')){
  134. $var = substr($item,1,-2);
  135. }else{
  136. $var = substr($item,1);
  137. }
  138. $matches[$var] = array_shift($paths);
  139. }else{ // 过滤URL中的静态变量
  140. array_shift($paths);
  141. }
  142. }
  143. if(0=== strpos($url,'/') || 0===strpos($url,'http')) { // 路由重定向跳转
  144. if(strpos($url,':')) { // 传递动态参数
  145. $values = array_values($matches);
  146. $url = preg_replace('/:(\d+)/e','$values[\\1-1]',$url);
  147. }
  148. header("Location: $url", true,(is_array($route) && isset($route[1]))?$route[1]:301);
  149. exit;
  150. }else{
  151. // 解析路由地址
  152. $var = $this->parseUrl($url);
  153. // 解析路由地址里面的动态参数
  154. $values = array_values($matches);
  155. foreach ($var as $key=>$val){
  156. if(0===strpos($val,':')) {
  157. $var[$key] = $values[substr($val,1)-1];
  158. }
  159. }
  160. $var = array_merge($matches,$var);
  161. // 解析剩余的URL参数
  162. if($paths) {
  163. preg_replace('@(\w+)\/([^\/]+)@e', '$var[strtolower(\'\\1\')]=strip_tags(\'\\2\');', implode('/',$paths));
  164. }
  165. // 解析路由自动传人参数
  166. if(is_array($route) && isset($route[1])) {
  167. parse_str($route[1],$params);
  168. $var = array_merge($var,$params);
  169. }
  170. $_GET = array_merge($var,$_GET);
  171. }
  172. return true;
  173. }
  174. // 解析正则路由
  175. // '路由正则'=>'[分组/模块/操作]?参数1=值1&参数2=值2...'
  176. // '路由正则'=>array('[分组/模块/操作]?参数1=值1&参数2=值2...','额外参数1=值1&额外参数2=值2...')
  177. // '路由正则'=>'外部地址'
  178. // '路由正则'=>array('外部地址','重定向代码')
  179. // 参数值和外部地址中可以用动态变量 采用 :1 :2 的方式
  180. // '/new\/(\d+)\/(\d+)/'=>array('News/read?id=:1&page=:2&cate=1','status=1'),
  181. // '/new\/(\d+)/'=>array('/new.php?id=:1&page=:2&status=1','301'), 重定向
  182. private function parseRegex($matches,$route,$regx) {
  183. // 获取路由地址规则
  184. $url = is_array($route)?$route[0]:$route;
  185. $url = preg_replace('/:(\d+)/e','$matches[\\1]',$url);
  186. if(0=== strpos($url,'/') || 0===strpos($url,'http')) { // 路由重定向跳转
  187. header("Location: $url", true,(is_array($route) && isset($route[1]))?$route[1]:301);
  188. exit;
  189. }else{
  190. // 解析路由地址
  191. $var = $this->parseUrl($url);
  192. // 解析剩余的URL参数
  193. $regx = substr_replace($regx,'',0,strlen($matches[0]));
  194. if($regx) {
  195. preg_replace('@(\w+)\/([^,\/]+)@e', '$var[strtolower(\'\\1\')]=strip_tags(\'\\2\');', $regx);
  196. }
  197. // 解析路由自动传人参数
  198. if(is_array($route) && isset($route[1])) {
  199. parse_str($route[1],$params);
  200. $var = array_merge($var,$params);
  201. }
  202. $_GET = array_merge($var,$_GET);
  203. }
  204. return true;
  205. }
  206. }