CheckRestRouteBehavior.class.php 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  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. /**
  12. * 系统行为扩展 REST路由检测
  13. */
  14. class CheckRestRouteBehavior extends Behavior {
  15. // 行为参数定义(默认值) 可在项目配置中覆盖
  16. protected $options = array(
  17. 'URL_ROUTER_ON' => false, // 是否开启URL路由
  18. 'URL_ROUTE_RULES' => array(), // 默认路由规则,注:分组配置无法替代
  19. );
  20. /**
  21. * 路由检测
  22. * @access public
  23. * @return void
  24. */
  25. public function run(&$return) {
  26. $regx = trim($_SERVER['PATH_INFO'],'/');
  27. // 是否开启路由使用
  28. if(empty($regx) || !C('URL_ROUTER_ON')) $return = false;
  29. // 路由定义文件优先于config中的配置定义
  30. $routes = C('URL_ROUTE_RULES');
  31. if(is_array(C('routes'))) $routes = C('routes');
  32. // 路由处理
  33. if(!empty($routes)) {
  34. $depr = C('URL_PATHINFO_DEPR');
  35. foreach ($routes as $key=>$route){
  36. // 定义格式: array('路由规则或者正则','路由地址','路由参数','提交类型','资源类型')
  37. if(isset($route[3]) && strtolower($_SERVER['REQUEST_METHOD']) != strtolower($route[3])) {
  38. continue; // 如果设置了提交类型则过滤
  39. }
  40. if(isset($route[4]) && !in_array(__EXT__,explode(',',$route[4]),true)) {
  41. continue; // 如果设置了扩展名则过滤
  42. }
  43. if(0===strpos($route[0],'/') && preg_match($route[0],$regx,$matches)) { // 正则路由
  44. return self::parseRegex($matches,$route,$regx);
  45. }else{ // 规则路由
  46. $len1= substr_count($regx,'/');
  47. $len2 = substr_count($route[0],'/');
  48. if($len1>=$len2) {
  49. if('$' == substr($route[0],-1,1)) {// 完整匹配
  50. if($len1 != $len2) {
  51. continue;
  52. }else{
  53. $route[0] = substr($route[0],0,-1);
  54. }
  55. }
  56. $match = self::checkUrlMatch($regx,$route[0]);
  57. if($match) return $return = self::parseRule($route,$regx);
  58. }
  59. }
  60. }
  61. }
  62. $return = false;
  63. }
  64. // 检测URL和规则路由是否匹配
  65. static private function checkUrlMatch($regx,$rule) {
  66. $m1 = explode('/',$regx);
  67. $m2 = explode('/',$rule);
  68. $match = true; // 是否匹配
  69. foreach ($m2 as $key=>$val){
  70. if(':' == substr($val,0,1)) {// 动态变量
  71. if(strpos($val,'\\')) {
  72. $type = substr($val,-1);
  73. if('d'==$type && !is_numeric($m1[$key])) {
  74. $match = false;
  75. break;
  76. }
  77. }elseif(strpos($val,'^')){
  78. $array = explode('|',substr(strstr($val,'^'),1));
  79. if(in_array($m1[$key],$array)) {
  80. $match = false;
  81. break;
  82. }
  83. }
  84. }elseif(0 !== strcasecmp($val,$m1[$key])){
  85. $match = false;
  86. break;
  87. }
  88. }
  89. return $match;
  90. }
  91. static private function parseUrl($url) {
  92. $var = array();
  93. if(false !== strpos($url,'?')) { // [分组/模块/操作?]参数1=值1&参数2=值2...
  94. $info = parse_url($url);
  95. $path = explode('/',$info['path']);
  96. parse_str($info['query'],$var);
  97. }elseif(strpos($url,'/')){ // [分组/模块/操作]
  98. $path = explode('/',$url);
  99. }else{ // 参数1=值1&参数2=值2...
  100. parse_str($url,$var);
  101. }
  102. if(isset($path)) {
  103. $var[C('VAR_ACTION')] = array_pop($path);
  104. if(!empty($path)) {
  105. $var[C('VAR_MODULE')] = array_pop($path);
  106. }
  107. if(!empty($path)) {
  108. $var[C('VAR_GROUP')] = array_pop($path);
  109. }
  110. }
  111. return $var;
  112. }
  113. // 解析规则路由
  114. // array('路由规则','[分组/模块/操作]','额外参数1=值1&额外参数2=值2...','请求类型','资源类型')
  115. // array('路由规则','外部地址','重定向代码','请求类型','资源类型')
  116. // 路由规则中 :开头 表示动态变量
  117. // 外部地址中可以用动态变量 采用 :1 :2 的方式
  118. // array('news/:month/:day/:id','News/read?cate=1','status=1','post','html,xml'),
  119. // array('new/:id','/new.php?id=:1',301,'get','xml'), 重定向
  120. static private function parseRule($route,$regx) {
  121. // 获取路由地址规则
  122. $url = $route[1];
  123. // 获取URL地址中的参数
  124. $paths = explode('/',$regx);
  125. // 解析路由规则
  126. $matches = array();
  127. $rule = explode('/',$route[0]);
  128. foreach ($rule as $item){
  129. if(0===strpos($item,':')) { // 动态变量获取
  130. if($pos = strpos($item,'^') ) {
  131. $var = substr($item,1,$pos-1);
  132. }elseif(strpos($item,'\\')){
  133. $var = substr($item,1,-2);
  134. }else{
  135. $var = substr($item,1);
  136. }
  137. $matches[$var] = array_shift($paths);
  138. }else{ // 过滤URL中的静态变量
  139. array_shift($paths);
  140. }
  141. }
  142. if(0=== strpos($url,'/') || 0===strpos($url,'http')) { // 路由重定向跳转
  143. if(strpos($url,':')) { // 传递动态参数
  144. $values = array_values($matches);
  145. $url = preg_replace('/:(\d)/e','$values[\\1-1]',$url);
  146. }
  147. header("Location: $url", true,isset($route[2])?$route[2]:301);
  148. exit;
  149. }else{
  150. // 解析路由地址
  151. $var = self::parseUrl($url);
  152. // 解析路由地址里面的动态参数
  153. $values = array_values($matches);
  154. foreach ($var as $key=>$val){
  155. if(0===strpos($val,':')) {
  156. $var[$key] = $values[substr($val,1)-1];
  157. }
  158. }
  159. $var = array_merge($matches,$var);
  160. // 解析剩余的URL参数
  161. if($paths) {
  162. preg_replace('@(\w+)\/([^,\/]+)@e', '$var[strtolower(\'\\1\')]="\\2";', implode('/',$paths));
  163. }
  164. // 解析路由自动传人参数
  165. if(isset($route[2])) {
  166. parse_str($route[2],$params);
  167. $var = array_merge($var,$params);
  168. }
  169. $_GET = array_merge($var,$_GET);
  170. }
  171. return true;
  172. }
  173. // 解析正则路由
  174. // array('路由正则','[分组/模块/操作]?参数1=值1&参数2=值2...','额外参数','请求类型','资源类型')
  175. // array('路由正则','外部地址','重定向代码','请求类型','资源类型')
  176. // 参数值和外部地址中可以用动态变量 采用 :1 :2 的方式
  177. // array('/new\/(\d+)\/(\d+)/','News/read?id=:1&page=:2&cate=1','status=1','post','html,xml'),
  178. // array('/new\/(\d+)/','/new.php?id=:1&page=:2&status=1','301','get','html,xml'), 重定向
  179. static private function parseRegex($matches,$route,$regx) {
  180. // 获取路由地址规则
  181. $url = preg_replace('/:(\d)/e','$matches[\\1]',$route[1]);
  182. if(0=== strpos($url,'/') || 0===strpos($url,'http')) { // 路由重定向跳转
  183. header("Location: $url", true,isset($route[1])?$route[2]:301);
  184. exit;
  185. }else{
  186. // 解析路由地址
  187. $var = self::parseUrl($url);
  188. // 解析剩余的URL参数
  189. $regx = substr_replace($regx,'',0,strlen($matches[0]));
  190. if($regx) {
  191. preg_replace('@(\w+)\/([^,\/]+)@e', '$var[strtolower(\'\\1\')]="\\2";', $regx);
  192. }
  193. // 解析路由自动传人参数
  194. if(isset($route[2])) {
  195. parse_str($route[2],$params);
  196. $var = array_merge($var,$params);
  197. }
  198. $_GET = array_merge($var,$_GET);
  199. }
  200. return true;
  201. }
  202. }