Route.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006~2021 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. declare (strict_types = 1);
  12. namespace think;
  13. use Closure;
  14. use think\exception\RouteNotFoundException;
  15. use think\route\Dispatch;
  16. use think\route\dispatch\Callback;
  17. use think\route\dispatch\Url as UrlDispatch;
  18. use think\route\Domain;
  19. use think\route\Resource;
  20. use think\route\ResourceRegister;
  21. use think\route\Rule;
  22. use think\route\RuleGroup;
  23. use think\route\RuleItem;
  24. use think\route\RuleName;
  25. use think\route\Url as UrlBuild;
  26. /**
  27. * 路由管理类
  28. * @package think
  29. */
  30. class Route
  31. {
  32. /**
  33. * REST定义
  34. * @var array
  35. */
  36. protected $rest = [
  37. 'index' => ['get', '', 'index'],
  38. 'create' => ['get', '/create', 'create'],
  39. 'edit' => ['get', '/<id>/edit', 'edit'],
  40. 'read' => ['get', '/<id>', 'read'],
  41. 'save' => ['post', '', 'save'],
  42. 'update' => ['put', '/<id>', 'update'],
  43. 'delete' => ['delete', '/<id>', 'delete'],
  44. ];
  45. /**
  46. * 配置参数
  47. * @var array
  48. */
  49. protected $config = [
  50. // pathinfo分隔符
  51. 'pathinfo_depr' => '/',
  52. // 是否开启路由延迟解析
  53. 'url_lazy_route' => false,
  54. // 是否强制使用路由
  55. 'url_route_must' => false,
  56. // 合并路由规则
  57. 'route_rule_merge' => false,
  58. // 路由是否完全匹配
  59. 'route_complete_match' => false,
  60. // 去除斜杠
  61. 'remove_slash' => false,
  62. // 使用注解路由
  63. 'route_annotation' => false,
  64. // 默认的路由变量规则
  65. 'default_route_pattern' => '[\w\.]+',
  66. // URL伪静态后缀
  67. 'url_html_suffix' => 'html',
  68. // 访问控制器层名称
  69. 'controller_layer' => 'controller',
  70. // 空控制器名
  71. 'empty_controller' => 'Error',
  72. // 是否使用控制器后缀
  73. 'controller_suffix' => false,
  74. // 默认控制器名
  75. 'default_controller' => 'Index',
  76. // 默认操作名
  77. 'default_action' => 'index',
  78. // 操作方法后缀
  79. 'action_suffix' => '',
  80. // 非路由变量是否使用普通参数方式(用于URL生成)
  81. 'url_common_param' => true,
  82. ];
  83. /**
  84. * 当前应用
  85. * @var App
  86. */
  87. protected $app;
  88. /**
  89. * 请求对象
  90. * @var Request
  91. */
  92. protected $request;
  93. /**
  94. * @var RuleName
  95. */
  96. protected $ruleName;
  97. /**
  98. * 当前HOST
  99. * @var string
  100. */
  101. protected $host;
  102. /**
  103. * 当前分组对象
  104. * @var RuleGroup
  105. */
  106. protected $group;
  107. /**
  108. * 路由绑定
  109. * @var array
  110. */
  111. protected $bind = [];
  112. /**
  113. * 域名对象
  114. * @var Domain[]
  115. */
  116. protected $domains = [];
  117. /**
  118. * 跨域路由规则
  119. * @var RuleGroup
  120. */
  121. protected $cross;
  122. /**
  123. * 路由是否延迟解析
  124. * @var bool
  125. */
  126. protected $lazy = false;
  127. /**
  128. * 路由是否测试模式
  129. * @var bool
  130. */
  131. protected $isTest = false;
  132. /**
  133. * (分组)路由规则是否合并解析
  134. * @var bool
  135. */
  136. protected $mergeRuleRegex = false;
  137. /**
  138. * 是否去除URL最后的斜线
  139. * @var bool
  140. */
  141. protected $removeSlash = false;
  142. public function __construct(App $app)
  143. {
  144. $this->app = $app;
  145. $this->ruleName = new RuleName();
  146. $this->setDefaultDomain();
  147. if (is_file($this->app->getRuntimePath() . 'route.php')) {
  148. // 读取路由映射文件
  149. $this->import(include $this->app->getRuntimePath() . 'route.php');
  150. }
  151. $this->config = array_merge($this->config, $this->app->config->get('route'));
  152. $this->init();
  153. }
  154. protected function init()
  155. {
  156. if (!empty($this->config['middleware'])) {
  157. $this->app->middleware->import($this->config['middleware'], 'route');
  158. }
  159. $this->lazy($this->config['url_lazy_route']);
  160. $this->mergeRuleRegex = $this->config['route_rule_merge'];
  161. $this->removeSlash = $this->config['remove_slash'];
  162. $this->group->removeSlash($this->removeSlash);
  163. // 注册全局MISS路由
  164. $this->miss(function () {
  165. return Response::create('', 'html', 204)->header(['Allow' => 'GET, POST, PUT, DELETE']);
  166. }, 'options')->allowCrossDomain();
  167. }
  168. public function config(string $name = null)
  169. {
  170. if (is_null($name)) {
  171. return $this->config;
  172. }
  173. return $this->config[$name] ?? null;
  174. }
  175. /**
  176. * 设置路由域名及分组(包括资源路由)是否延迟解析
  177. * @access public
  178. * @param bool $lazy 路由是否延迟解析
  179. * @return $this
  180. */
  181. public function lazy(bool $lazy = true)
  182. {
  183. $this->lazy = $lazy;
  184. return $this;
  185. }
  186. /**
  187. * 设置路由为测试模式
  188. * @access public
  189. * @param bool $test 路由是否测试模式
  190. * @return void
  191. */
  192. public function setTestMode(bool $test): void
  193. {
  194. $this->isTest = $test;
  195. }
  196. /**
  197. * 检查路由是否为测试模式
  198. * @access public
  199. * @return bool
  200. */
  201. public function isTest(): bool
  202. {
  203. return $this->isTest;
  204. }
  205. /**
  206. * 设置路由域名及分组(包括资源路由)是否合并解析
  207. * @access public
  208. * @param bool $merge 路由是否合并解析
  209. * @return $this
  210. */
  211. public function mergeRuleRegex(bool $merge = true)
  212. {
  213. $this->mergeRuleRegex = $merge;
  214. $this->group->mergeRuleRegex($merge);
  215. return $this;
  216. }
  217. /**
  218. * 初始化默认域名
  219. * @access protected
  220. * @return void
  221. */
  222. protected function setDefaultDomain(): void
  223. {
  224. // 注册默认域名
  225. $domain = new Domain($this);
  226. $this->domains['-'] = $domain;
  227. // 默认分组
  228. $this->group = $domain;
  229. }
  230. /**
  231. * 设置当前分组
  232. * @access public
  233. * @param RuleGroup $group 域名
  234. * @return void
  235. */
  236. public function setGroup(RuleGroup $group): void
  237. {
  238. $this->group = $group;
  239. }
  240. /**
  241. * 获取指定标识的路由分组 不指定则获取当前分组
  242. * @access public
  243. * @param string $name 分组标识
  244. * @return RuleGroup
  245. */
  246. public function getGroup(string $name = null)
  247. {
  248. return $name ? $this->ruleName->getGroup($name) : $this->group;
  249. }
  250. /**
  251. * 注册变量规则
  252. * @access public
  253. * @param array $pattern 变量规则
  254. * @return $this
  255. */
  256. public function pattern(array $pattern)
  257. {
  258. $this->group->pattern($pattern);
  259. return $this;
  260. }
  261. /**
  262. * 注册路由参数
  263. * @access public
  264. * @param array $option 参数
  265. * @return $this
  266. */
  267. public function option(array $option)
  268. {
  269. $this->group->option($option);
  270. return $this;
  271. }
  272. /**
  273. * 注册域名路由
  274. * @access public
  275. * @param string|array $name 子域名
  276. * @param mixed $rule 路由规则
  277. * @return Domain
  278. */
  279. public function domain($name, $rule = null): Domain
  280. {
  281. // 支持多个域名使用相同路由规则
  282. $domainName = is_array($name) ? array_shift($name) : $name;
  283. if (!isset($this->domains[$domainName])) {
  284. $domain = (new Domain($this, $domainName, $rule, $this->lazy))
  285. ->removeSlash($this->removeSlash)
  286. ->mergeRuleRegex($this->mergeRuleRegex);
  287. $this->domains[$domainName] = $domain;
  288. } else {
  289. $domain = $this->domains[$domainName];
  290. $domain->parseGroupRule($rule);
  291. }
  292. if (is_array($name) && !empty($name)) {
  293. foreach ($name as $item) {
  294. $this->domains[$item] = $domainName;
  295. }
  296. }
  297. // 返回域名对象
  298. return $domain;
  299. }
  300. /**
  301. * 获取域名
  302. * @access public
  303. * @return array
  304. */
  305. public function getDomains(): array
  306. {
  307. return $this->domains;
  308. }
  309. /**
  310. * 获取RuleName对象
  311. * @access public
  312. * @return RuleName
  313. */
  314. public function getRuleName(): RuleName
  315. {
  316. return $this->ruleName;
  317. }
  318. /**
  319. * 设置路由绑定
  320. * @access public
  321. * @param string $bind 绑定信息
  322. * @param string $domain 域名
  323. * @return $this
  324. */
  325. public function bind(string $bind, string $domain = null)
  326. {
  327. $domain = is_null($domain) ? '-' : $domain;
  328. $this->bind[$domain] = $bind;
  329. return $this;
  330. }
  331. /**
  332. * 读取路由绑定信息
  333. * @access public
  334. * @return array
  335. */
  336. public function getBind(): array
  337. {
  338. return $this->bind;
  339. }
  340. /**
  341. * 读取路由绑定
  342. * @access public
  343. * @param string $domain 域名
  344. * @return string|null
  345. */
  346. public function getDomainBind(string $domain = null)
  347. {
  348. if (is_null($domain)) {
  349. $domain = $this->host;
  350. } elseif (false === strpos($domain, '.') && $this->request) {
  351. $domain .= '.' . $this->request->rootDomain();
  352. }
  353. if ($this->request) {
  354. $subDomain = $this->request->subDomain();
  355. if (strpos($subDomain, '.')) {
  356. $name = '*' . strstr($subDomain, '.');
  357. }
  358. }
  359. if (isset($this->bind[$domain])) {
  360. $result = $this->bind[$domain];
  361. } elseif (isset($name) && isset($this->bind[$name])) {
  362. $result = $this->bind[$name];
  363. } elseif (!empty($subDomain) && isset($this->bind['*'])) {
  364. $result = $this->bind['*'];
  365. } else {
  366. $result = null;
  367. }
  368. return $result;
  369. }
  370. /**
  371. * 读取路由标识
  372. * @access public
  373. * @param string $name 路由标识
  374. * @param string $domain 域名
  375. * @param string $method 请求类型
  376. * @return array
  377. */
  378. public function getName(string $name = null, string $domain = null, string $method = '*'): array
  379. {
  380. return $this->ruleName->getName($name, $domain, $method);
  381. }
  382. /**
  383. * 批量导入路由标识
  384. * @access public
  385. * @param array $name 路由标识
  386. * @return void
  387. */
  388. public function import(array $name): void
  389. {
  390. $this->ruleName->import($name);
  391. }
  392. /**
  393. * 注册路由标识
  394. * @access public
  395. * @param string $name 路由标识
  396. * @param RuleItem $ruleItem 路由规则
  397. * @param bool $first 是否优先
  398. * @return void
  399. */
  400. public function setName(string $name, RuleItem $ruleItem, bool $first = false): void
  401. {
  402. $this->ruleName->setName($name, $ruleItem, $first);
  403. }
  404. /**
  405. * 保存路由规则
  406. * @access public
  407. * @param string $rule 路由规则
  408. * @param RuleItem $ruleItem RuleItem对象
  409. * @return void
  410. */
  411. public function setRule(string $rule, RuleItem $ruleItem = null): void
  412. {
  413. $this->ruleName->setRule($rule, $ruleItem);
  414. }
  415. /**
  416. * 读取路由
  417. * @access public
  418. * @param string $rule 路由规则
  419. * @return RuleItem[]
  420. */
  421. public function getRule(string $rule): array
  422. {
  423. return $this->ruleName->getRule($rule);
  424. }
  425. /**
  426. * 读取路由列表
  427. * @access public
  428. * @return array
  429. */
  430. public function getRuleList(): array
  431. {
  432. return $this->ruleName->getRuleList();
  433. }
  434. /**
  435. * 清空路由规则
  436. * @access public
  437. * @return void
  438. */
  439. public function clear(): void
  440. {
  441. $this->ruleName->clear();
  442. if ($this->group) {
  443. $this->group->clear();
  444. }
  445. }
  446. /**
  447. * 注册路由规则
  448. * @access public
  449. * @param string $rule 路由规则
  450. * @param mixed $route 路由地址
  451. * @param string $method 请求类型
  452. * @return RuleItem
  453. */
  454. public function rule(string $rule, $route = null, string $method = '*'): RuleItem
  455. {
  456. if ($route instanceof Response) {
  457. // 兼容之前的路由到响应对象,感觉不需要,使用场景很少,闭包就能实现
  458. $route = function () use ($route) {
  459. return $route;
  460. };
  461. }
  462. return $this->group->addRule($rule, $route, $method);
  463. }
  464. /**
  465. * 设置路由规则全局有效
  466. * @access public
  467. * @param Rule $rule 路由规则
  468. * @return $this
  469. */
  470. public function setCrossDomainRule(Rule $rule)
  471. {
  472. if (!isset($this->cross)) {
  473. $this->cross = (new RuleGroup($this))->mergeRuleRegex($this->mergeRuleRegex);
  474. }
  475. $this->cross->addRuleItem($rule);
  476. return $this;
  477. }
  478. /**
  479. * 注册路由分组
  480. * @access public
  481. * @param string|\Closure $name 分组名称或者参数
  482. * @param mixed $route 分组路由
  483. * @return RuleGroup
  484. */
  485. public function group($name, $route = null): RuleGroup
  486. {
  487. if ($name instanceof Closure) {
  488. $route = $name;
  489. $name = '';
  490. }
  491. return (new RuleGroup($this, $this->group, $name, $route, $this->lazy))
  492. ->removeSlash($this->removeSlash)
  493. ->mergeRuleRegex($this->mergeRuleRegex);
  494. }
  495. /**
  496. * 注册路由
  497. * @access public
  498. * @param string $rule 路由规则
  499. * @param mixed $route 路由地址
  500. * @return RuleItem
  501. */
  502. public function any(string $rule, $route): RuleItem
  503. {
  504. return $this->rule($rule, $route, '*');
  505. }
  506. /**
  507. * 注册GET路由
  508. * @access public
  509. * @param string $rule 路由规则
  510. * @param mixed $route 路由地址
  511. * @return RuleItem
  512. */
  513. public function get(string $rule, $route): RuleItem
  514. {
  515. return $this->rule($rule, $route, 'GET');
  516. }
  517. /**
  518. * 注册POST路由
  519. * @access public
  520. * @param string $rule 路由规则
  521. * @param mixed $route 路由地址
  522. * @return RuleItem
  523. */
  524. public function post(string $rule, $route): RuleItem
  525. {
  526. return $this->rule($rule, $route, 'POST');
  527. }
  528. /**
  529. * 注册PUT路由
  530. * @access public
  531. * @param string $rule 路由规则
  532. * @param mixed $route 路由地址
  533. * @return RuleItem
  534. */
  535. public function put(string $rule, $route): RuleItem
  536. {
  537. return $this->rule($rule, $route, 'PUT');
  538. }
  539. /**
  540. * 注册DELETE路由
  541. * @access public
  542. * @param string $rule 路由规则
  543. * @param mixed $route 路由地址
  544. * @return RuleItem
  545. */
  546. public function delete(string $rule, $route): RuleItem
  547. {
  548. return $this->rule($rule, $route, 'DELETE');
  549. }
  550. /**
  551. * 注册PATCH路由
  552. * @access public
  553. * @param string $rule 路由规则
  554. * @param mixed $route 路由地址
  555. * @return RuleItem
  556. */
  557. public function patch(string $rule, $route): RuleItem
  558. {
  559. return $this->rule($rule, $route, 'PATCH');
  560. }
  561. /**
  562. * 注册HEAD路由
  563. * @access public
  564. * @param string $rule 路由规则
  565. * @param mixed $route 路由地址
  566. * @return RuleItem
  567. */
  568. public function head(string $rule, $route): RuleItem
  569. {
  570. return $this->rule($rule, $route, 'HEAD');
  571. }
  572. /**
  573. * 注册OPTIONS路由
  574. * @access public
  575. * @param string $rule 路由规则
  576. * @param mixed $route 路由地址
  577. * @return RuleItem
  578. */
  579. public function options(string $rule, $route): RuleItem
  580. {
  581. return $this->rule($rule, $route, 'OPTIONS');
  582. }
  583. /**
  584. * 注册资源路由
  585. * @access public
  586. * @param string $rule 路由规则
  587. * @param string $route 路由地址
  588. * @return Resource|ResourceRegister
  589. */
  590. public function resource(string $rule, string $route)
  591. {
  592. $resource = new Resource($this, $this->group, $rule, $route, $this->rest);
  593. if (!$this->lazy) {
  594. return new ResourceRegister($resource);
  595. }
  596. return $resource;
  597. }
  598. /**
  599. * 注册视图路由
  600. * @access public
  601. * @param string $rule 路由规则
  602. * @param string $template 路由模板地址
  603. * @param array $vars 模板变量
  604. * @return RuleItem
  605. */
  606. public function view(string $rule, string $template = '', array $vars = []): RuleItem
  607. {
  608. return $this->rule($rule, function () use ($vars, $template) {
  609. return Response::create($template, 'view')->assign($vars);
  610. }, 'GET');
  611. }
  612. /**
  613. * 注册重定向路由
  614. * @access public
  615. * @param string $rule 路由规则
  616. * @param string $route 路由地址
  617. * @param int $status 状态码
  618. * @return RuleItem
  619. */
  620. public function redirect(string $rule, string $route = '', int $status = 301): RuleItem
  621. {
  622. return $this->rule($rule, function (Request $request) use ($status, $route) {
  623. $search = $replace = [];
  624. $matches = $request->rule()->getVars();
  625. foreach ($matches as $key => $value) {
  626. $search[] = '<' . $key . '>';
  627. $replace[] = $value;
  628. $search[] = ':' . $key;
  629. $replace[] = $value;
  630. }
  631. $route = str_replace($search, $replace, $route);
  632. return Response::create($route, 'redirect')->code($status);
  633. }, '*');
  634. }
  635. /**
  636. * rest方法定义和修改
  637. * @access public
  638. * @param string|array $name 方法名称
  639. * @param array|bool $resource 资源
  640. * @return $this
  641. */
  642. public function rest($name, $resource = [])
  643. {
  644. if (is_array($name)) {
  645. $this->rest = $resource ? $name : array_merge($this->rest, $name);
  646. } else {
  647. $this->rest[$name] = $resource;
  648. }
  649. return $this;
  650. }
  651. /**
  652. * 获取rest方法定义的参数
  653. * @access public
  654. * @param string $name 方法名称
  655. * @return array|null
  656. */
  657. public function getRest(string $name = null)
  658. {
  659. if (is_null($name)) {
  660. return $this->rest;
  661. }
  662. return $this->rest[$name] ?? null;
  663. }
  664. /**
  665. * 注册未匹配路由规则后的处理
  666. * @access public
  667. * @param string|Closure $route 路由地址
  668. * @param string $method 请求类型
  669. * @return RuleItem
  670. */
  671. public function miss($route, string $method = '*'): RuleItem
  672. {
  673. return $this->group->miss($route, $method);
  674. }
  675. /**
  676. * 路由调度
  677. * @param Request $request
  678. * @param Closure|bool $withRoute
  679. * @return Response
  680. */
  681. public function dispatch(Request $request, $withRoute = true)
  682. {
  683. $this->request = $request;
  684. $this->host = $this->request->host(true);
  685. if ($withRoute) {
  686. //加载路由
  687. if ($withRoute instanceof Closure) {
  688. $withRoute();
  689. }
  690. $dispatch = $this->check();
  691. } else {
  692. $dispatch = $this->url($this->path());
  693. }
  694. $dispatch->init($this->app);
  695. return $this->app->middleware->pipeline('route')
  696. ->send($request)
  697. ->then(function () use ($dispatch) {
  698. return $dispatch->run();
  699. });
  700. }
  701. /**
  702. * 检测URL路由
  703. * @access public
  704. * @return Dispatch|false
  705. * @throws RouteNotFoundException
  706. */
  707. public function check()
  708. {
  709. // 自动检测域名路由
  710. $url = str_replace($this->config['pathinfo_depr'], '|', $this->path());
  711. $completeMatch = $this->config['route_complete_match'];
  712. $result = $this->checkDomain()->check($this->request, $url, $completeMatch);
  713. if (false === $result && !empty($this->cross)) {
  714. // 检测跨域路由
  715. $result = $this->cross->check($this->request, $url, $completeMatch);
  716. }
  717. if (false !== $result) {
  718. return $result;
  719. } elseif ($this->config['url_route_must']) {
  720. throw new RouteNotFoundException();
  721. }
  722. return $this->url($url);
  723. }
  724. /**
  725. * 获取当前请求URL的pathinfo信息(不含URL后缀)
  726. * @access protected
  727. * @return string
  728. */
  729. protected function path(): string
  730. {
  731. $suffix = $this->config['url_html_suffix'];
  732. $pathinfo = $this->request->pathinfo();
  733. if (false === $suffix) {
  734. // 禁止伪静态访问
  735. $path = $pathinfo;
  736. } elseif ($suffix) {
  737. // 去除正常的URL后缀
  738. $path = preg_replace('/\.(' . ltrim($suffix, '.') . ')$/i', '', $pathinfo);
  739. } else {
  740. // 允许任何后缀访问
  741. $path = preg_replace('/\.' . $this->request->ext() . '$/i', '', $pathinfo);
  742. }
  743. return $path;
  744. }
  745. /**
  746. * 默认URL解析
  747. * @access public
  748. * @param string $url URL地址
  749. * @return Dispatch
  750. */
  751. public function url(string $url): Dispatch
  752. {
  753. if ($this->request->method() == 'OPTIONS') {
  754. // 自动响应options请求
  755. return new Callback($this->request, $this->group, function () {
  756. return Response::create('', 'html', 204)->header(['Allow' => 'GET, POST, PUT, DELETE']);
  757. });
  758. }
  759. return new UrlDispatch($this->request, $this->group, $url);
  760. }
  761. /**
  762. * 检测域名的路由规则
  763. * @access protected
  764. * @return Domain
  765. */
  766. protected function checkDomain(): Domain
  767. {
  768. $item = false;
  769. if (count($this->domains) > 1) {
  770. // 获取当前子域名
  771. $subDomain = $this->request->subDomain();
  772. $domain = $subDomain ? explode('.', $subDomain) : [];
  773. $domain2 = $domain ? array_pop($domain) : '';
  774. if ($domain) {
  775. // 存在三级域名
  776. $domain3 = array_pop($domain);
  777. }
  778. if (isset($this->domains[$this->host])) {
  779. // 子域名配置
  780. $item = $this->domains[$this->host];
  781. } elseif (isset($this->domains[$subDomain])) {
  782. $item = $this->domains[$subDomain];
  783. } elseif (isset($this->domains['*.' . $domain2]) && !empty($domain3)) {
  784. // 泛三级域名
  785. $item = $this->domains['*.' . $domain2];
  786. $panDomain = $domain3;
  787. } elseif (isset($this->domains['*']) && !empty($domain2)) {
  788. // 泛二级域名
  789. if ('www' != $domain2) {
  790. $item = $this->domains['*'];
  791. $panDomain = $domain2;
  792. }
  793. }
  794. if (isset($panDomain)) {
  795. // 保存当前泛域名
  796. $this->request->setPanDomain($panDomain);
  797. }
  798. }
  799. if (false === $item) {
  800. // 检测全局域名规则
  801. $item = $this->domains['-'];
  802. }
  803. if (is_string($item)) {
  804. $item = $this->domains[$item];
  805. }
  806. return $item;
  807. }
  808. /**
  809. * URL生成 支持路由反射
  810. * @access public
  811. * @param string $url 路由地址
  812. * @param array $vars 参数 ['a'=>'val1', 'b'=>'val2']
  813. * @return UrlBuild
  814. */
  815. public function buildUrl(string $url = '', array $vars = []): UrlBuild
  816. {
  817. return $this->app->make(UrlBuild::class, [$this, $this->app, $url, $vars], true);
  818. }
  819. /**
  820. * 设置全局的路由分组参数
  821. * @access public
  822. * @param string $method 方法名
  823. * @param array $args 调用参数
  824. * @return RuleGroup
  825. */
  826. public function __call($method, $args)
  827. {
  828. return call_user_func_array([$this->group, $method], $args);
  829. }
  830. }