Route.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006~2018 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\RouteNotFoundException;
  13. use think\route\dispatch\Url as UrlDispatch;
  14. use think\route\Domain;
  15. use think\route\Resource;
  16. use think\route\RuleGroup;
  17. use think\route\RuleItem;
  18. class Route
  19. {
  20. /**
  21. * REST定义
  22. * @var array
  23. */
  24. protected $rest = [
  25. 'index' => ['get', '', 'index'],
  26. 'create' => ['get', '/create', 'create'],
  27. 'edit' => ['get', '/:id/edit', 'edit'],
  28. 'read' => ['get', '/:id', 'read'],
  29. 'save' => ['post', '', 'save'],
  30. 'update' => ['put', '/:id', 'update'],
  31. 'delete' => ['delete', '/:id', 'delete'],
  32. ];
  33. /**
  34. * 请求方法前缀定义
  35. * @var array
  36. */
  37. protected $methodPrefix = [
  38. 'get' => 'get',
  39. 'post' => 'post',
  40. 'put' => 'put',
  41. 'delete' => 'delete',
  42. 'patch' => 'patch',
  43. ];
  44. /**
  45. * 配置对象
  46. * @var Config
  47. */
  48. protected $config;
  49. /**
  50. * 请求对象
  51. * @var Request
  52. */
  53. protected $request;
  54. /**
  55. * 当前HOST
  56. * @var string
  57. */
  58. protected $host;
  59. /**
  60. * 当前域名
  61. * @var string
  62. */
  63. protected $domain;
  64. /**
  65. * 当前分组
  66. * @var string
  67. */
  68. protected $group;
  69. /**
  70. * 路由标识
  71. * @var array
  72. */
  73. protected $name = [];
  74. /**
  75. * 路由绑定
  76. * @var array
  77. */
  78. protected $bind = [];
  79. /**
  80. * 域名对象
  81. * @var array
  82. */
  83. protected $domains = [];
  84. /**
  85. * 跨域路由规则
  86. * @var RuleGroup
  87. */
  88. protected $cross;
  89. /**
  90. * 当前路由标识
  91. * @var string
  92. */
  93. protected $ruleName;
  94. /**
  95. * 路由别名
  96. * @var array
  97. */
  98. protected $alias = [];
  99. public function __construct(Request $request, Config $config)
  100. {
  101. $this->config = $config;
  102. $this->request = $request;
  103. $this->host = $this->request->host();
  104. $this->setDefaultDomain();
  105. }
  106. /**
  107. * 初始化默认域名
  108. * @access protected
  109. * @return void
  110. */
  111. protected function setDefaultDomain()
  112. {
  113. // 默认域名
  114. $this->domain = $this->host;
  115. // 注册默认域名
  116. $domain = new Domain($this, $this->host);
  117. $this->domains[$this->host] = $domain;
  118. // 默认分组
  119. $this->group = $this->createTopGroup($domain);
  120. }
  121. /**
  122. * 创建一个域名下的顶级路由分组
  123. * @access protected
  124. * @param Domain $domain 域名
  125. * @return RuleGroup
  126. */
  127. protected function createTopGroup(Domain $domain)
  128. {
  129. $group = new RuleGroup($this);
  130. // 注册分组到当前域名
  131. $domain->addRule($group);
  132. return $group;
  133. }
  134. /**
  135. * 设置当前域名
  136. * @access public
  137. * @param RuleGroup $group 域名
  138. * @return void
  139. */
  140. public function setGroup(RuleGroup $group)
  141. {
  142. $this->group = $group;
  143. }
  144. /**
  145. * 获取当前分组
  146. * @access public
  147. * @return RuleGroup
  148. */
  149. public function getGroup()
  150. {
  151. return $this->group;
  152. }
  153. /**
  154. * 注册变量规则
  155. * @access public
  156. * @param string|array $name 变量名
  157. * @param string $rule 变量规则
  158. * @return $this
  159. */
  160. public function pattern($name, $rule = '')
  161. {
  162. $this->group->pattern($name, $rule);
  163. return $this;
  164. }
  165. /**
  166. * 注册路由参数
  167. * @access public
  168. * @param string|array $name 参数名
  169. * @param mixed $value 值
  170. * @return $this
  171. */
  172. public function option($name, $value = '')
  173. {
  174. $this->group->option($name, $value);
  175. return $this;
  176. }
  177. /**
  178. * 获取当前根域名
  179. * @access protected
  180. * @return string
  181. */
  182. protected function getRootDomain()
  183. {
  184. $root = $this->config->get('app.url_domain_root');
  185. if (!$root) {
  186. $item = explode('.', $this->host);
  187. $count = count($item);
  188. $root = $count > 1 ? $item[$count - 2] . '.' . $item[$count - 1] : $item[0];
  189. }
  190. return $root;
  191. }
  192. /**
  193. * 注册域名路由
  194. * @access public
  195. * @param string|array $name 子域名
  196. * @param mixed $rule 路由规则
  197. * @param array $option 路由参数
  198. * @param array $pattern 变量规则
  199. * @return Domain
  200. */
  201. public function domain($name, $rule = '', $option = [], $pattern = [])
  202. {
  203. // 支持多个域名使用相同路由规则
  204. $domainName = is_array($name) ? array_shift($name) : $name;
  205. if ('*' != $domainName && !strpos($domainName, '.')) {
  206. $domainName .= '.' . $this->getRootDomain();
  207. }
  208. $route = $this->config->get('url_lazy_route') ? $rule : null;
  209. $domain = new Domain($this, $domainName, $route, $option, $pattern);
  210. if (is_null($route)) {
  211. // 获取原始分组
  212. $originGroup = $this->group;
  213. // 设置当前域名
  214. $this->domain = $domainName;
  215. $this->group = $this->createTopGroup($domain);
  216. // 解析域名路由规则
  217. $this->parseGroupRule($domain, $rule);
  218. // 还原默认域名
  219. $this->domain = $this->host;
  220. // 还原默认分组
  221. $this->group = $originGroup;
  222. }
  223. $this->domains[$domainName] = $domain;
  224. if (is_array($name) && !empty($name)) {
  225. $root = $this->getRootDomain();
  226. foreach ($name as $item) {
  227. if (!strpos($item, '.')) {
  228. $item .= '.' . $root;
  229. }
  230. $this->domains[$item] = $domainName;
  231. }
  232. }
  233. // 返回域名对象
  234. return $domain;
  235. }
  236. /**
  237. * 解析分组和域名的路由规则及绑定
  238. * @access public
  239. * @param RuleGroup $group 分组路由对象
  240. * @param mixed $rule 路由规则
  241. * @return void
  242. */
  243. public function parseGroupRule($group, $rule)
  244. {
  245. if ($rule instanceof \Closure) {
  246. Container::getInstance()->invokeFunction($rule);
  247. } elseif ($rule instanceof Response) {
  248. $group->setRule($rule);
  249. } elseif (is_array($rule)) {
  250. $this->rules($rule);
  251. } elseif ($rule) {
  252. if (false !== strpos($rule, '?')) {
  253. list($rule, $query) = explode('?', $rule);
  254. parse_str($query, $vars);
  255. $group->append($vars);
  256. }
  257. $this->bind($rule);
  258. }
  259. }
  260. /**
  261. * 获取域名
  262. * @access public
  263. * @return array
  264. */
  265. public function getDomains()
  266. {
  267. return $this->domains;
  268. }
  269. /**
  270. * 设置路由绑定
  271. * @access public
  272. * @param string $bind 绑定信息
  273. * @return $this
  274. */
  275. public function bind($bind)
  276. {
  277. $this->bind[$this->domain] = $bind;
  278. return $this;
  279. }
  280. /**
  281. * 读取路由绑定
  282. * @access public
  283. * @param string $domain 域名
  284. * @return string|null
  285. */
  286. public function getBind($domain = null)
  287. {
  288. if (is_null($domain)) {
  289. $domain = $this->domain;
  290. }
  291. $subDomain = $this->request->subDomain();
  292. if (strpos($subDomain, '.')) {
  293. $name = '*' . strstr($subDomain, '.');
  294. }
  295. if (isset($this->bind[$domain])) {
  296. $result = $this->bind[$domain];
  297. } elseif (isset($name) && isset($this->bind[$name])) {
  298. $result = $this->bind[$name];
  299. } elseif (isset($this->bind['*'])) {
  300. $result = $this->bind['*'];
  301. } else {
  302. $result = null;
  303. }
  304. return $result;
  305. }
  306. /**
  307. * 设置当前路由标识
  308. * @access public
  309. * @param string $name 路由命名标识
  310. * @return $this
  311. */
  312. public function name($name)
  313. {
  314. $this->ruleName = $name;
  315. return $this;
  316. }
  317. /**
  318. * 读取路由标识
  319. * @access public
  320. * @param string $name 路由标识
  321. * @return mixed
  322. */
  323. public function getName($name = null)
  324. {
  325. if (is_null($name)) {
  326. return $this->name;
  327. }
  328. $name = strtolower($name);
  329. return isset($this->name[$name]) ? $this->name[$name] : null;
  330. }
  331. /**
  332. * 批量导入路由标识
  333. * @access public
  334. * @param array $name 路由标识
  335. * @return $this
  336. */
  337. public function setName($name)
  338. {
  339. $this->name = $name;
  340. return $this;
  341. }
  342. /**
  343. * 导入配置文件的路由规则
  344. * @access public
  345. * @param array $rules 路由规则
  346. * @param string $type 请求类型
  347. * @return void
  348. */
  349. public function import(array $rules, $type = '*')
  350. {
  351. // 检查域名部署
  352. if (isset($rules['__domain__'])) {
  353. foreach ($rules['__domain__'] as $key => $rule) {
  354. $this->domain($key, $rule);
  355. }
  356. unset($rules['__domain__']);
  357. }
  358. // 检查变量规则
  359. if (isset($rules['__pattern__'])) {
  360. $this->pattern($rules['__pattern__']);
  361. unset($rules['__pattern__']);
  362. }
  363. // 检查路由别名
  364. if (isset($rules['__alias__'])) {
  365. $this->alias($rules['__alias__']);
  366. unset($rules['__alias__']);
  367. }
  368. // 检查资源路由
  369. if (isset($rules['__rest__'])) {
  370. foreach ($rules['__rest__'] as $key => $rule) {
  371. $this->resource($key, $rule);
  372. }
  373. unset($rules['__rest__']);
  374. }
  375. // 检查路由规则(包含分组)
  376. foreach ($rules as $key => $val) {
  377. if (is_numeric($key)) {
  378. $key = array_shift($val);
  379. }
  380. if (empty($val)) {
  381. continue;
  382. }
  383. if (is_string($key) && 0 === strpos($key, '[')) {
  384. $key = substr($key, 1, -1);
  385. $this->group($key, $val);
  386. } elseif (is_array($val)) {
  387. $this->rule($key, $val[0], $type, $val[1], isset($val[2]) ? $val[2] : []);
  388. } else {
  389. $this->rule($key, $val, $type);
  390. }
  391. }
  392. }
  393. /**
  394. * 注册路由规则
  395. * @access public
  396. * @param string $rule 路由规则
  397. * @param mixed $route 路由地址
  398. * @param string $method 请求类型
  399. * @param array $option 路由参数
  400. * @param array $pattern 变量规则
  401. * @return RuleItem
  402. */
  403. public function rule($rule, $route, $method = '*', $option = [], $pattern = [])
  404. {
  405. // 读取路由标识
  406. if (is_array($rule)) {
  407. $name = $rule[0];
  408. $rule = $rule[1];
  409. } elseif ($this->ruleName) {
  410. $name = $this->ruleName;
  411. $this->ruleName = null;
  412. } elseif (is_string($route)) {
  413. $name = $route;
  414. }
  415. $method = strtolower($method);
  416. // 创建路由规则实例
  417. $ruleItem = new RuleItem($this, $this->group, $rule, $route, $method, $option, $pattern);
  418. if (isset($name)) {
  419. // 上级完整分组名
  420. $group = $this->group->getFullName();
  421. if ($group) {
  422. $rule = $group . '/' . $rule;
  423. }
  424. // 设置路由标识 用于URL快速生成
  425. $this->setRuleName($rule, $name, $option);
  426. }
  427. // 添加到当前分组
  428. $this->group->addRule($ruleItem, $method);
  429. if (!empty($option['cross_domain'])) {
  430. $this->setCrossDomainRule($ruleItem, $method);
  431. }
  432. return $ruleItem;
  433. }
  434. /**
  435. * 设置路由标识 用于URL反解生成
  436. * @access public
  437. * @param string $rule 路由规则
  438. * @param string $name 路由标识
  439. * @param array $option 路由参数
  440. * @return void
  441. */
  442. public function setRuleName($rule, $name, $option = [])
  443. {
  444. $vars = $this->parseVar($rule);
  445. if (isset($option['ext'])) {
  446. $suffix = $option['ext'];
  447. } elseif ($this->group->getOption('ext')) {
  448. $suffix = $this->group->getOption('ext');
  449. } else {
  450. $suffix = null;
  451. }
  452. $this->name[strtolower($name)][] = [$rule, $vars, $this->domain, $suffix];
  453. }
  454. /**
  455. * 设置跨域有效路由规则
  456. * @access public
  457. * @param Rule $rule 路由规则
  458. * @param string $method 请求类型
  459. * @return $this
  460. */
  461. public function setCrossDomainRule($rule, $method = '*')
  462. {
  463. if (!isset($this->cross)) {
  464. $this->cross = new RuleGroup($this);
  465. }
  466. $this->cross->addRule($rule, $method);
  467. return $this;
  468. }
  469. /**
  470. * 批量注册路由规则
  471. * @access public
  472. * @param array $rules 路由规则
  473. * @param string $method 请求类型
  474. * @param array $option 路由参数
  475. * @param array $pattern 变量规则
  476. * @return void
  477. */
  478. public function rules($rules, $method = '*', $option = [], $pattern = [])
  479. {
  480. foreach ($rules as $key => $val) {
  481. if (is_numeric($key)) {
  482. $key = array_shift($val);
  483. }
  484. if (is_array($val)) {
  485. $route = array_shift($val);
  486. $option = $val ? array_shift($val) : [];
  487. $pattern = $val ? array_shift($val) : [];
  488. } else {
  489. $route = $val;
  490. }
  491. $this->rule($key, $route, $method, $option, $pattern);
  492. }
  493. }
  494. /**
  495. * 注册路由分组
  496. * @access public
  497. * @param string|array $name 分组名称或者参数
  498. * @param array|\Closure $route 分组路由
  499. * @param array $option 路由参数
  500. * @param array $pattern 变量规则
  501. * @return RuleGroup
  502. */
  503. public function group($name, $route, $option = [], $pattern = [])
  504. {
  505. if (is_array($name)) {
  506. $option = $name;
  507. $name = isset($option['name']) ? $option['name'] : '';
  508. }
  509. // 创建分组实例
  510. $rule = $this->config->get('url_lazy_route') ? $route : null;
  511. $group = new RuleGroup($this, $this->group, $name, $rule, $option, $pattern);
  512. if (is_null($rule)) {
  513. // 解析分组路由
  514. $parent = $this->getGroup();
  515. $this->group = $group;
  516. // 解析分组路由规则
  517. $this->parseGroupRule($group, $route);
  518. $this->group = $parent;
  519. }
  520. // 注册子分组
  521. $this->group->addRule($group);
  522. if (!empty($option['cross_domain'])) {
  523. $this->setCrossDomainRule($group);
  524. }
  525. return $group;
  526. }
  527. /**
  528. * 注册路由
  529. * @access public
  530. * @param string $rule 路由规则
  531. * @param string $route 路由地址
  532. * @param array $option 路由参数
  533. * @param array $pattern 变量规则
  534. * @return RuleItem
  535. */
  536. public function any($rule, $route = '', $option = [], $pattern = [])
  537. {
  538. return $this->rule($rule, $route, '*', $option, $pattern);
  539. }
  540. /**
  541. * 注册GET路由
  542. * @access public
  543. * @param string $rule 路由规则
  544. * @param string $route 路由地址
  545. * @param array $option 路由参数
  546. * @param array $pattern 变量规则
  547. * @return RuleItem
  548. */
  549. public function get($rule, $route = '', $option = [], $pattern = [])
  550. {
  551. return $this->rule($rule, $route, 'GET', $option, $pattern);
  552. }
  553. /**
  554. * 注册POST路由
  555. * @access public
  556. * @param string $rule 路由规则
  557. * @param string $route 路由地址
  558. * @param array $option 路由参数
  559. * @param array $pattern 变量规则
  560. * @return RuleItem
  561. */
  562. public function post($rule, $route = '', $option = [], $pattern = [])
  563. {
  564. return $this->rule($rule, $route, 'POST', $option, $pattern);
  565. }
  566. /**
  567. * 注册PUT路由
  568. * @access public
  569. * @param string $rule 路由规则
  570. * @param string $route 路由地址
  571. * @param array $option 路由参数
  572. * @param array $pattern 变量规则
  573. * @return RuleItem
  574. */
  575. public function put($rule, $route = '', $option = [], $pattern = [])
  576. {
  577. return $this->rule($rule, $route, 'PUT', $option, $pattern);
  578. }
  579. /**
  580. * 注册DELETE路由
  581. * @access public
  582. * @param string $rule 路由规则
  583. * @param string $route 路由地址
  584. * @param array $option 路由参数
  585. * @param array $pattern 变量规则
  586. * @return RuleItem
  587. */
  588. public function delete($rule, $route = '', $option = [], $pattern = [])
  589. {
  590. return $this->rule($rule, $route, 'DELETE', $option, $pattern);
  591. }
  592. /**
  593. * 注册PATCH路由
  594. * @access public
  595. * @param string $rule 路由规则
  596. * @param string $route 路由地址
  597. * @param array $option 路由参数
  598. * @param array $pattern 变量规则
  599. * @return RuleItem
  600. */
  601. public function patch($rule, $route = '', $option = [], $pattern = [])
  602. {
  603. return $this->rule($rule, $route, 'PATCH', $option, $pattern);
  604. }
  605. /**
  606. * 注册资源路由
  607. * @access public
  608. * @param string $rule 路由规则
  609. * @param string $route 路由地址
  610. * @param array $option 路由参数
  611. * @param array $pattern 变量规则
  612. * @return Resource
  613. */
  614. public function resource($rule, $route = '', $option = [], $pattern = [])
  615. {
  616. $resource = new Resource($this, $this->group, $rule, $route, $option, $pattern, $this->rest);
  617. // 添加到当前分组
  618. $this->group->addRule($resource);
  619. return $resource;
  620. }
  621. /**
  622. * 注册控制器路由 操作方法对应不同的请求后缀
  623. * @access public
  624. * @param string $rule 路由规则
  625. * @param string $route 路由地址
  626. * @param array $option 路由参数
  627. * @param array $pattern 变量规则
  628. * @return $this
  629. */
  630. public function controller($rule, $route = '', $option = [], $pattern = [])
  631. {
  632. foreach ($this->methodPrefix as $type => $val) {
  633. $this->$type($rule . '/:action', $route . '/' . $val . ':action', $option, $pattern);
  634. }
  635. return $this;
  636. }
  637. /**
  638. * 注册视图路由
  639. * @access public
  640. * @param string|array $rule 路由规则
  641. * @param string $template 路由模板地址
  642. * @param array $vars 模板变量
  643. * @param array $option 路由参数
  644. * @param array $pattern 变量规则
  645. * @return RuleItem
  646. */
  647. public function view($rule, $template = '', $vars = [], $option = [], $pattern = [])
  648. {
  649. return $this->rule($rule, $template, 'GET', $option, $pattern)->view($vars);
  650. }
  651. /**
  652. * 注册重定向路由
  653. * @access public
  654. * @param string|array $rule 路由规则
  655. * @param string $template 路由模板地址
  656. * @param array $status 状态码
  657. * @param array $option 路由参数
  658. * @param array $pattern 变量规则
  659. * @return RuleItem
  660. */
  661. public function redirect($rule, $route = '', $status = 301, $option = [], $pattern = [])
  662. {
  663. return $this->rule($rule, $route, '*', $option, $pattern)->redirect()->status($status);
  664. }
  665. /**
  666. * 注册别名路由
  667. * @access public
  668. * @param string|array $rule 路由别名
  669. * @param string $route 路由地址
  670. * @param array $option 路由参数
  671. * @return $this
  672. */
  673. public function alias($rule = null, $route = '', $option = [])
  674. {
  675. if (is_array($rule)) {
  676. $this->alias = array_merge($this->alias, $rule);
  677. } else {
  678. $this->alias[$rule] = $option ? [$route, $option] : $route;
  679. }
  680. return $this;
  681. }
  682. /**
  683. * 获取别名路由定义
  684. * @access public
  685. * @param string $name 路由别名
  686. * @return string|array|null
  687. */
  688. public function getAlias($name = null)
  689. {
  690. if (is_null($name)) {
  691. return $this->alias;
  692. }
  693. return isset($this->alias[$name]) ? $this->alias[$name] : null;
  694. }
  695. /**
  696. * 设置不同请求类型下面的方法前缀
  697. * @access public
  698. * @param string|array $method 请求类型
  699. * @param string $prefix 类型前缀
  700. * @return $this
  701. */
  702. public function setMethodPrefix($method, $prefix = '')
  703. {
  704. if (is_array($method)) {
  705. $this->methodPrefix = array_merge($this->methodPrefix, array_change_key_case($method));
  706. } else {
  707. $this->methodPrefix[strtolower($method)] = $prefix;
  708. }
  709. return $this;
  710. }
  711. /**
  712. * 获取请求类型的方法前缀
  713. * @access public
  714. * @param string $method 请求类型
  715. * @param string $prefix 类型前缀
  716. * @return string|null
  717. */
  718. public function getMethodPrefix($method)
  719. {
  720. $method = strtolower($method);
  721. return isset($this->methodPrefix[$method]) ? $this->methodPrefix[$method] : null;
  722. }
  723. /**
  724. * rest方法定义和修改
  725. * @access public
  726. * @param string $name 方法名称
  727. * @param array|bool $resource 资源
  728. * @return $this
  729. */
  730. public function rest($name, $resource = [])
  731. {
  732. if (is_array($name)) {
  733. $this->rest = $resource ? $name : array_merge($this->rest, $name);
  734. } else {
  735. $this->rest[$name] = $resource;
  736. }
  737. return $this;
  738. }
  739. /**
  740. * 获取rest方法定义的参数
  741. * @access public
  742. * @param string $name 方法名称
  743. * @return array|null
  744. */
  745. public function getRest($name = null)
  746. {
  747. if (is_null($name)) {
  748. return $this->rest;
  749. }
  750. return isset($this->rest[$name]) ? $this->rest[$name] : null;
  751. }
  752. /**
  753. * 注册未匹配路由规则后的处理
  754. * @access public
  755. * @param string $route 路由地址
  756. * @param string $method 请求类型
  757. * @param array $option 路由参数
  758. * @return RuleItem
  759. */
  760. public function miss($route, $method = '*', $option = [])
  761. {
  762. return $this->rule('', $route, $method, $option)->isMiss();
  763. }
  764. /**
  765. * 注册一个自动解析的URL路由
  766. * @access public
  767. * @param string $route 路由地址
  768. * @return RuleItem
  769. */
  770. public function auto($route)
  771. {
  772. return $this->rule('', $route)->isAuto();
  773. }
  774. /**
  775. * 检测URL路由
  776. * @access public
  777. * @param string $url URL地址
  778. * @param string $depr URL分隔符
  779. * @param bool $must 是否强制路由
  780. * @param bool $completeMatch 路由是否完全匹配
  781. * @return Dispatch
  782. * @throws RouteNotFoundException
  783. */
  784. public function check($url, $depr = '/', $must = false, $completeMatch = false)
  785. {
  786. // 自动检测域名路由
  787. $domain = $this->checkDomain();
  788. $url = str_replace($depr, '|', $url);
  789. $result = $domain->check($this->request, $url, $depr, $completeMatch);
  790. if (false === $result && !empty($this->cross)) {
  791. // 检测跨越路由
  792. $result = $this->cross->check($this->request, $url, $depr, $completeMatch);
  793. }
  794. if (false !== $result) {
  795. // 路由匹配
  796. return $result;
  797. } elseif ($must) {
  798. // 强制路由不匹配则抛出异常
  799. throw new RouteNotFoundException();
  800. } else {
  801. // 默认路由解析
  802. return new UrlDispatch($url, ['depr' => $depr, 'auto_search' => $this->config->get('app.controller_auto_search')]);
  803. }
  804. }
  805. /**
  806. * 检测域名的路由规则
  807. * @access protected
  808. * @param string $host 当前主机地址
  809. * @return Domain
  810. */
  811. protected function checkDomain()
  812. {
  813. // 获取当前子域名
  814. $subDomain = $this->request->subDomain();
  815. $item = false;
  816. if ($subDomain && count($this->domains) > 1) {
  817. $domain = explode('.', $subDomain);
  818. $domain2 = array_pop($domain);
  819. if ($domain) {
  820. // 存在三级域名
  821. $domain3 = array_pop($domain);
  822. }
  823. if ($subDomain && isset($this->domains[$subDomain])) {
  824. // 子域名配置
  825. $item = $this->domains[$subDomain];
  826. } elseif (isset($this->domains['*.' . $domain2]) && !empty($domain3)) {
  827. // 泛三级域名
  828. $item = $this->domains['*.' . $domain2];
  829. $panDomain = $domain3;
  830. } elseif (isset($this->domains['*']) && !empty($domain2)) {
  831. // 泛二级域名
  832. if ('www' != $domain2) {
  833. $item = $this->domains['*'];
  834. $panDomain = $domain2;
  835. }
  836. }
  837. if (isset($panDomain)) {
  838. // 保存当前泛域名
  839. $this->request->panDomain($panDomain);
  840. }
  841. }
  842. if (false === $item) {
  843. // 检测当前完整域名
  844. $item = $this->domains[$this->host];
  845. }
  846. if (is_string($item)) {
  847. $item = $this->domains[$item];
  848. }
  849. return $item;
  850. }
  851. /**
  852. * 分析路由规则中的变量
  853. * @access public
  854. * @param string $rule 路由规则
  855. * @return array
  856. */
  857. public function parseVar($rule)
  858. {
  859. // 提取路由规则中的变量
  860. $var = [];
  861. foreach (explode('/', $rule) as $val) {
  862. $optional = false;
  863. if (false !== strpos($val, '<') && preg_match_all('/<(\w+(\??))>/', $val, $matches)) {
  864. foreach ($matches[1] as $name) {
  865. if (strpos($name, '?')) {
  866. $name = substr($name, 0, -1);
  867. $optional = true;
  868. } else {
  869. $optional = false;
  870. }
  871. $var[$name] = $optional ? 2 : 1;
  872. }
  873. }
  874. if (0 === strpos($val, '[:')) {
  875. // 可选参数
  876. $optional = true;
  877. $val = substr($val, 1, -1);
  878. }
  879. if (0 === strpos($val, ':')) {
  880. // URL变量
  881. $name = substr($val, 1);
  882. if ('$' == substr($name, -1)) {
  883. $name = substr($name, 0, -1);
  884. }
  885. $var[$name] = $optional ? 2 : 1;
  886. }
  887. }
  888. return $var;
  889. }
  890. /**
  891. * 设置全局的路由分组参数
  892. * @access public
  893. * @param string $method 方法名
  894. * @param array $args 调用参数
  895. * @return RuleGroup
  896. */
  897. public function __call($method, $args)
  898. {
  899. return call_user_func_array([$this->group, $method], $args);
  900. }
  901. }