Rule.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  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\route;
  13. use Closure;
  14. use think\Container;
  15. use think\middleware\AllowCrossDomain;
  16. use think\middleware\CheckRequestCache;
  17. use think\middleware\FormTokenCheck;
  18. use think\Request;
  19. use think\Route;
  20. use think\route\dispatch\Callback as CallbackDispatch;
  21. use think\route\dispatch\Controller as ControllerDispatch;
  22. /**
  23. * 路由规则基础类
  24. */
  25. abstract class Rule
  26. {
  27. /**
  28. * 路由标识
  29. * @var string
  30. */
  31. protected $name;
  32. /**
  33. * 所在域名
  34. * @var string
  35. */
  36. protected $domain;
  37. /**
  38. * 路由对象
  39. * @var Route
  40. */
  41. protected $router;
  42. /**
  43. * 路由所属分组
  44. * @var RuleGroup
  45. */
  46. protected $parent;
  47. /**
  48. * 路由规则
  49. * @var mixed
  50. */
  51. protected $rule;
  52. /**
  53. * 路由地址
  54. * @var string|Closure
  55. */
  56. protected $route;
  57. /**
  58. * 请求类型
  59. * @var string
  60. */
  61. protected $method;
  62. /**
  63. * 路由变量
  64. * @var array
  65. */
  66. protected $vars = [];
  67. /**
  68. * 路由参数
  69. * @var array
  70. */
  71. protected $option = [];
  72. /**
  73. * 路由变量规则
  74. * @var array
  75. */
  76. protected $pattern = [];
  77. /**
  78. * 需要和分组合并的路由参数
  79. * @var array
  80. */
  81. protected $mergeOptions = ['model', 'append', 'middleware'];
  82. abstract public function check(Request $request, string $url, bool $completeMatch = false);
  83. /**
  84. * 设置路由参数
  85. * @access public
  86. * @param array $option 参数
  87. * @return $this
  88. */
  89. public function option(array $option)
  90. {
  91. $this->option = array_merge($this->option, $option);
  92. return $this;
  93. }
  94. /**
  95. * 设置单个路由参数
  96. * @access public
  97. * @param string $name 参数名
  98. * @param mixed $value 值
  99. * @return $this
  100. */
  101. public function setOption(string $name, $value)
  102. {
  103. $this->option[$name] = $value;
  104. return $this;
  105. }
  106. /**
  107. * 注册变量规则
  108. * @access public
  109. * @param array $pattern 变量规则
  110. * @return $this
  111. */
  112. public function pattern(array $pattern)
  113. {
  114. $this->pattern = array_merge($this->pattern, $pattern);
  115. return $this;
  116. }
  117. /**
  118. * 设置标识
  119. * @access public
  120. * @param string $name 标识名
  121. * @return $this
  122. */
  123. public function name(string $name)
  124. {
  125. $this->name = $name;
  126. return $this;
  127. }
  128. /**
  129. * 获取路由对象
  130. * @access public
  131. * @return Route
  132. */
  133. public function getRouter(): Route
  134. {
  135. return $this->router;
  136. }
  137. /**
  138. * 获取Name
  139. * @access public
  140. * @return string
  141. */
  142. public function getName(): string
  143. {
  144. return $this->name ?: '';
  145. }
  146. /**
  147. * 获取当前路由规则
  148. * @access public
  149. * @return mixed
  150. */
  151. public function getRule()
  152. {
  153. return $this->rule;
  154. }
  155. /**
  156. * 获取当前路由地址
  157. * @access public
  158. * @return mixed
  159. */
  160. public function getRoute()
  161. {
  162. return $this->route;
  163. }
  164. /**
  165. * 获取当前路由的变量
  166. * @access public
  167. * @return array
  168. */
  169. public function getVars(): array
  170. {
  171. return $this->vars;
  172. }
  173. /**
  174. * 获取Parent对象
  175. * @access public
  176. * @return $this|null
  177. */
  178. public function getParent()
  179. {
  180. return $this->parent;
  181. }
  182. /**
  183. * 获取路由所在域名
  184. * @access public
  185. * @return string
  186. */
  187. public function getDomain(): string
  188. {
  189. return $this->domain ?: $this->parent->getDomain();
  190. }
  191. /**
  192. * 获取路由参数
  193. * @access public
  194. * @param string $name 变量名
  195. * @return mixed
  196. */
  197. public function config(string $name = '')
  198. {
  199. return $this->router->config($name);
  200. }
  201. /**
  202. * 获取变量规则定义
  203. * @access public
  204. * @param string $name 变量名
  205. * @return mixed
  206. */
  207. public function getPattern(string $name = '')
  208. {
  209. $pattern = $this->pattern;
  210. if ($this->parent) {
  211. $pattern = array_merge($this->parent->getPattern(), $pattern);
  212. }
  213. if ('' === $name) {
  214. return $pattern;
  215. }
  216. return $pattern[$name] ?? null;
  217. }
  218. /**
  219. * 获取路由参数定义
  220. * @access public
  221. * @param string $name 参数名
  222. * @param mixed $default 默认值
  223. * @return mixed
  224. */
  225. public function getOption(string $name = '', $default = null)
  226. {
  227. $option = $this->option;
  228. if ($this->parent) {
  229. $parentOption = $this->parent->getOption();
  230. // 合并分组参数
  231. foreach ($this->mergeOptions as $item) {
  232. if (isset($parentOption[$item]) && isset($option[$item])) {
  233. $option[$item] = array_merge($parentOption[$item], $option[$item]);
  234. }
  235. }
  236. $option = array_merge($parentOption, $option);
  237. }
  238. if ('' === $name) {
  239. return $option;
  240. }
  241. return $option[$name] ?? $default;
  242. }
  243. /**
  244. * 获取当前路由的请求类型
  245. * @access public
  246. * @return string
  247. */
  248. public function getMethod(): string
  249. {
  250. return strtolower($this->method);
  251. }
  252. /**
  253. * 设置路由请求类型
  254. * @access public
  255. * @param string $method 请求类型
  256. * @return $this
  257. */
  258. public function method(string $method)
  259. {
  260. return $this->setOption('method', strtolower($method));
  261. }
  262. /**
  263. * 检查后缀
  264. * @access public
  265. * @param string $ext URL后缀
  266. * @return $this
  267. */
  268. public function ext(string $ext = '')
  269. {
  270. return $this->setOption('ext', $ext);
  271. }
  272. /**
  273. * 检查禁止后缀
  274. * @access public
  275. * @param string $ext URL后缀
  276. * @return $this
  277. */
  278. public function denyExt(string $ext = '')
  279. {
  280. return $this->setOption('deny_ext', $ext);
  281. }
  282. /**
  283. * 检查域名
  284. * @access public
  285. * @param string $domain 域名
  286. * @return $this
  287. */
  288. public function domain(string $domain)
  289. {
  290. $this->domain = $domain;
  291. return $this->setOption('domain', $domain);
  292. }
  293. /**
  294. * 设置参数过滤检查
  295. * @access public
  296. * @param array $filter 参数过滤
  297. * @return $this
  298. */
  299. public function filter(array $filter)
  300. {
  301. $this->option['filter'] = $filter;
  302. return $this;
  303. }
  304. /**
  305. * 绑定模型
  306. * @access public
  307. * @param array|string|Closure $var 路由变量名 多个使用 & 分割
  308. * @param string|Closure $model 绑定模型类
  309. * @param bool $exception 是否抛出异常
  310. * @return $this
  311. */
  312. public function model($var, $model = null, bool $exception = true)
  313. {
  314. if ($var instanceof Closure) {
  315. $this->option['model'][] = $var;
  316. } elseif (is_array($var)) {
  317. $this->option['model'] = $var;
  318. } elseif (is_null($model)) {
  319. $this->option['model']['id'] = [$var, true];
  320. } else {
  321. $this->option['model'][$var] = [$model, $exception];
  322. }
  323. return $this;
  324. }
  325. /**
  326. * 附加路由隐式参数
  327. * @access public
  328. * @param array $append 追加参数
  329. * @return $this
  330. */
  331. public function append(array $append = [])
  332. {
  333. $this->option['append'] = $append;
  334. return $this;
  335. }
  336. /**
  337. * 绑定验证
  338. * @access public
  339. * @param mixed $validate 验证器类
  340. * @param string $scene 验证场景
  341. * @param array $message 验证提示
  342. * @param bool $batch 批量验证
  343. * @return $this
  344. */
  345. public function validate($validate, string $scene = null, array $message = [], bool $batch = false)
  346. {
  347. $this->option['validate'] = [$validate, $scene, $message, $batch];
  348. return $this;
  349. }
  350. /**
  351. * 指定路由中间件
  352. * @access public
  353. * @param string|array|Closure $middleware 中间件
  354. * @param mixed $params 参数
  355. * @return $this
  356. */
  357. public function middleware($middleware, ...$params)
  358. {
  359. if (empty($params) && is_array($middleware)) {
  360. $this->option['middleware'] = $middleware;
  361. } else {
  362. foreach ((array) $middleware as $item) {
  363. $this->option['middleware'][] = [$item, $params];
  364. }
  365. }
  366. return $this;
  367. }
  368. /**
  369. * 允许跨域
  370. * @access public
  371. * @param array $header 自定义Header
  372. * @return $this
  373. */
  374. public function allowCrossDomain(array $header = [])
  375. {
  376. return $this->middleware(AllowCrossDomain::class, $header);
  377. }
  378. /**
  379. * 表单令牌验证
  380. * @access public
  381. * @param string $token 表单令牌token名称
  382. * @return $this
  383. */
  384. public function token(string $token = '__token__')
  385. {
  386. return $this->middleware(FormTokenCheck::class, $token);
  387. }
  388. /**
  389. * 设置路由缓存
  390. * @access public
  391. * @param array|string $cache 缓存
  392. * @return $this
  393. */
  394. public function cache($cache)
  395. {
  396. return $this->middleware(CheckRequestCache::class, $cache);
  397. }
  398. /**
  399. * 检查URL分隔符
  400. * @access public
  401. * @param string $depr URL分隔符
  402. * @return $this
  403. */
  404. public function depr(string $depr)
  405. {
  406. return $this->setOption('param_depr', $depr);
  407. }
  408. /**
  409. * 设置需要合并的路由参数
  410. * @access public
  411. * @param array $option 路由参数
  412. * @return $this
  413. */
  414. public function mergeOptions(array $option = [])
  415. {
  416. $this->mergeOptions = array_merge($this->mergeOptions, $option);
  417. return $this;
  418. }
  419. /**
  420. * 检查是否为HTTPS请求
  421. * @access public
  422. * @param bool $https 是否为HTTPS
  423. * @return $this
  424. */
  425. public function https(bool $https = true)
  426. {
  427. return $this->setOption('https', $https);
  428. }
  429. /**
  430. * 检查是否为JSON请求
  431. * @access public
  432. * @param bool $json 是否为JSON
  433. * @return $this
  434. */
  435. public function json(bool $json = true)
  436. {
  437. return $this->setOption('json', $json);
  438. }
  439. /**
  440. * 检查是否为AJAX请求
  441. * @access public
  442. * @param bool $ajax 是否为AJAX
  443. * @return $this
  444. */
  445. public function ajax(bool $ajax = true)
  446. {
  447. return $this->setOption('ajax', $ajax);
  448. }
  449. /**
  450. * 检查是否为PJAX请求
  451. * @access public
  452. * @param bool $pjax 是否为PJAX
  453. * @return $this
  454. */
  455. public function pjax(bool $pjax = true)
  456. {
  457. return $this->setOption('pjax', $pjax);
  458. }
  459. /**
  460. * 路由到一个模板地址 需要额外传入的模板变量
  461. * @access public
  462. * @param array $view 视图
  463. * @return $this
  464. */
  465. public function view(array $view = [])
  466. {
  467. return $this->setOption('view', $view);
  468. }
  469. /**
  470. * 设置路由完整匹配
  471. * @access public
  472. * @param bool $match 是否完整匹配
  473. * @return $this
  474. */
  475. public function completeMatch(bool $match = true)
  476. {
  477. return $this->setOption('complete_match', $match);
  478. }
  479. /**
  480. * 是否去除URL最后的斜线
  481. * @access public
  482. * @param bool $remove 是否去除最后斜线
  483. * @return $this
  484. */
  485. public function removeSlash(bool $remove = true)
  486. {
  487. return $this->setOption('remove_slash', $remove);
  488. }
  489. /**
  490. * 设置路由规则全局有效
  491. * @access public
  492. * @return $this
  493. */
  494. public function crossDomainRule()
  495. {
  496. if ($this instanceof RuleGroup) {
  497. $method = '*';
  498. } else {
  499. $method = $this->method;
  500. }
  501. $this->router->setCrossDomainRule($this, $method);
  502. return $this;
  503. }
  504. /**
  505. * 解析匹配到的规则路由
  506. * @access public
  507. * @param Request $request 请求对象
  508. * @param string $rule 路由规则
  509. * @param mixed $route 路由地址
  510. * @param string $url URL地址
  511. * @param array $option 路由参数
  512. * @param array $matches 匹配的变量
  513. * @return Dispatch
  514. */
  515. public function parseRule(Request $request, string $rule, $route, string $url, array $option = [], array $matches = []): Dispatch
  516. {
  517. if (is_string($route) && isset($option['prefix'])) {
  518. // 路由地址前缀
  519. $route = $option['prefix'] . $route;
  520. }
  521. // 替换路由地址中的变量
  522. $extraParams = true;
  523. $search = $replace = [];
  524. $depr = $this->router->config('pathinfo_depr');
  525. foreach ($matches as $key => $value) {
  526. $search[] = '<' . $key . '>';
  527. $replace[] = $value;
  528. $search[] = ':' . $key;
  529. $replace[] = $value;
  530. if (strpos($value, $depr)) {
  531. $extraParams = false;
  532. }
  533. }
  534. if (is_string($route)) {
  535. $route = str_replace($search, $replace, $route);
  536. }
  537. // 解析额外参数
  538. if ($extraParams) {
  539. $count = substr_count($rule, '/');
  540. $url = array_slice(explode('|', $url), $count + 1);
  541. $this->parseUrlParams(implode('|', $url), $matches);
  542. }
  543. $this->vars = $matches;
  544. // 发起路由调度
  545. return $this->dispatch($request, $route, $option);
  546. }
  547. /**
  548. * 发起路由调度
  549. * @access protected
  550. * @param Request $request Request对象
  551. * @param mixed $route 路由地址
  552. * @param array $option 路由参数
  553. * @return Dispatch
  554. */
  555. protected function dispatch(Request $request, $route, array $option): Dispatch
  556. {
  557. if (is_subclass_of($route, Dispatch::class)) {
  558. $result = new $route($request, $this, $route, $this->vars);
  559. } elseif ($route instanceof Closure) {
  560. // 执行闭包
  561. $result = new CallbackDispatch($request, $this, $route, $this->vars);
  562. } elseif (false !== strpos($route, '@') || false !== strpos($route, '::') || false !== strpos($route, '\\')) {
  563. // 路由到类的方法
  564. $route = str_replace('::', '@', $route);
  565. $result = $this->dispatchMethod($request, $route);
  566. } else {
  567. // 路由到控制器/操作
  568. $result = $this->dispatchController($request, $route);
  569. }
  570. return $result;
  571. }
  572. /**
  573. * 解析URL地址为 模块/控制器/操作
  574. * @access protected
  575. * @param Request $request Request对象
  576. * @param string $route 路由地址
  577. * @return CallbackDispatch
  578. */
  579. protected function dispatchMethod(Request $request, string $route): CallbackDispatch
  580. {
  581. $path = $this->parseUrlPath($route);
  582. $route = str_replace('/', '@', implode('/', $path));
  583. $method = strpos($route, '@') ? explode('@', $route) : $route;
  584. $params = $this->vars;
  585. if (!empty($method[1])) {
  586. $method1Arr = parse_url($method[1]);
  587. $method[1] = $method1Arr['path'];
  588. if (!empty($method1Arr['query'])) {
  589. parse_str($method1Arr['query'], $params);
  590. if (!empty($this->vars)) {
  591. $params = array_merge($this->vars, $params);
  592. }
  593. }
  594. }
  595. return new CallbackDispatch($request, $this, $method, $params);
  596. }
  597. /**
  598. * 解析URL地址为 模块/控制器/操作
  599. * @access protected
  600. * @param Request $request Request对象
  601. * @param string $route 路由地址
  602. * @return ControllerDispatch
  603. */
  604. protected function dispatchController(Request $request, string $route): ControllerDispatch
  605. {
  606. $path = $this->parseUrlPath($route);
  607. $action = array_pop($path);
  608. $controller = !empty($path) ? array_pop($path) : null;
  609. $app = !empty($path) ? array_pop($path) : null;
  610. // 路由到模块/控制器/操作
  611. return new ControllerDispatch($request, $this, [$app, $controller, $action], $this->vars);
  612. }
  613. /**
  614. * 路由检查
  615. * @access protected
  616. * @param array $option 路由参数
  617. * @param Request $request Request对象
  618. * @return bool
  619. */
  620. protected function checkOption(array $option, Request $request): bool
  621. {
  622. // 请求类型检测
  623. if (!empty($option['method'])) {
  624. if (is_string($option['method']) && false === stripos($option['method'], $request->method())) {
  625. return false;
  626. }
  627. }
  628. // AJAX PJAX 请求检查
  629. foreach (['ajax', 'pjax', 'json'] as $item) {
  630. if (isset($option[$item])) {
  631. $call = 'is' . $item;
  632. if ($option[$item] && !$request->$call() || !$option[$item] && $request->$call()) {
  633. return false;
  634. }
  635. }
  636. }
  637. // 伪静态后缀检测
  638. if ($request->url() != '/' && ((isset($option['ext']) && false === stripos('|' . $option['ext'] . '|', '|' . $request->ext() . '|'))
  639. || (isset($option['deny_ext']) && false !== stripos('|' . $option['deny_ext'] . '|', '|' . $request->ext() . '|')))) {
  640. return false;
  641. }
  642. // 域名检查
  643. if ((isset($option['domain']) && !in_array($option['domain'], [$request->host(true), $request->subDomain()]))) {
  644. return false;
  645. }
  646. // HTTPS检查
  647. if ((isset($option['https']) && $option['https'] && !$request->isSsl())
  648. || (isset($option['https']) && !$option['https'] && $request->isSsl())) {
  649. return false;
  650. }
  651. // 请求参数检查
  652. if (isset($option['filter'])) {
  653. foreach ($option['filter'] as $name => $value) {
  654. if ($request->param($name, '', null) != $value) {
  655. return false;
  656. }
  657. }
  658. }
  659. return true;
  660. }
  661. /**
  662. * 解析URL地址中的参数Request对象
  663. * @access protected
  664. * @param string $rule 路由规则
  665. * @param array $var 变量
  666. * @return void
  667. */
  668. protected function parseUrlParams(string $url, array &$var = []): void
  669. {
  670. if ($url) {
  671. preg_replace_callback('/(\w+)\|([^\|]+)/', function ($match) use (&$var) {
  672. $var[$match[1]] = strip_tags($match[2]);
  673. }, $url);
  674. }
  675. }
  676. /**
  677. * 解析URL的pathinfo参数
  678. * @access public
  679. * @param string $url URL地址
  680. * @return array
  681. */
  682. public function parseUrlPath(string $url): array
  683. {
  684. // 分隔符替换 确保路由定义使用统一的分隔符
  685. $url = str_replace('|', '/', $url);
  686. $url = trim($url, '/');
  687. if (strpos($url, '/')) {
  688. // [控制器/操作]
  689. $path = explode('/', $url);
  690. } else {
  691. $path = [$url];
  692. }
  693. return $path;
  694. }
  695. /**
  696. * 生成路由的正则规则
  697. * @access protected
  698. * @param string $rule 路由规则
  699. * @param array $match 匹配的变量
  700. * @param array $pattern 路由变量规则
  701. * @param array $option 路由参数
  702. * @param bool $completeMatch 路由是否完全匹配
  703. * @param string $suffix 路由正则变量后缀
  704. * @return string
  705. */
  706. protected function buildRuleRegex(string $rule, array $match, array $pattern = [], array $option = [], bool $completeMatch = false, string $suffix = ''): string
  707. {
  708. foreach ($match as $name) {
  709. $value = $this->buildNameRegex($name, $pattern, $suffix);
  710. if ($value) {
  711. $origin[] = $name;
  712. $replace[] = $value;
  713. }
  714. }
  715. // 是否区分 / 地址访问
  716. if ('/' != $rule) {
  717. if (!empty($option['remove_slash'])) {
  718. $rule = rtrim($rule, '/');
  719. } elseif (substr($rule, -1) == '/') {
  720. $rule = rtrim($rule, '/');
  721. $hasSlash = true;
  722. }
  723. }
  724. $regex = isset($replace) ? str_replace($origin, $replace, $rule) : $rule;
  725. $regex = str_replace([')?/', ')?-'], [')/', ')-'], $regex);
  726. if (isset($hasSlash)) {
  727. $regex .= '/';
  728. }
  729. return $regex . ($completeMatch ? '$' : '');
  730. }
  731. /**
  732. * 生成路由变量的正则规则
  733. * @access protected
  734. * @param string $name 路由变量
  735. * @param array $pattern 变量规则
  736. * @param string $suffix 路由正则变量后缀
  737. * @return string
  738. */
  739. protected function buildNameRegex(string $name, array $pattern, string $suffix): string
  740. {
  741. $optional = '';
  742. $slash = substr($name, 0, 1);
  743. if (in_array($slash, ['/', '-'])) {
  744. $prefix = $slash;
  745. $name = substr($name, 1);
  746. $slash = substr($name, 0, 1);
  747. } else {
  748. $prefix = '';
  749. }
  750. if ('<' != $slash) {
  751. return '';
  752. }
  753. if (strpos($name, '?')) {
  754. $name = substr($name, 1, -2);
  755. $optional = '?';
  756. } elseif (strpos($name, '>')) {
  757. $name = substr($name, 1, -1);
  758. }
  759. if (isset($pattern[$name])) {
  760. $nameRule = $pattern[$name];
  761. if (0 === strpos($nameRule, '/') && '/' == substr($nameRule, -1)) {
  762. $nameRule = substr($nameRule, 1, -1);
  763. }
  764. } else {
  765. $nameRule = $this->router->config('default_route_pattern');
  766. }
  767. return '(' . $prefix . '(?<' . $name . $suffix . '>' . $nameRule . '))' . $optional;
  768. }
  769. /**
  770. * 设置路由参数
  771. * @access public
  772. * @param string $method 方法名
  773. * @param array $args 调用参数
  774. * @return $this
  775. */
  776. public function __call($method, $args)
  777. {
  778. if (count($args) > 1) {
  779. $args[0] = $args;
  780. }
  781. array_unshift($args, $method);
  782. return call_user_func_array([$this, 'setOption'], $args);
  783. }
  784. public function __sleep()
  785. {
  786. return ['name', 'rule', 'route', 'method', 'vars', 'option', 'pattern'];
  787. }
  788. public function __wakeup()
  789. {
  790. $this->router = Container::pull('route');
  791. }
  792. public function __debugInfo()
  793. {
  794. return [
  795. 'name' => $this->name,
  796. 'rule' => $this->rule,
  797. 'route' => $this->route,
  798. 'method' => $this->method,
  799. 'vars' => $this->vars,
  800. 'option' => $this->option,
  801. 'pattern' => $this->pattern,
  802. ];
  803. }
  804. }