Route.php 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006~2017 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. namespace think;
  12. use think\exception\HttpException;
  13. class Route
  14. {
  15. // 路由规则
  16. private static $rules = [
  17. 'get' => [],
  18. 'post' => [],
  19. 'put' => [],
  20. 'delete' => [],
  21. 'patch' => [],
  22. 'head' => [],
  23. 'options' => [],
  24. '*' => [],
  25. 'alias' => [],
  26. 'domain' => [],
  27. 'pattern' => [],
  28. 'name' => [],
  29. ];
  30. // REST路由操作方法定义
  31. private static $rest = [
  32. 'index' => ['get', '', 'index'],
  33. 'create' => ['get', '/create', 'create'],
  34. 'edit' => ['get', '/:id/edit', 'edit'],
  35. 'read' => ['get', '/:id', 'read'],
  36. 'save' => ['post', '', 'save'],
  37. 'update' => ['put', '/:id', 'update'],
  38. 'delete' => ['delete', '/:id', 'delete'],
  39. ];
  40. // 不同请求类型的方法前缀
  41. private static $methodPrefix = [
  42. 'get' => 'get',
  43. 'post' => 'post',
  44. 'put' => 'put',
  45. 'delete' => 'delete',
  46. 'patch' => 'patch',
  47. ];
  48. // 子域名
  49. private static $subDomain = '';
  50. // 域名绑定
  51. private static $bind = [];
  52. // 当前分组信息
  53. private static $group = [];
  54. // 当前子域名绑定
  55. private static $domainBind;
  56. private static $domainRule;
  57. // 当前域名
  58. private static $domain;
  59. // 当前路由执行过程中的参数
  60. private static $option = [];
  61. /**
  62. * 注册变量规则
  63. * @access public
  64. * @param string|array $name 变量名
  65. * @param string $rule 变量规则
  66. * @return void
  67. */
  68. public static function pattern($name = null, $rule = '')
  69. {
  70. if (is_array($name)) {
  71. self::$rules['pattern'] = array_merge(self::$rules['pattern'], $name);
  72. } else {
  73. self::$rules['pattern'][$name] = $rule;
  74. }
  75. }
  76. /**
  77. * 注册子域名部署规则
  78. * @access public
  79. * @param string|array $domain 子域名
  80. * @param mixed $rule 路由规则
  81. * @param array $option 路由参数
  82. * @param array $pattern 变量规则
  83. * @return void
  84. */
  85. public static function domain($domain, $rule = '', $option = [], $pattern = [])
  86. {
  87. if (is_array($domain)) {
  88. foreach ($domain as $key => $item) {
  89. self::domain($key, $item, $option, $pattern);
  90. }
  91. } elseif ($rule instanceof \Closure) {
  92. // 执行闭包
  93. self::setDomain($domain);
  94. call_user_func_array($rule, []);
  95. self::setDomain(null);
  96. } elseif (is_array($rule)) {
  97. self::setDomain($domain);
  98. self::group('', function () use ($rule) {
  99. // 动态注册域名的路由规则
  100. self::registerRules($rule);
  101. }, $option, $pattern);
  102. self::setDomain(null);
  103. } else {
  104. self::$rules['domain'][$domain]['[bind]'] = [$rule, $option, $pattern];
  105. }
  106. }
  107. private static function setDomain($domain)
  108. {
  109. self::$domain = $domain;
  110. }
  111. /**
  112. * 设置路由绑定
  113. * @access public
  114. * @param mixed $bind 绑定信息
  115. * @param string $type 绑定类型 默认为module 支持 namespace class controller
  116. * @return mixed
  117. */
  118. public static function bind($bind, $type = 'module')
  119. {
  120. self::$bind = ['type' => $type, $type => $bind];
  121. }
  122. /**
  123. * 设置或者获取路由标识
  124. * @access public
  125. * @param string|array $name 路由命名标识 数组表示批量设置
  126. * @param array $value 路由地址及变量信息
  127. * @return array
  128. */
  129. public static function name($name = '', $value = null)
  130. {
  131. if (is_array($name)) {
  132. return self::$rules['name'] = $name;
  133. } elseif ('' === $name) {
  134. return self::$rules['name'];
  135. } elseif (!is_null($value)) {
  136. self::$rules['name'][strtolower($name)][] = $value;
  137. } else {
  138. $name = strtolower($name);
  139. return isset(self::$rules['name'][$name]) ? self::$rules['name'][$name] : null;
  140. }
  141. }
  142. /**
  143. * 读取路由绑定
  144. * @access public
  145. * @param string $type 绑定类型
  146. * @return mixed
  147. */
  148. public static function getBind($type)
  149. {
  150. return isset(self::$bind[$type]) ? self::$bind[$type] : null;
  151. }
  152. /**
  153. * 导入配置文件的路由规则
  154. * @access public
  155. * @param array $rule 路由规则
  156. * @param string $type 请求类型
  157. * @return void
  158. */
  159. public static function import(array $rule, $type = '*')
  160. {
  161. // 检查域名部署
  162. if (isset($rule['__domain__'])) {
  163. self::domain($rule['__domain__']);
  164. unset($rule['__domain__']);
  165. }
  166. // 检查变量规则
  167. if (isset($rule['__pattern__'])) {
  168. self::pattern($rule['__pattern__']);
  169. unset($rule['__pattern__']);
  170. }
  171. // 检查路由别名
  172. if (isset($rule['__alias__'])) {
  173. self::alias($rule['__alias__']);
  174. unset($rule['__alias__']);
  175. }
  176. // 检查资源路由
  177. if (isset($rule['__rest__'])) {
  178. self::resource($rule['__rest__']);
  179. unset($rule['__rest__']);
  180. }
  181. self::registerRules($rule, strtolower($type));
  182. }
  183. // 批量注册路由
  184. protected static function registerRules($rules, $type = '*')
  185. {
  186. foreach ($rules as $key => $val) {
  187. if (is_numeric($key)) {
  188. $key = array_shift($val);
  189. }
  190. if (empty($val)) {
  191. continue;
  192. }
  193. if (is_string($key) && 0 === strpos($key, '[')) {
  194. $key = substr($key, 1, -1);
  195. self::group($key, $val);
  196. } elseif (is_array($val)) {
  197. self::setRule($key, $val[0], $type, $val[1], isset($val[2]) ? $val[2] : []);
  198. } else {
  199. self::setRule($key, $val, $type);
  200. }
  201. }
  202. }
  203. /**
  204. * 注册路由规则
  205. * @access public
  206. * @param string $rule 路由规则
  207. * @param string $route 路由地址
  208. * @param string $type 请求类型
  209. * @param array $option 路由参数
  210. * @param array $pattern 变量规则
  211. * @return void
  212. */
  213. public static function rule($rule, $route = '', $type = '*', $option = [], $pattern = [])
  214. {
  215. $group = self::getGroup('name');
  216. if (!is_null($group)) {
  217. // 路由分组
  218. $option = array_merge(self::getGroup('option'), $option);
  219. $pattern = array_merge(self::getGroup('pattern'), $pattern);
  220. }
  221. $type = strtolower($type);
  222. if (strpos($type, '|')) {
  223. $option['method'] = $type;
  224. $type = '*';
  225. }
  226. if (is_array($rule) && empty($route)) {
  227. foreach ($rule as $key => $val) {
  228. if (is_numeric($key)) {
  229. $key = array_shift($val);
  230. }
  231. if (is_array($val)) {
  232. $route = $val[0];
  233. $option1 = array_merge($option, $val[1]);
  234. $pattern1 = array_merge($pattern, isset($val[2]) ? $val[2] : []);
  235. } else {
  236. $route = $val;
  237. }
  238. self::setRule($key, $route, $type, isset($option1) ? $option1 : $option, isset($pattern1) ? $pattern1 : $pattern, $group);
  239. }
  240. } else {
  241. self::setRule($rule, $route, $type, $option, $pattern, $group);
  242. }
  243. }
  244. /**
  245. * 设置路由规则
  246. * @access public
  247. * @param string $rule 路由规则
  248. * @param string $route 路由地址
  249. * @param string $type 请求类型
  250. * @param array $option 路由参数
  251. * @param array $pattern 变量规则
  252. * @param string $group 所属分组
  253. * @return void
  254. */
  255. protected static function setRule($rule, $route, $type = '*', $option = [], $pattern = [], $group = '')
  256. {
  257. if (is_array($rule)) {
  258. $name = $rule[0];
  259. $rule = $rule[1];
  260. } elseif (is_string($route)) {
  261. $name = $route;
  262. }
  263. if (!isset($option['complete_match'])) {
  264. if (Config::get('route_complete_match')) {
  265. $option['complete_match'] = true;
  266. } elseif ('$' == substr($rule, -1, 1)) {
  267. // 是否完整匹配
  268. $option['complete_match'] = true;
  269. }
  270. } elseif (empty($option['complete_match']) && '$' == substr($rule, -1, 1)) {
  271. // 是否完整匹配
  272. $option['complete_match'] = true;
  273. }
  274. if ('$' == substr($rule, -1, 1)) {
  275. $rule = substr($rule, 0, -1);
  276. }
  277. if ('/' != $rule || $group) {
  278. $rule = trim($rule, '/');
  279. }
  280. $vars = self::parseVar($rule);
  281. if (isset($name)) {
  282. $key = $group ? $group . ($rule ? '/' . $rule : '') : $rule;
  283. self::name($name, [$key, $vars, self::$domain]);
  284. }
  285. if ($group) {
  286. if ('*' != $type) {
  287. $option['method'] = $type;
  288. }
  289. if (self::$domain) {
  290. self::$rules['domain'][self::$domain]['*'][$group]['rule'][] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern];
  291. } else {
  292. self::$rules['*'][$group]['rule'][] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern];
  293. }
  294. } else {
  295. if ('*' != $type && isset(self::$rules['*'][$rule])) {
  296. unset(self::$rules['*'][$rule]);
  297. }
  298. if (self::$domain) {
  299. self::$rules['domain'][self::$domain][$type][$rule] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern];
  300. } else {
  301. self::$rules[$type][$rule] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern];
  302. }
  303. if ('*' == $type) {
  304. // 注册路由快捷方式
  305. foreach (['get', 'post', 'put', 'delete', 'patch', 'head', 'options'] as $method) {
  306. if (self::$domain) {
  307. self::$rules['domain'][self::$domain][$method][$rule] = true;
  308. } else {
  309. self::$rules[$method][$rule] = true;
  310. }
  311. }
  312. }
  313. }
  314. }
  315. /**
  316. * 设置当前执行的参数信息
  317. * @access public
  318. * @param array $options 参数信息
  319. * @return mixed
  320. */
  321. protected static function setOption($options = [])
  322. {
  323. self::$option[] = $options;
  324. }
  325. /**
  326. * 获取当前执行的所有参数信息
  327. * @access public
  328. * @return array
  329. */
  330. public static function getOption()
  331. {
  332. return self::$option;
  333. }
  334. /**
  335. * 获取当前的分组信息
  336. * @access public
  337. * @param string $type 分组信息名称 name option pattern
  338. * @return mixed
  339. */
  340. public static function getGroup($type)
  341. {
  342. if (isset(self::$group[$type])) {
  343. return self::$group[$type];
  344. } else {
  345. return 'name' == $type ? null : [];
  346. }
  347. }
  348. /**
  349. * 设置当前的路由分组
  350. * @access public
  351. * @param string $name 分组名称
  352. * @param array $option 分组路由参数
  353. * @param array $pattern 分组变量规则
  354. * @return void
  355. */
  356. public static function setGroup($name, $option = [], $pattern = [])
  357. {
  358. self::$group['name'] = $name;
  359. self::$group['option'] = $option ?: [];
  360. self::$group['pattern'] = $pattern ?: [];
  361. }
  362. /**
  363. * 注册路由分组
  364. * @access public
  365. * @param string|array $name 分组名称或者参数
  366. * @param array|\Closure $routes 路由地址
  367. * @param array $option 路由参数
  368. * @param array $pattern 变量规则
  369. * @return void
  370. */
  371. public static function group($name, $routes, $option = [], $pattern = [])
  372. {
  373. if (is_array($name)) {
  374. $option = $name;
  375. $name = isset($option['name']) ? $option['name'] : '';
  376. }
  377. // 分组
  378. $currentGroup = self::getGroup('name');
  379. if ($currentGroup) {
  380. $name = $currentGroup . ($name ? '/' . ltrim($name, '/') : '');
  381. }
  382. if (!empty($name)) {
  383. if ($routes instanceof \Closure) {
  384. $currentOption = self::getGroup('option');
  385. $currentPattern = self::getGroup('pattern');
  386. self::setGroup($name, array_merge($currentOption, $option), array_merge($currentPattern, $pattern));
  387. call_user_func_array($routes, []);
  388. self::setGroup($currentGroup, $currentOption, $currentPattern);
  389. if ($currentGroup != $name) {
  390. self::$rules['*'][$name]['route'] = '';
  391. self::$rules['*'][$name]['var'] = self::parseVar($name);
  392. self::$rules['*'][$name]['option'] = $option;
  393. self::$rules['*'][$name]['pattern'] = $pattern;
  394. }
  395. } else {
  396. $item = [];
  397. foreach ($routes as $key => $val) {
  398. if (is_numeric($key)) {
  399. $key = array_shift($val);
  400. }
  401. if (is_array($val)) {
  402. $route = $val[0];
  403. $option1 = array_merge($option, isset($val[1]) ? $val[1] : []);
  404. $pattern1 = array_merge($pattern, isset($val[2]) ? $val[2] : []);
  405. } else {
  406. $route = $val;
  407. }
  408. $options = isset($option1) ? $option1 : $option;
  409. $patterns = isset($pattern1) ? $pattern1 : $pattern;
  410. if ('$' == substr($key, -1, 1)) {
  411. // 是否完整匹配
  412. $options['complete_match'] = true;
  413. $key = substr($key, 0, -1);
  414. }
  415. $key = trim($key, '/');
  416. $vars = self::parseVar($key);
  417. $item[] = ['rule' => $key, 'route' => $route, 'var' => $vars, 'option' => $options, 'pattern' => $patterns];
  418. // 设置路由标识
  419. self::name($route, [$name . ($key ? '/' . $key : ''), $vars, self::$domain]);
  420. }
  421. self::$rules['*'][$name] = ['rule' => $item, 'route' => '', 'var' => [], 'option' => $option, 'pattern' => $pattern];
  422. }
  423. foreach (['get', 'post', 'put', 'delete', 'patch', 'head', 'options'] as $method) {
  424. if (!isset(self::$rules[$method][$name])) {
  425. self::$rules[$method][$name] = true;
  426. } elseif (is_array(self::$rules[$method][$name])) {
  427. self::$rules[$method][$name] = array_merge(self::$rules['*'][$name], self::$rules[$method][$name]);
  428. }
  429. }
  430. } elseif ($routes instanceof \Closure) {
  431. // 闭包注册
  432. $currentOption = self::getGroup('option');
  433. $currentPattern = self::getGroup('pattern');
  434. self::setGroup('', array_merge($currentOption, $option), array_merge($currentPattern, $pattern));
  435. call_user_func_array($routes, []);
  436. self::setGroup($currentGroup, $currentOption, $currentPattern);
  437. } else {
  438. // 批量注册路由
  439. self::rule($routes, '', '*', $option, $pattern);
  440. }
  441. }
  442. /**
  443. * 注册路由
  444. * @access public
  445. * @param string $rule 路由规则
  446. * @param string $route 路由地址
  447. * @param array $option 路由参数
  448. * @param array $pattern 变量规则
  449. * @return void
  450. */
  451. public static function any($rule, $route = '', $option = [], $pattern = [])
  452. {
  453. self::rule($rule, $route, '*', $option, $pattern);
  454. }
  455. /**
  456. * 注册GET路由
  457. * @access public
  458. * @param string $rule 路由规则
  459. * @param string $route 路由地址
  460. * @param array $option 路由参数
  461. * @param array $pattern 变量规则
  462. * @return void
  463. */
  464. public static function get($rule, $route = '', $option = [], $pattern = [])
  465. {
  466. self::rule($rule, $route, 'GET', $option, $pattern);
  467. }
  468. /**
  469. * 注册POST路由
  470. * @access public
  471. * @param string $rule 路由规则
  472. * @param string $route 路由地址
  473. * @param array $option 路由参数
  474. * @param array $pattern 变量规则
  475. * @return void
  476. */
  477. public static function post($rule, $route = '', $option = [], $pattern = [])
  478. {
  479. self::rule($rule, $route, 'POST', $option, $pattern);
  480. }
  481. /**
  482. * 注册PUT路由
  483. * @access public
  484. * @param string $rule 路由规则
  485. * @param string $route 路由地址
  486. * @param array $option 路由参数
  487. * @param array $pattern 变量规则
  488. * @return void
  489. */
  490. public static function put($rule, $route = '', $option = [], $pattern = [])
  491. {
  492. self::rule($rule, $route, 'PUT', $option, $pattern);
  493. }
  494. /**
  495. * 注册DELETE路由
  496. * @access public
  497. * @param string $rule 路由规则
  498. * @param string $route 路由地址
  499. * @param array $option 路由参数
  500. * @param array $pattern 变量规则
  501. * @return void
  502. */
  503. public static function delete($rule, $route = '', $option = [], $pattern = [])
  504. {
  505. self::rule($rule, $route, 'DELETE', $option, $pattern);
  506. }
  507. /**
  508. * 注册PATCH路由
  509. * @access public
  510. * @param string $rule 路由规则
  511. * @param string $route 路由地址
  512. * @param array $option 路由参数
  513. * @param array $pattern 变量规则
  514. * @return void
  515. */
  516. public static function patch($rule, $route = '', $option = [], $pattern = [])
  517. {
  518. self::rule($rule, $route, 'PATCH', $option, $pattern);
  519. }
  520. /**
  521. * 注册资源路由
  522. * @access public
  523. * @param string $rule 路由规则
  524. * @param string $route 路由地址
  525. * @param array $option 路由参数
  526. * @param array $pattern 变量规则
  527. * @return void
  528. */
  529. public static function resource($rule, $route = '', $option = [], $pattern = [])
  530. {
  531. if (is_array($rule)) {
  532. foreach ($rule as $key => $val) {
  533. if (is_array($val)) {
  534. list($val, $option, $pattern) = array_pad($val, 3, []);
  535. }
  536. self::resource($key, $val, $option, $pattern);
  537. }
  538. } else {
  539. if (strpos($rule, '.')) {
  540. // 注册嵌套资源路由
  541. $array = explode('.', $rule);
  542. $last = array_pop($array);
  543. $item = [];
  544. foreach ($array as $val) {
  545. $item[] = $val . '/:' . (isset($option['var'][$val]) ? $option['var'][$val] : $val . '_id');
  546. }
  547. $rule = implode('/', $item) . '/' . $last;
  548. }
  549. // 注册资源路由
  550. foreach (self::$rest as $key => $val) {
  551. if ((isset($option['only']) && !in_array($key, $option['only']))
  552. || (isset($option['except']) && in_array($key, $option['except']))) {
  553. continue;
  554. }
  555. if (isset($last) && strpos($val[1], ':id') && isset($option['var'][$last])) {
  556. $val[1] = str_replace(':id', ':' . $option['var'][$last], $val[1]);
  557. } elseif (strpos($val[1], ':id') && isset($option['var'][$rule])) {
  558. $val[1] = str_replace(':id', ':' . $option['var'][$rule], $val[1]);
  559. }
  560. $item = ltrim($rule . $val[1], '/');
  561. $option['rest'] = $key;
  562. self::rule($item . '$', $route . '/' . $val[2], $val[0], $option, $pattern);
  563. }
  564. }
  565. }
  566. /**
  567. * 注册控制器路由 操作方法对应不同的请求后缀
  568. * @access public
  569. * @param string $rule 路由规则
  570. * @param string $route 路由地址
  571. * @param array $option 路由参数
  572. * @param array $pattern 变量规则
  573. * @return void
  574. */
  575. public static function controller($rule, $route = '', $option = [], $pattern = [])
  576. {
  577. foreach (self::$methodPrefix as $type => $val) {
  578. self::$type($rule . '/:action', $route . '/' . $val . ':action', $option, $pattern);
  579. }
  580. }
  581. /**
  582. * 注册别名路由
  583. * @access public
  584. * @param string|array $rule 路由别名
  585. * @param string $route 路由地址
  586. * @param array $option 路由参数
  587. * @return void
  588. */
  589. public static function alias($rule = null, $route = '', $option = [])
  590. {
  591. if (is_array($rule)) {
  592. self::$rules['alias'] = array_merge(self::$rules['alias'], $rule);
  593. } else {
  594. self::$rules['alias'][$rule] = $option ? [$route, $option] : $route;
  595. }
  596. }
  597. /**
  598. * 设置不同请求类型下面的方法前缀
  599. * @access public
  600. * @param string $method 请求类型
  601. * @param string $prefix 类型前缀
  602. * @return void
  603. */
  604. public static function setMethodPrefix($method, $prefix = '')
  605. {
  606. if (is_array($method)) {
  607. self::$methodPrefix = array_merge(self::$methodPrefix, array_change_key_case($method));
  608. } else {
  609. self::$methodPrefix[strtolower($method)] = $prefix;
  610. }
  611. }
  612. /**
  613. * rest方法定义和修改
  614. * @access public
  615. * @param string $name 方法名称
  616. * @param array|bool $resource 资源
  617. * @return void
  618. */
  619. public static function rest($name, $resource = [])
  620. {
  621. if (is_array($name)) {
  622. self::$rest = $resource ? $name : array_merge(self::$rest, $name);
  623. } else {
  624. self::$rest[$name] = $resource;
  625. }
  626. }
  627. /**
  628. * 注册未匹配路由规则后的处理
  629. * @access public
  630. * @param string $route 路由地址
  631. * @param string $method 请求类型
  632. * @param array $option 路由参数
  633. * @return void
  634. */
  635. public static function miss($route, $method = '*', $option = [])
  636. {
  637. self::rule('__miss__', $route, $method, $option, []);
  638. }
  639. /**
  640. * 注册一个自动解析的URL路由
  641. * @access public
  642. * @param string $route 路由地址
  643. * @return void
  644. */
  645. public static function auto($route)
  646. {
  647. self::rule('__auto__', $route, '*', [], []);
  648. }
  649. /**
  650. * 获取或者批量设置路由定义
  651. * @access public
  652. * @param mixed $rules 请求类型或者路由定义数组
  653. * @return array
  654. */
  655. public static function rules($rules = '')
  656. {
  657. if (is_array($rules)) {
  658. self::$rules = $rules;
  659. } elseif ($rules) {
  660. return true === $rules ? self::$rules : self::$rules[strtolower($rules)];
  661. } else {
  662. $rules = self::$rules;
  663. unset($rules['pattern'], $rules['alias'], $rules['domain'], $rules['name']);
  664. return $rules;
  665. }
  666. }
  667. /**
  668. * 检测子域名部署
  669. * @access public
  670. * @param Request $request Request请求对象
  671. * @param array $currentRules 当前路由规则
  672. * @param string $method 请求类型
  673. * @return void
  674. */
  675. public static function checkDomain($request, &$currentRules, $method = 'get')
  676. {
  677. // 域名规则
  678. $rules = self::$rules['domain'];
  679. // 开启子域名部署 支持二级和三级域名
  680. if (!empty($rules)) {
  681. $host = $request->host();
  682. if (isset($rules[$host])) {
  683. // 完整域名或者IP配置
  684. $item = $rules[$host];
  685. } else {
  686. $rootDomain = Config::get('url_domain_root');
  687. if ($rootDomain) {
  688. // 配置域名根 例如 thinkphp.cn 163.com.cn 如果是国家级域名 com.cn net.cn 之类的域名需要配置
  689. $domain = explode('.', rtrim(stristr($host, $rootDomain, true), '.'));
  690. } else {
  691. $domain = explode('.', $host, -2);
  692. }
  693. // 子域名配置
  694. if (!empty($domain)) {
  695. // 当前子域名
  696. $subDomain = implode('.', $domain);
  697. self::$subDomain = $subDomain;
  698. $domain2 = array_pop($domain);
  699. if ($domain) {
  700. // 存在三级域名
  701. $domain3 = array_pop($domain);
  702. }
  703. if ($subDomain && isset($rules[$subDomain])) {
  704. // 子域名配置
  705. $item = $rules[$subDomain];
  706. } elseif (isset($rules['*.' . $domain2]) && !empty($domain3)) {
  707. // 泛三级域名
  708. $item = $rules['*.' . $domain2];
  709. $panDomain = $domain3;
  710. } elseif (isset($rules['*']) && !empty($domain2)) {
  711. // 泛二级域名
  712. if ('www' != $domain2) {
  713. $item = $rules['*'];
  714. $panDomain = $domain2;
  715. }
  716. }
  717. }
  718. }
  719. if (!empty($item)) {
  720. if (isset($panDomain)) {
  721. // 保存当前泛域名
  722. $request->route(['__domain__' => $panDomain]);
  723. }
  724. if (isset($item['[bind]'])) {
  725. // 解析子域名部署规则
  726. list($rule, $option, $pattern) = $item['[bind]'];
  727. if (!empty($option['https']) && !$request->isSsl()) {
  728. // https检测
  729. throw new HttpException(404, 'must use https request:' . $host);
  730. }
  731. if (strpos($rule, '?')) {
  732. // 传入其它参数
  733. $array = parse_url($rule);
  734. $result = $array['path'];
  735. parse_str($array['query'], $params);
  736. if (isset($panDomain)) {
  737. $pos = array_search('*', $params);
  738. if (false !== $pos) {
  739. // 泛域名作为参数
  740. $params[$pos] = $panDomain;
  741. }
  742. }
  743. $_GET = array_merge($_GET, $params);
  744. } else {
  745. $result = $rule;
  746. }
  747. if (0 === strpos($result, '\\')) {
  748. // 绑定到命名空间 例如 \app\index\behavior
  749. self::$bind = ['type' => 'namespace', 'namespace' => $result];
  750. } elseif (0 === strpos($result, '@')) {
  751. // 绑定到类 例如 @app\index\controller\User
  752. self::$bind = ['type' => 'class', 'class' => substr($result, 1)];
  753. } else {
  754. // 绑定到模块/控制器 例如 index/user
  755. self::$bind = ['type' => 'module', 'module' => $result];
  756. }
  757. self::$domainBind = true;
  758. } else {
  759. self::$domainRule = $item;
  760. $currentRules = isset($item[$method]) ? $item[$method] : $item['*'];
  761. }
  762. }
  763. }
  764. }
  765. /**
  766. * 检测URL路由
  767. * @access public
  768. * @param Request $request Request请求对象
  769. * @param string $url URL地址
  770. * @param string $depr URL分隔符
  771. * @param bool $checkDomain 是否检测域名规则
  772. * @return false|array
  773. */
  774. public static function check($request, $url, $depr = '/', $checkDomain = false)
  775. {
  776. // 分隔符替换 确保路由定义使用统一的分隔符
  777. $url = str_replace($depr, '|', $url);
  778. if (strpos($url, '|') && isset(self::$rules['alias'][strstr($url, '|', true)])) {
  779. // 检测路由别名
  780. $result = self::checkRouteAlias($request, $url, $depr);
  781. if (false !== $result) {
  782. return $result;
  783. }
  784. }
  785. $method = strtolower($request->method());
  786. // 获取当前请求类型的路由规则
  787. $rules = self::$rules[$method];
  788. // 检测域名部署
  789. if ($checkDomain) {
  790. self::checkDomain($request, $rules, $method);
  791. }
  792. // 检测URL绑定
  793. $return = self::checkUrlBind($url, $rules, $depr);
  794. if (false !== $return) {
  795. return $return;
  796. }
  797. if ('|' != $url) {
  798. $url = rtrim($url, '|');
  799. }
  800. $item = str_replace('|', '/', $url);
  801. if (isset($rules[$item])) {
  802. // 静态路由规则检测
  803. $rule = $rules[$item];
  804. if (true === $rule) {
  805. $rule = self::getRouteExpress($item);
  806. }
  807. if (!empty($rule['route']) && self::checkOption($rule['option'], $request)) {
  808. self::setOption($rule['option']);
  809. return self::parseRule($item, $rule['route'], $url, $rule['option']);
  810. }
  811. }
  812. // 路由规则检测
  813. if (!empty($rules)) {
  814. return self::checkRoute($request, $rules, $url, $depr);
  815. }
  816. return false;
  817. }
  818. private static function getRouteExpress($key)
  819. {
  820. return self::$domainRule ? self::$domainRule['*'][$key] : self::$rules['*'][$key];
  821. }
  822. /**
  823. * 检测路由规则
  824. * @access private
  825. * @param Request $request
  826. * @param array $rules 路由规则
  827. * @param string $url URL地址
  828. * @param string $depr URL分割符
  829. * @param string $group 路由分组名
  830. * @param array $options 路由参数(分组)
  831. * @return mixed
  832. */
  833. private static function checkRoute($request, $rules, $url, $depr = '/', $group = '', $options = [])
  834. {
  835. foreach ($rules as $key => $item) {
  836. if (true === $item) {
  837. $item = self::getRouteExpress($key);
  838. }
  839. if (!isset($item['rule'])) {
  840. continue;
  841. }
  842. $rule = $item['rule'];
  843. $route = $item['route'];
  844. $vars = $item['var'];
  845. $option = $item['option'];
  846. $pattern = $item['pattern'];
  847. // 检查参数有效性
  848. if (!self::checkOption($option, $request)) {
  849. continue;
  850. }
  851. if (isset($option['ext'])) {
  852. // 路由ext参数 优先于系统配置的URL伪静态后缀参数
  853. $url = preg_replace('/\.' . $request->ext() . '$/i', '', $url);
  854. }
  855. if (is_array($rule)) {
  856. // 分组路由
  857. $pos = strpos(str_replace('<', ':', $key), ':');
  858. if (false !== $pos) {
  859. $str = substr($key, 0, $pos);
  860. } else {
  861. $str = $key;
  862. }
  863. if (is_string($str) && $str && 0 !== strpos(str_replace('|', '/', $url), $str)) {
  864. continue;
  865. }
  866. self::setOption($option);
  867. $result = self::checkRoute($request, $rule, $url, $depr, $key, $option);
  868. if (false !== $result) {
  869. return $result;
  870. }
  871. } elseif ($route) {
  872. if ('__miss__' == $rule || '__auto__' == $rule) {
  873. // 指定特殊路由
  874. $var = trim($rule, '__');
  875. ${$var} = $item;
  876. continue;
  877. }
  878. if ($group) {
  879. $rule = $group . ($rule ? '/' . ltrim($rule, '/') : '');
  880. }
  881. self::setOption($option);
  882. if (isset($options['bind_model']) && isset($option['bind_model'])) {
  883. $option['bind_model'] = array_merge($options['bind_model'], $option['bind_model']);
  884. }
  885. $result = self::checkRule($rule, $route, $url, $pattern, $option, $depr);
  886. if (false !== $result) {
  887. return $result;
  888. }
  889. }
  890. }
  891. if (isset($auto)) {
  892. // 自动解析URL地址
  893. return self::parseUrl($auto['route'] . '/' . $url, $depr);
  894. } elseif (isset($miss)) {
  895. // 未匹配所有路由的路由规则处理
  896. return self::parseRule('', $miss['route'], $url, $miss['option']);
  897. }
  898. return false;
  899. }
  900. /**
  901. * 检测路由别名
  902. * @access private
  903. * @param Request $request
  904. * @param string $url URL地址
  905. * @param string $depr URL分隔符
  906. * @return mixed
  907. */
  908. private static function checkRouteAlias($request, $url, $depr)
  909. {
  910. $array = explode('|', $url);
  911. $alias = array_shift($array);
  912. $item = self::$rules['alias'][$alias];
  913. if (is_array($item)) {
  914. list($rule, $option) = $item;
  915. $action = $array[0];
  916. if (isset($option['allow']) && !in_array($action, explode(',', $option['allow']))) {
  917. // 允许操作
  918. return false;
  919. } elseif (isset($option['except']) && in_array($action, explode(',', $option['except']))) {
  920. // 排除操作
  921. return false;
  922. }
  923. if (isset($option['method'][$action])) {
  924. $option['method'] = $option['method'][$action];
  925. }
  926. } else {
  927. $rule = $item;
  928. }
  929. $bind = implode('|', $array);
  930. // 参数有效性检查
  931. if (isset($option) && !self::checkOption($option, $request)) {
  932. // 路由不匹配
  933. return false;
  934. } elseif (0 === strpos($rule, '\\')) {
  935. // 路由到类
  936. return self::bindToClass($bind, substr($rule, 1), $depr);
  937. } elseif (0 === strpos($rule, '@')) {
  938. // 路由到控制器类
  939. return self::bindToController($bind, substr($rule, 1), $depr);
  940. } else {
  941. // 路由到模块/控制器
  942. return self::bindToModule($bind, $rule, $depr);
  943. }
  944. }
  945. /**
  946. * 检测URL绑定
  947. * @access private
  948. * @param string $url URL地址
  949. * @param array $rules 路由规则
  950. * @param string $depr URL分隔符
  951. * @return mixed
  952. */
  953. private static function checkUrlBind(&$url, &$rules, $depr = '/')
  954. {
  955. if (!empty(self::$bind)) {
  956. $type = self::$bind['type'];
  957. $bind = self::$bind[$type];
  958. // 记录绑定信息
  959. App::$debug && Log::record('[ BIND ] ' . var_export($bind, true), 'info');
  960. // 如果有URL绑定 则进行绑定检测
  961. switch ($type) {
  962. case 'class':
  963. // 绑定到类
  964. return self::bindToClass($url, $bind, $depr);
  965. case 'controller':
  966. // 绑定到控制器类
  967. return self::bindToController($url, $bind, $depr);
  968. case 'namespace':
  969. // 绑定到命名空间
  970. return self::bindToNamespace($url, $bind, $depr);
  971. }
  972. }
  973. return false;
  974. }
  975. /**
  976. * 绑定到类
  977. * @access public
  978. * @param string $url URL地址
  979. * @param string $class 类名(带命名空间)
  980. * @param string $depr URL分隔符
  981. * @return array
  982. */
  983. public static function bindToClass($url, $class, $depr = '/')
  984. {
  985. $url = str_replace($depr, '|', $url);
  986. $array = explode('|', $url, 2);
  987. $action = !empty($array[0]) ? $array[0] : Config::get('default_action');
  988. if (!empty($array[1])) {
  989. self::parseUrlParams($array[1]);
  990. }
  991. return ['type' => 'method', 'method' => [$class, $action]];
  992. }
  993. /**
  994. * 绑定到命名空间
  995. * @access public
  996. * @param string $url URL地址
  997. * @param string $namespace 命名空间
  998. * @param string $depr URL分隔符
  999. * @return array
  1000. */
  1001. public static function bindToNamespace($url, $namespace, $depr = '/')
  1002. {
  1003. $url = str_replace($depr, '|', $url);
  1004. $array = explode('|', $url, 3);
  1005. $class = !empty($array[0]) ? $array[0] : Config::get('default_controller');
  1006. $method = !empty($array[1]) ? $array[1] : Config::get('default_action');
  1007. if (!empty($array[2])) {
  1008. self::parseUrlParams($array[2]);
  1009. }
  1010. return ['type' => 'method', 'method' => [$namespace . '\\' . Loader::parseName($class, 1), $method]];
  1011. }
  1012. /**
  1013. * 绑定到控制器类
  1014. * @access public
  1015. * @param string $url URL地址
  1016. * @param string $controller 控制器名 (支持带模块名 index/user )
  1017. * @param string $depr URL分隔符
  1018. * @return array
  1019. */
  1020. public static function bindToController($url, $controller, $depr = '/')
  1021. {
  1022. $url = str_replace($depr, '|', $url);
  1023. $array = explode('|', $url, 2);
  1024. $action = !empty($array[0]) ? $array[0] : Config::get('default_action');
  1025. if (!empty($array[1])) {
  1026. self::parseUrlParams($array[1]);
  1027. }
  1028. return ['type' => 'controller', 'controller' => $controller . '/' . $action];
  1029. }
  1030. /**
  1031. * 绑定到模块/控制器
  1032. * @access public
  1033. * @param string $url URL地址
  1034. * @param string $controller 控制器类名(带命名空间)
  1035. * @param string $depr URL分隔符
  1036. * @return array
  1037. */
  1038. public static function bindToModule($url, $controller, $depr = '/')
  1039. {
  1040. $url = str_replace($depr, '|', $url);
  1041. $array = explode('|', $url, 2);
  1042. $action = !empty($array[0]) ? $array[0] : Config::get('default_action');
  1043. if (!empty($array[1])) {
  1044. self::parseUrlParams($array[1]);
  1045. }
  1046. return ['type' => 'module', 'module' => $controller . '/' . $action];
  1047. }
  1048. /**
  1049. * 路由参数有效性检查
  1050. * @access private
  1051. * @param array $option 路由参数
  1052. * @param Request $request Request对象
  1053. * @return bool
  1054. */
  1055. private static function checkOption($option, $request)
  1056. {
  1057. if ((isset($option['method']) && is_string($option['method']) && false === stripos($option['method'], $request->method()))
  1058. || (isset($option['ajax']) && $option['ajax'] && !$request->isAjax()) // Ajax检测
  1059. || (isset($option['ajax']) && !$option['ajax'] && $request->isAjax()) // 非Ajax检测
  1060. || (isset($option['pjax']) && $option['pjax'] && !$request->isPjax()) // Pjax检测
  1061. || (isset($option['pjax']) && !$option['pjax'] && $request->isPjax()) // 非Pjax检测
  1062. || (isset($option['ext']) && false === stripos('|' . $option['ext'] . '|', $request->ext() ? '|' . $request->ext() . '|' : '')) // 伪静态后缀检测
  1063. || (isset($option['deny_ext']) && false !== stripos('|' . $option['deny_ext'] . '|', $request->ext() ? '|' . $request->ext() . '|' : ''))
  1064. || (isset($option['domain']) && !in_array($option['domain'], [$_SERVER['HTTP_HOST'], self::$subDomain])) // 域名检测
  1065. || (isset($option['https']) && $option['https'] && !$request->isSsl()) // https检测
  1066. || (isset($option['https']) && !$option['https'] && $request->isSsl()) // https检测
  1067. || (!empty($option['before_behavior']) && false === Hook::exec($option['before_behavior'])) // 行为检测
  1068. || (!empty($option['callback']) && is_callable($option['callback']) && false === call_user_func($option['callback'])) // 自定义检测
  1069. ) {
  1070. return false;
  1071. }
  1072. return true;
  1073. }
  1074. /**
  1075. * 检测路由规则
  1076. * @access private
  1077. * @param string $rule 路由规则
  1078. * @param string $route 路由地址
  1079. * @param string $url URL地址
  1080. * @param array $pattern 变量规则
  1081. * @param array $option 路由参数
  1082. * @param string $depr URL分隔符(全局)
  1083. * @return array|false
  1084. */
  1085. private static function checkRule($rule, $route, $url, $pattern, $option, $depr)
  1086. {
  1087. // 检查完整规则定义
  1088. if (isset($pattern['__url__']) && !preg_match('/^' . $pattern['__url__'] . '/', str_replace('|', $depr, $url))) {
  1089. return false;
  1090. }
  1091. // 检查路由的参数分隔符
  1092. if (isset($option['param_depr'])) {
  1093. $url = str_replace(['|', $option['param_depr']], [$depr, '|'], $url);
  1094. }
  1095. $len1 = substr_count($url, '|');
  1096. $len2 = substr_count($rule, '/');
  1097. // 多余参数是否合并
  1098. $merge = !empty($option['merge_extra_vars']) ? true : false;
  1099. if ($merge && $len1 > $len2) {
  1100. $url = str_replace('|', $depr, $url);
  1101. $url = implode('|', explode($depr, $url, $len2 + 1));
  1102. }
  1103. if ($len1 >= $len2 || strpos($rule, '[')) {
  1104. if (!empty($option['complete_match'])) {
  1105. // 完整匹配
  1106. if (!$merge && $len1 != $len2 && (false === strpos($rule, '[') || $len1 > $len2 || $len1 < $len2 - substr_count($rule, '['))) {
  1107. return false;
  1108. }
  1109. }
  1110. $pattern = array_merge(self::$rules['pattern'], $pattern);
  1111. if (false !== $match = self::match($url, $rule, $pattern, $merge)) {
  1112. // 匹配到路由规则
  1113. return self::parseRule($rule, $route, $url, $option, $match, $merge);
  1114. }
  1115. }
  1116. return false;
  1117. }
  1118. /**
  1119. * 解析模块的URL地址 [模块/控制器/操作?]参数1=值1&参数2=值2...
  1120. * @access public
  1121. * @param string $url URL地址
  1122. * @param string $depr URL分隔符
  1123. * @param bool $autoSearch 是否自动深度搜索控制器
  1124. * @return array
  1125. */
  1126. public static function parseUrl($url, $depr = '/', $autoSearch = false)
  1127. {
  1128. if (isset(self::$bind['module'])) {
  1129. $bind = str_replace('/', $depr, self::$bind['module']);
  1130. // 如果有模块/控制器绑定
  1131. $url = $bind . ('.' != substr($bind, -1) ? $depr : '') . ltrim($url, $depr);
  1132. }
  1133. $url = str_replace($depr, '|', $url);
  1134. list($path, $var) = self::parseUrlPath($url);
  1135. $route = [null, null, null];
  1136. if (isset($path)) {
  1137. // 解析模块
  1138. $module = Config::get('app_multi_module') ? array_shift($path) : null;
  1139. if ($autoSearch) {
  1140. // 自动搜索控制器
  1141. $dir = APP_PATH . ($module ? $module . DS : '') . Config::get('url_controller_layer');
  1142. $suffix = App::$suffix || Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : '';
  1143. $item = [];
  1144. $find = false;
  1145. foreach ($path as $val) {
  1146. $item[] = $val;
  1147. $file = $dir . DS . str_replace('.', DS, $val) . $suffix . EXT;
  1148. $file = pathinfo($file, PATHINFO_DIRNAME) . DS . Loader::parseName(pathinfo($file, PATHINFO_FILENAME), 1) . EXT;
  1149. if (is_file($file)) {
  1150. $find = true;
  1151. break;
  1152. } else {
  1153. $dir .= DS . Loader::parseName($val);
  1154. }
  1155. }
  1156. if ($find) {
  1157. $controller = implode('.', $item);
  1158. $path = array_slice($path, count($item));
  1159. } else {
  1160. $controller = array_shift($path);
  1161. }
  1162. } else {
  1163. // 解析控制器
  1164. $controller = !empty($path) ? array_shift($path) : null;
  1165. }
  1166. // 解析操作
  1167. $action = !empty($path) ? array_shift($path) : null;
  1168. // 解析额外参数
  1169. self::parseUrlParams(empty($path) ? '' : implode('|', $path));
  1170. // 封装路由
  1171. $route = [$module, $controller, $action];
  1172. // 检查地址是否被定义过路由
  1173. $name = strtolower($module . '/' . Loader::parseName($controller, 1) . '/' . $action);
  1174. $name2 = '';
  1175. if (empty($module) || isset($bind) && $module == $bind) {
  1176. $name2 = strtolower(Loader::parseName($controller, 1) . '/' . $action);
  1177. }
  1178. if (isset(self::$rules['name'][$name]) || isset(self::$rules['name'][$name2])) {
  1179. throw new HttpException(404, 'invalid request:' . str_replace('|', $depr, $url));
  1180. }
  1181. }
  1182. return ['type' => 'module', 'module' => $route];
  1183. }
  1184. /**
  1185. * 解析URL的pathinfo参数和变量
  1186. * @access private
  1187. * @param string $url URL地址
  1188. * @return array
  1189. */
  1190. private static function parseUrlPath($url)
  1191. {
  1192. // 分隔符替换 确保路由定义使用统一的分隔符
  1193. $url = str_replace('|', '/', $url);
  1194. $url = trim($url, '/');
  1195. $var = [];
  1196. if (false !== strpos($url, '?')) {
  1197. // [模块/控制器/操作?]参数1=值1&参数2=值2...
  1198. $info = parse_url($url);
  1199. $path = explode('/', $info['path']);
  1200. parse_str($info['query'], $var);
  1201. } elseif (strpos($url, '/')) {
  1202. // [模块/控制器/操作]
  1203. $path = explode('/', $url);
  1204. } elseif (false !== strpos($url, '=')) {
  1205. // 参数1=值1&参数2=值2...
  1206. parse_str($url, $var);
  1207. } else {
  1208. $path = [$url];
  1209. }
  1210. return [$path, $var];
  1211. }
  1212. /**
  1213. * 检测URL和规则路由是否匹配
  1214. * @access private
  1215. * @param string $url URL地址
  1216. * @param string $rule 路由规则
  1217. * @param array $pattern 变量规则
  1218. * @return array|false
  1219. */
  1220. private static function match($url, $rule, $pattern)
  1221. {
  1222. $m2 = explode('/', $rule);
  1223. $m1 = explode('|', $url);
  1224. $var = [];
  1225. foreach ($m2 as $key => $val) {
  1226. // val中定义了多个变量 <id><name>
  1227. if (false !== strpos($val, '<') && preg_match_all('/<(\w+(\??))>/', $val, $matches)) {
  1228. $value = [];
  1229. $replace = [];
  1230. foreach ($matches[1] as $name) {
  1231. if (strpos($name, '?')) {
  1232. $name = substr($name, 0, -1);
  1233. $replace[] = '(' . (isset($pattern[$name]) ? $pattern[$name] : '\w+') . ')?';
  1234. } else {
  1235. $replace[] = '(' . (isset($pattern[$name]) ? $pattern[$name] : '\w+') . ')';
  1236. }
  1237. $value[] = $name;
  1238. }
  1239. $val = str_replace($matches[0], $replace, $val);
  1240. if (preg_match('/^' . $val . '$/', isset($m1[$key]) ? $m1[$key] : '', $match)) {
  1241. array_shift($match);
  1242. foreach ($value as $k => $name) {
  1243. if (isset($match[$k])) {
  1244. $var[$name] = $match[$k];
  1245. }
  1246. }
  1247. continue;
  1248. } else {
  1249. return false;
  1250. }
  1251. }
  1252. if (0 === strpos($val, '[:')) {
  1253. // 可选参数
  1254. $val = substr($val, 1, -1);
  1255. $optional = true;
  1256. } else {
  1257. $optional = false;
  1258. }
  1259. if (0 === strpos($val, ':')) {
  1260. // URL变量
  1261. $name = substr($val, 1);
  1262. if (!$optional && !isset($m1[$key])) {
  1263. return false;
  1264. }
  1265. if (isset($m1[$key]) && isset($pattern[$name])) {
  1266. // 检查变量规则
  1267. if ($pattern[$name] instanceof \Closure) {
  1268. $result = call_user_func_array($pattern[$name], [$m1[$key]]);
  1269. if (false === $result) {
  1270. return false;
  1271. }
  1272. } elseif (!preg_match('/^' . $pattern[$name] . '$/', $m1[$key])) {
  1273. return false;
  1274. }
  1275. }
  1276. $var[$name] = isset($m1[$key]) ? $m1[$key] : '';
  1277. } elseif (!isset($m1[$key]) || 0 !== strcasecmp($val, $m1[$key])) {
  1278. return false;
  1279. }
  1280. }
  1281. // 成功匹配后返回URL中的动态变量数组
  1282. return $var;
  1283. }
  1284. /**
  1285. * 解析规则路由
  1286. * @access private
  1287. * @param string $rule 路由规则
  1288. * @param string $route 路由地址
  1289. * @param string $pathinfo URL地址
  1290. * @param array $option 路由参数
  1291. * @param array $matches 匹配的变量
  1292. * @return array
  1293. */
  1294. private static function parseRule($rule, $route, $pathinfo, $option = [], $matches = [])
  1295. {
  1296. $request = Request::instance();
  1297. // 解析路由规则
  1298. if ($rule) {
  1299. $rule = explode('/', $rule);
  1300. // 获取URL地址中的参数
  1301. $paths = explode('|', $pathinfo);
  1302. foreach ($rule as $item) {
  1303. $fun = '';
  1304. if (0 === strpos($item, '[:')) {
  1305. $item = substr($item, 1, -1);
  1306. }
  1307. if (0 === strpos($item, ':')) {
  1308. $var = substr($item, 1);
  1309. $matches[$var] = array_shift($paths);
  1310. } else {
  1311. // 过滤URL中的静态变量
  1312. array_shift($paths);
  1313. }
  1314. }
  1315. } else {
  1316. $paths = explode('|', $pathinfo);
  1317. }
  1318. // 获取路由地址规则
  1319. if (is_string($route) && isset($option['prefix'])) {
  1320. // 路由地址前缀
  1321. $route = $option['prefix'] . $route;
  1322. }
  1323. // 替换路由地址中的变量
  1324. if (is_string($route) && !empty($matches)) {
  1325. foreach ($matches as $key => $val) {
  1326. if (false !== strpos($route, ':' . $key)) {
  1327. $route = str_replace(':' . $key, $val, $route);
  1328. }
  1329. }
  1330. }
  1331. // 绑定模型数据
  1332. if (isset($option['bind_model'])) {
  1333. $bind = [];
  1334. foreach ($option['bind_model'] as $key => $val) {
  1335. if ($val instanceof \Closure) {
  1336. $result = call_user_func_array($val, [$matches]);
  1337. } else {
  1338. if (is_array($val)) {
  1339. $fields = explode('&', $val[1]);
  1340. $model = $val[0];
  1341. $exception = isset($val[2]) ? $val[2] : true;
  1342. } else {
  1343. $fields = ['id'];
  1344. $model = $val;
  1345. $exception = true;
  1346. }
  1347. $where = [];
  1348. $match = true;
  1349. foreach ($fields as $field) {
  1350. if (!isset($matches[$field])) {
  1351. $match = false;
  1352. break;
  1353. } else {
  1354. $where[$field] = $matches[$field];
  1355. }
  1356. }
  1357. if ($match) {
  1358. $query = strpos($model, '\\') ? $model::where($where) : Loader::model($model)->where($where);
  1359. $result = $query->failException($exception)->find();
  1360. }
  1361. }
  1362. if (!empty($result)) {
  1363. $bind[$key] = $result;
  1364. }
  1365. }
  1366. $request->bind($bind);
  1367. }
  1368. // 解析额外参数
  1369. self::parseUrlParams(empty($paths) ? '' : implode('|', $paths), $matches);
  1370. // 记录匹配的路由信息
  1371. $request->routeInfo(['rule' => $rule, 'route' => $route, 'option' => $option, 'var' => $matches]);
  1372. // 检测路由after行为
  1373. if (!empty($option['after_behavior'])) {
  1374. if ($option['after_behavior'] instanceof \Closure) {
  1375. $result = call_user_func_array($option['after_behavior'], []);
  1376. } else {
  1377. foreach ((array) $option['after_behavior'] as $behavior) {
  1378. $result = Hook::exec($behavior, '');
  1379. if (!is_null($result)) {
  1380. break;
  1381. }
  1382. }
  1383. }
  1384. // 路由规则重定向
  1385. if ($result instanceof Response) {
  1386. return ['type' => 'response', 'response' => $result];
  1387. } elseif (is_array($result)) {
  1388. return $result;
  1389. }
  1390. }
  1391. if ($route instanceof \Closure) {
  1392. // 执行闭包
  1393. $result = ['type' => 'function', 'function' => $route];
  1394. } elseif (0 === strpos($route, '/') || strpos($route, '://')) {
  1395. // 路由到重定向地址
  1396. $result = ['type' => 'redirect', 'url' => $route, 'status' => isset($option['status']) ? $option['status'] : 301];
  1397. } elseif (false !== strpos($route, '\\')) {
  1398. // 路由到方法
  1399. list($path, $var) = self::parseUrlPath($route);
  1400. $route = str_replace('/', '@', implode('/', $path));
  1401. $method = strpos($route, '@') ? explode('@', $route) : $route;
  1402. $result = ['type' => 'method', 'method' => $method, 'var' => $var];
  1403. } elseif (0 === strpos($route, '@')) {
  1404. // 路由到控制器
  1405. $route = substr($route, 1);
  1406. list($route, $var) = self::parseUrlPath($route);
  1407. $result = ['type' => 'controller', 'controller' => implode('/', $route), 'var' => $var];
  1408. $request->action(array_pop($route));
  1409. $request->controller($route ? array_pop($route) : Config::get('default_controller'));
  1410. $request->module($route ? array_pop($route) : Config::get('default_module'));
  1411. App::$modulePath = APP_PATH . (Config::get('app_multi_module') ? $request->module() . DS : '');
  1412. } else {
  1413. // 路由到模块/控制器/操作
  1414. $result = self::parseModule($route);
  1415. }
  1416. // 开启请求缓存
  1417. if ($request->isGet() && isset($option['cache'])) {
  1418. $cache = $option['cache'];
  1419. if (is_array($cache)) {
  1420. list($key, $expire) = $cache;
  1421. } else {
  1422. $key = str_replace('|', '/', $pathinfo);
  1423. $expire = $cache;
  1424. }
  1425. $request->cache($key, $expire);
  1426. }
  1427. return $result;
  1428. }
  1429. /**
  1430. * 解析URL地址为 模块/控制器/操作
  1431. * @access private
  1432. * @param string $url URL地址
  1433. * @return array
  1434. */
  1435. private static function parseModule($url)
  1436. {
  1437. list($path, $var) = self::parseUrlPath($url);
  1438. $action = array_pop($path);
  1439. $controller = !empty($path) ? array_pop($path) : null;
  1440. $module = Config::get('app_multi_module') && !empty($path) ? array_pop($path) : null;
  1441. $method = Request::instance()->method();
  1442. if (Config::get('use_action_prefix') && !empty(self::$methodPrefix[$method])) {
  1443. // 操作方法前缀支持
  1444. $action = 0 !== strpos($action, self::$methodPrefix[$method]) ? self::$methodPrefix[$method] . $action : $action;
  1445. }
  1446. // 设置当前请求的路由变量
  1447. Request::instance()->route($var);
  1448. // 路由到模块/控制器/操作
  1449. return ['type' => 'module', 'module' => [$module, $controller, $action], 'convert' => false];
  1450. }
  1451. /**
  1452. * 解析URL地址中的参数Request对象
  1453. * @access private
  1454. * @param string $rule 路由规则
  1455. * @param array $var 变量
  1456. * @return void
  1457. */
  1458. private static function parseUrlParams($url, &$var = [])
  1459. {
  1460. if ($url) {
  1461. if (Config::get('url_param_type')) {
  1462. $var += explode('|', $url);
  1463. } else {
  1464. preg_replace_callback('/(\w+)\|([^\|]+)/', function ($match) use (&$var) {
  1465. $var[$match[1]] = strip_tags($match[2]);
  1466. }, $url);
  1467. }
  1468. }
  1469. // 设置当前请求的参数
  1470. Request::instance()->route($var);
  1471. }
  1472. // 分析路由规则中的变量
  1473. private static function parseVar($rule)
  1474. {
  1475. // 提取路由规则中的变量
  1476. $var = [];
  1477. foreach (explode('/', $rule) as $val) {
  1478. $optional = false;
  1479. if (false !== strpos($val, '<') && preg_match_all('/<(\w+(\??))>/', $val, $matches)) {
  1480. foreach ($matches[1] as $name) {
  1481. if (strpos($name, '?')) {
  1482. $name = substr($name, 0, -1);
  1483. $optional = true;
  1484. } else {
  1485. $optional = false;
  1486. }
  1487. $var[$name] = $optional ? 2 : 1;
  1488. }
  1489. }
  1490. if (0 === strpos($val, '[:')) {
  1491. // 可选参数
  1492. $optional = true;
  1493. $val = substr($val, 1, -1);
  1494. }
  1495. if (0 === strpos($val, ':')) {
  1496. // URL变量
  1497. $name = substr($val, 1);
  1498. $var[$name] = $optional ? 2 : 1;
  1499. }
  1500. }
  1501. return $var;
  1502. }
  1503. }