Passed
Push — 8.0 ( d0e1cd...7f09d5 )
by liu
02:13
created

Route::bind()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 2
dl 0
loc 7
ccs 0
cts 4
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
// +----------------------------------------------------------------------
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2023 http://thinkphp.cn All rights reserved.
6
// +----------------------------------------------------------------------
7
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
8
// +----------------------------------------------------------------------
9
// | Author: liu21st <[email protected]>
10
// +----------------------------------------------------------------------
11
declare (strict_types = 1);
12
13
namespace think;
14
15
use Closure;
16
use think\exception\RouteNotFoundException;
17
use think\route\Dispatch;
18
use think\route\dispatch\Callback;
19
use think\route\Domain;
20
use think\route\Resource;
21
use think\route\ResourceRegister;
22
use think\route\Rule;
23
use think\route\RuleGroup;
24
use think\route\RuleItem;
25
use think\route\RuleName;
26
use think\route\Url as UrlBuild;
27
28
/**
29
 * 路由管理类
30
 * @package think
31
 */
32
class Route
33
{
34
    /**
35
     * REST定义
36
     * @var array
37
     */
38
    protected $rest = [
39
        'index'  => ['get', '', 'index'],
40
        'create' => ['get', '/create', 'create'],
41
        'edit'   => ['get', '/<id>/edit', 'edit'],
42
        'read'   => ['get', '/<id>', 'read'],
43
        'save'   => ['post', '', 'save'],
44
        'update' => ['put', '/<id>', 'update'],
45
        'delete' => ['delete', '/<id>', 'delete'],
46
    ];
47
48
    /**
49
     * 配置参数
50
     * @var array
51
     */
52
    protected $config = [
53
        // pathinfo分隔符
54
        'pathinfo_depr'         => '/',
55
        // 是否开启路由延迟解析
56
        'url_lazy_route'        => false,
57
        // 是否强制使用路由
58
        'url_route_must'        => false,
59
        // 是否区分大小写
60
        'url_case_sensitive'    => false,
61
        // 合并路由规则
62
        'route_rule_merge'      => false,
63
        // 路由是否完全匹配
64
        'route_complete_match'  => false,
65
        // 去除斜杠
66
        'remove_slash'          => false,
67
        // 使用注解路由
68
        'route_annotation'      => false,
69
        // 默认的路由变量规则
70
        'default_route_pattern' => '[\w\.]+',
71
        // URL伪静态后缀
72
        'url_html_suffix'       => 'html',
73
        // 访问控制器层名称
74
        'controller_layer'      => 'controller',
75
        // 空控制器名
76
        'empty_controller'      => 'Error',
77
        // 是否使用控制器后缀
78
        'controller_suffix'     => false,
79
        // 是否使用多模块
80
        'use_multi_module'      => false,
81
        // 默认路由 [路由规则, 路由地址]
82
        'default_route'         => [],
83
        // 默认模块名
84
        'default_module'        => 'Index',
85
        // 默认控制器名
86
        'default_controller'    => 'Index',        
87
        // 默认操作名
88
        'default_action'        => 'index',
89
        // 操作方法后缀
90
        'action_suffix'         => '',
91
        // 非路由变量是否使用普通参数方式(用于URL生成)
92
        'url_common_param'      => true,
93
    ];
94
95
    /**
96
     * 请求对象
97
     * @var Request
98
     */
99
    protected $request;
100
101
    /**
102
     * @var RuleName
103
     */
104
    protected $ruleName;
105
106
    /**
107
     * 当前HOST
108
     * @var string
109
     */
110
    protected $host;
111
112
    /**
113
     * 当前分组对象
114
     * @var RuleGroup
115
     */
116
    protected $group;
117
118
    /**
119
     * 路由绑定
120
     * @var array
121
     */
122
    protected $bind = [];
123
124
    /**
125
     * 域名对象
126
     * @var Domain[]
127
     */
128
    protected $domains = [];
129
130
    /**
131
     * 跨域路由规则
132
     * @var RuleGroup
133
     */
134
    protected $cross;
135
136
    /**
137
     * 路由是否延迟解析
138
     * @var bool
139
     */
140
    protected $lazy = false;
141
142
    /**
143
     * (分组)路由规则是否合并解析
144
     * @var bool
145
     */
146
    protected $mergeRuleRegex = false;
147
148
    /**
149
     * 是否去除URL最后的斜线
150
     * @var bool
151
     */
152
    protected $removeSlash = false;
153
154 27
    public function __construct(protected App $app)
155
    {
156 27
        $this->ruleName = new RuleName();
157 27
        $this->setDefaultDomain();
158
159 27
        if (is_file($this->app->getRuntimePath() . 'route.php')) {
160
            // 读取路由映射文件
161
            $this->import(include $this->app->getRuntimePath() . 'route.php');
162
        }
163
164 27
        $this->config = array_merge($this->config, $this->app->config->get('route'));
0 ignored issues
show
Bug introduced by
It seems like $this->app->config->get('route') can also be of type null; however, parameter $arrays of array_merge() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

164
        $this->config = array_merge($this->config, /** @scrutinizer ignore-type */ $this->app->config->get('route'));
Loading history...
165
166 27
        $this->init();
167
    }
168
169 27
    protected function init()
170
    {
171 27
        if (!empty($this->config['middleware'])) {
172
            $this->app->middleware->import($this->config['middleware'], 'route');
173
        }
174
175 27
        $this->lazy($this->config['url_lazy_route']);
176 27
        $this->mergeRuleRegex = $this->config['route_rule_merge'];
177 27
        $this->removeSlash    = $this->config['remove_slash'];
178
179 27
        $this->group->removeSlash($this->removeSlash);
180
181
        // 注册全局MISS路由
182 27
        $this->miss(function () {
183 3
            return Response::create('', 'html', 204)->header(['Allow' => 'GET, POST, PUT, DELETE']);
184 27
        }, 'options');
185
    }
186
187 27
    public function config(?string $name = null)
188
    {
189 27
        if (is_null($name)) {
190
            return $this->config;
191
        }
192
193 27
        return $this->config[$name] ?? null;
194
    }
195
196
    /**
197
     * 设置路由域名及分组(包括资源路由)是否延迟解析
198
     * @access public
199
     * @param bool $lazy 路由是否延迟解析
200
     * @return $this
201
     */
202 27
    public function lazy(bool $lazy = true)
203
    {
204 27
        $this->lazy = $lazy;
205 27
        return $this;
206
    }
207
208
    /**
209
     * 设置路由域名及分组(包括资源路由)是否合并解析
210
     * @access public
211
     * @param bool $merge 路由是否合并解析
212
     * @return $this
213
     */
214
    public function mergeRuleRegex(bool $merge = true)
215
    {
216
        $this->mergeRuleRegex = $merge;
217
        $this->group->mergeRuleRegex($merge);
218
219
        return $this;
220
    }
221
222
    /**
223
     * 初始化默认域名
224
     * @access protected
225
     * @return void
226
     */
227 27
    protected function setDefaultDomain(): void
228
    {
229
        // 注册默认域名
230 27
        $domain = new Domain($this);
231
232 27
        $this->domains['-'] = $domain;
233
234
        // 默认分组
235 27
        $this->group = $domain;
236
    }
237
238
    /**
239
     * 设置当前分组
240
     * @access public
241
     * @param RuleGroup $group 域名
242
     * @return void
243
     */
244 27
    public function setGroup(RuleGroup $group): void
245
    {
246 27
        $this->group = $group;
247
    }
248
249
    /**
250
     * 获取指定标识的路由分组 不指定则获取当前分组
251
     * @access public
252
     * @param string $name 分组标识
253
     * @return RuleGroup
254
     */
255 27
    public function getGroup(?string $name = null)
256
    {
257 27
        return $name ? $this->ruleName->getGroup($name) : $this->group;
258
    }
259
260
    /**
261
     * 注册变量规则
262
     * @access public
263
     * @param array $pattern 变量规则
264
     * @return $this
265
     */
266
    public function pattern(array $pattern)
267
    {
268
        $this->group->pattern($pattern);
269
270
        return $this;
271
    }
272
273
    /**
274
     * 注册路由参数
275
     * @access public
276
     * @param array $option 参数
277
     * @return $this
278
     */
279
    public function option(array $option)
280
    {
281
        $this->group->option($option);
282
283
        return $this;
284
    }
285
286
    /**
287
     * 注册域名路由
288
     * @access public
289
     * @param string|array $name 子域名
290
     * @param mixed        $rule 路由规则
291
     * @return Domain
292
     */
293 3
    public function domain(string | array $name, $rule = null): Domain
294
    {
295
        // 支持多个域名使用相同路由规则
296 3
        $domainName = is_array($name) ? array_shift($name) : $name;
0 ignored issues
show
introduced by
The condition is_array($name) is always true.
Loading history...
297
298 3
        if (!isset($this->domains[$domainName])) {
299 3
            $domain = (new Domain($this, $domainName, $rule, $this->lazy))
300 3
                ->removeSlash($this->removeSlash)
301 3
                ->mergeRuleRegex($this->mergeRuleRegex);
302
303 3
            $this->domains[$domainName] = $domain;
304
        } else {
305
            $domain = $this->domains[$domainName];
306
            $domain->parseGroupRule($rule);
307
        }
308
309 3
        if (is_array($name) && !empty($name)) {
310
            foreach ($name as $item) {
311
                $this->domains[$item] = $domainName;
312
            }
313
        }
314
315
        // 返回域名对象
316 3
        return $domain;
317
    }
318
319
    /**
320
     * 获取域名
321
     * @access public
322
     * @return array
323
     */
324
    public function getDomains(): array
325
    {
326
        return $this->domains;
327
    }
328
329
    /**
330
     * 获取RuleName对象
331
     * @access public
332
     * @return RuleName
333
     */
334
    public function getRuleName(): RuleName
335
    {
336
        return $this->ruleName;
337
    }
338
339
    /**
340
     * 设置路由绑定
341
     * @access public
342
     * @param string $bind   绑定信息
343
     * @param string $domain 域名
344
     * @return $this
345
     */
346
    public function bind(string $bind, ?string $domain = null)
347
    {
348
        $domain = is_null($domain) ? '-' : $domain;
349
350
        $this->bind[$domain] = $bind;
351
352
        return $this;
353
    }
354
355
    /**
356
     * 读取路由绑定信息
357
     * @access public
358
     * @return array
359
     */
360
    public function getBind(): array
361
    {
362
        return $this->bind;
363
    }
364
365
    /**
366
     * 读取路由绑定
367
     * @access public
368
     * @param string $domain 域名
369
     * @return string|null
370
     */
371 27
    public function getDomainBind(?string $domain = null)
372
    {
373 27
        if (is_null($domain)) {
374 24
            $domain = $this->host;
375 3
        } elseif (!str_contains($domain, '.') && $this->request) {
376 3
            $domain .= '.' . $this->request->rootDomain();
377
        }
378
379 27
        if ($this->request) {
380 27
            $subDomain = $this->request->subDomain();
381
382 27
            if (str_contains($subDomain, '.')) {
383
                $name = '*' . strstr($subDomain, '.');
384
            }
385
        }
386
387 27
        if (isset($this->bind[$domain])) {
388
            $result = $this->bind[$domain];
389 27
        } elseif (isset($name) && isset($this->bind[$name])) {
390
            $result = $this->bind[$name];
391 27
        } elseif (!empty($subDomain) && isset($this->bind['*'])) {
392
            $result = $this->bind['*'];
393
        } else {
394 27
            $result = null;
395
        }
396
397 27
        return $result;
398
    }
399
400
    /**
401
     * 读取路由标识
402
     * @access public
403
     * @param string $name   路由标识
404
     * @param string $domain 域名
405
     * @param string $method 请求类型
406
     * @return array
407
     */
408
    public function getName(?string $name = null, ?string $domain = null, string $method = '*'): array
409
    {
410
        return $this->ruleName->getName($name, $domain, $method);
411
    }
412
413
    /**
414
     * 批量导入路由标识
415
     * @access public
416
     * @param array $name 路由标识
417
     * @return void
418
     */
419
    public function import(array $name): void
420
    {
421
        $this->ruleName->import($name);
422
    }
423
424
    /**
425
     * 注册路由标识
426
     * @access public
427
     * @param string   $name     路由标识
428
     * @param RuleItem $ruleItem 路由规则
429
     * @param bool     $first    是否优先
430
     * @return void
431
     */
432 12
    public function setName(string $name, RuleItem $ruleItem, bool $first = false): void
433
    {
434 12
        $this->ruleName->setName($name, $ruleItem, $first);
435
    }
436
437
    /**
438
     * 保存路由规则
439
     * @access public
440
     * @param string   $rule     路由规则
441
     * @param RuleItem $ruleItem RuleItem对象
442
     * @return void
443
     */
444 27
    public function setRule(string $rule, ?RuleItem $ruleItem = null): void
445
    {
446 27
        $this->ruleName->setRule($rule, $ruleItem);
0 ignored issues
show
Bug introduced by
It seems like $ruleItem can also be of type null; however, parameter $ruleItem of think\route\RuleName::setRule() does only seem to accept think\route\RuleItem, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

446
        $this->ruleName->setRule($rule, /** @scrutinizer ignore-type */ $ruleItem);
Loading history...
447
    }
448
449
    /**
450
     * 读取路由
451
     * @access public
452
     * @param string $rule 路由规则
453
     * @return RuleItem[]
454
     */
455
    public function getRule(string $rule): array
456
    {
457
        return $this->ruleName->getRule($rule);
458
    }
459
460
    /**
461
     * 读取路由列表
462
     * @access public
463
     * @return array
464
     */
465
    public function getRuleList(): array
466
    {
467
        return $this->ruleName->getRuleList();
468
    }
469
470
    /**
471
     * 清空路由规则
472
     * @access public
473
     * @return void
474
     */
475
    public function clear(): void
476
    {
477
        $this->ruleName->clear();
478
479
        if ($this->group) {
480
            $this->group->clear();
481
        }
482
    }
483
484
    /**
485
     * 注册路由规则
486
     * @access public
487
     * @param string $rule   路由规则
488
     * @param mixed  $route  路由地址
489
     * @param string $method 请求类型
490
     * @return RuleItem
491
     */
492 24
    public function rule(string $rule, $route = null, string $method = '*'): RuleItem
493
    {
494 24
        return $this->group->addRule($rule, $route, $method);
495
    }
496
497
    /**
498
     * 设置路由规则全局有效
499
     * @access public
500
     * @param Rule   $rule   路由规则
501
     * @return $this
502
     */
503
    public function setCrossDomainRule(Rule $rule)
504
    {
505
        if (!isset($this->cross)) {
506
            $this->cross = (new RuleGroup($this))->mergeRuleRegex($this->mergeRuleRegex);
507
        }
508
509
        $this->cross->addRuleItem($rule);
510
511
        return $this;
512
    }
513
514
    /**
515
     * 注册路由分组
516
     * @access public
517
     * @param string|Closure $name  分组名称或者参数
518
     * @param mixed           $route 分组路由
519
     * @return RuleGroup
520
     */
521 3
    public function group(string | Closure $name, $route = null): RuleGroup
522
    {
523 3
        if ($name instanceof Closure) {
524 3
            $route = $name;
525 3
            $name  = '';
526
        }
527
528 3
        return (new RuleGroup($this, $this->group, $name, $route, $this->lazy))
529 3
            ->removeSlash($this->removeSlash)
530 3
            ->mergeRuleRegex($this->mergeRuleRegex);
531
    }
532
533
    /**
534
     * 注册路由
535
     * @access public
536
     * @param string $rule  路由规则
537
     * @param mixed  $route 路由地址
538
     * @return RuleItem
539
     */
540
    public function any(string $rule, $route): RuleItem
541
    {
542
        return $this->rule($rule, $route, '*');
543
    }
544
545
    /**
546
     * 注册GET路由
547
     * @access public
548
     * @param string $rule  路由规则
549
     * @param mixed  $route 路由地址
550
     * @return RuleItem
551
     */
552 18
    public function get(string $rule, $route): RuleItem
553
    {
554 18
        return $this->rule($rule, $route, 'GET');
555
    }
556
557
    /**
558
     * 注册POST路由
559
     * @access public
560
     * @param string $rule  路由规则
561
     * @param mixed  $route 路由地址
562
     * @return RuleItem
563
     */
564 3
    public function post(string $rule, $route): RuleItem
565
    {
566 3
        return $this->rule($rule, $route, 'POST');
567
    }
568
569
    /**
570
     * 注册PUT路由
571
     * @access public
572
     * @param string $rule  路由规则
573
     * @param mixed  $route 路由地址
574
     * @return RuleItem
575
     */
576 3
    public function put(string $rule, $route): RuleItem
577
    {
578 3
        return $this->rule($rule, $route, 'PUT');
579
    }
580
581
    /**
582
     * 注册DELETE路由
583
     * @access public
584
     * @param string $rule  路由规则
585
     * @param mixed  $route 路由地址
586
     * @return RuleItem
587
     */
588
    public function delete(string $rule, $route): RuleItem
589
    {
590
        return $this->rule($rule, $route, 'DELETE');
591
    }
592
593
    /**
594
     * 注册PATCH路由
595
     * @access public
596
     * @param string $rule  路由规则
597
     * @param mixed  $route 路由地址
598
     * @return RuleItem
599
     */
600
    public function patch(string $rule, $route): RuleItem
601
    {
602
        return $this->rule($rule, $route, 'PATCH');
603
    }
604
605
    /**
606
     * 注册HEAD路由
607
     * @access public
608
     * @param string $rule  路由规则
609
     * @param mixed  $route 路由地址
610
     * @return RuleItem
611
     */
612
    public function head(string $rule, $route): RuleItem
613
    {
614
        return $this->rule($rule, $route, 'HEAD');
615
    }
616
617
    /**
618
     * 注册OPTIONS路由
619
     * @access public
620
     * @param string $rule  路由规则
621
     * @param mixed  $route 路由地址
622
     * @return RuleItem
623
     */
624
    public function options(string $rule, $route): RuleItem
625
    {
626
        return $this->rule($rule, $route, 'OPTIONS');
627
    }
628
629
    /**
630
     * 注册资源路由
631
     * @access public
632
     * @param string $rule  路由规则
633
     * @param string $route 路由地址
634
     * @return Resource|ResourceRegister
635
     */
636
    public function resource(string $rule, string $route)
637
    {
638
        $resource = new Resource($this, $this->group, $rule, $route, $this->rest);
639
640
        if (!$this->lazy) {
641
            return new ResourceRegister($resource);
642
        }
643
644
        return $resource;
645
    }
646
647
    /**
648
     * 注册视图路由
649
     * @access public
650
     * @param string $rule     路由规则
651
     * @param string $template 路由模板地址
652
     * @param array  $vars     模板变量
653
     * @return RuleItem
654
     */
655 3
    public function view(string $rule, string $template = '', array $vars = []): RuleItem
656
    {
657 3
        return $this->rule($rule, function () use ($vars, $template) {
658 3
            return Response::create($template, 'view')->assign($vars);
0 ignored issues
show
Bug introduced by
The method assign() does not exist on think\Response. It seems like you code against a sub-type of think\Response such as think\response\View. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

658
            return Response::create($template, 'view')->/** @scrutinizer ignore-call */ assign($vars);
Loading history...
659 3
        }, 'GET');
660
    }
661
662
    /**
663
     * 注册重定向路由
664
     * @access public
665
     * @param string $rule   路由规则
666
     * @param string $route  路由地址
667
     * @param int    $status 状态码
668
     * @return RuleItem
669
     */
670 3
    public function redirect(string $rule, string $route = '', int $status = 301): RuleItem
671
    {
672 3
        return $this->rule($rule, function (Request $request) use ($status, $route) {
673 3
            $search  = $replace  = [];
674 3
            $matches = $request->rule()->getVars();
675
676 3
            foreach ($matches as $key => $value) {
677
                $search[]  = '<' . $key . '>';
678
                $replace[] = $value;
679
680
                $search[]  = ':' . $key;
681
                $replace[] = $value;
682
            }
683
684 3
            $route = str_replace($search, $replace, $route);
685 3
            return Response::create($route, 'redirect')->code($status);
686 3
        }, '*');
687
    }
688
689
    /**
690
     * rest方法定义和修改
691
     * @access public
692
     * @param string|array $name     方法名称
693
     * @param array|bool   $resource 资源
694
     * @return $this
695
     */
696
    public function rest(string | array $name, array | bool $resource = [])
697
    {
698
        if (is_array($name)) {
0 ignored issues
show
introduced by
The condition is_array($name) is always true.
Loading history...
699
            $this->rest = $resource ? $name : array_merge($this->rest, $name);
700
        } else {
701
            $this->rest[$name] = $resource;
702
        }
703
704
        return $this;
705
    }
706
707
    /**
708
     * 获取rest方法定义的参数
709
     * @access public
710
     * @param string $name 方法名称
711
     * @return array|null
712
     */
713
    public function getRest(?string $name = null)
714
    {
715
        if (is_null($name)) {
716
            return $this->rest;
717
        }
718
719
        return $this->rest[$name] ?? null;
720
    }
721
722
    /**
723
     * 注册未匹配路由规则后的处理
724
     * @access public
725
     * @param string|Closure $route  路由地址
726
     * @param string         $method 请求类型
727
     * @return RuleItem
728
     */
729 27
    public function miss(string | Closure $route, string $method = '*'): RuleItem
730
    {
731 27
        return $this->group->miss($route, $method);
732
    }
733
734
    /**
735
     * 路由调度
736
     * @param Request $request
737
     * @param Closure|bool $withRoute
738
     * @return Response
739
     */
740 27
    public function dispatch(Request $request, Closure | bool $withRoute = true)
741
    {
742 27
        $this->request = $request;
743 27
        $this->host    = $this->request->host(true);
744 27
        $completeMatch = (bool) $this->config['route_complete_match'];
745
746 27
        if ($withRoute) {
747
            //加载路由
748 27
            if ($withRoute instanceof Closure) {
749
                $withRoute();
750
            }
751 27
            $dispatch = $this->check($completeMatch);
752
        } else {
753
            $dispatch = $this->url($this->path(), $completeMatch);
754
        }
755
756 27
        $dispatch->init($this->app);
757
758 27
        return $this->app->middleware->pipeline('route')
759 27
            ->send($request)
760 27
            ->then(function () use ($dispatch) {
761 27
                return $dispatch->run();
762 27
            });
763
    }
764
765
    /**
766
     * 检测URL路由
767
     * @access public
768
     * @param  bool $completeMatch
769
     * @return Dispatch|false
770
     * @throws RouteNotFoundException
771
     */
772 27
    public function check(bool $completeMatch = false)
773
    {
774
        // 自动检测域名路由
775 27
        $url = str_replace($this->config['pathinfo_depr'], '|', $this->path());
776
777 27
        $result = $this->checkDomain()->check($this->request, $url, $completeMatch);
778
779 27
        if (false === $result && !empty($this->cross)) {
780
            // 检测跨域路由
781
            $result = $this->cross->check($this->request, $url, $completeMatch);
782
        }
783
784 27
        if (false !== $result) {
785 24
            return $result;
786 3
        } elseif ($this->config['url_route_must']) {
787
            throw new RouteNotFoundException();
788
        }
789 3
        return $this->url($url, $completeMatch);
790
    }
791
792
    /**
793
     * 获取当前请求URL的pathinfo信息(不含URL后缀)
794
     * @access protected
795
     * @return string
796
     */
797 27
    protected function path(): string
798
    {
799 27
        $suffix   = $this->config['url_html_suffix'];
800 27
        $pathinfo = $this->request->pathinfo();
801
802 27
        if (false === $suffix) {
803
            // 禁止伪静态访问
804
            $path = $pathinfo;
805 27
        } elseif ($suffix) {
806
            // 去除正常的URL后缀
807 27
            $path = preg_replace('/\.(' . ltrim($suffix, '.') . ')$/i', '', $pathinfo);
808
        } else {
809
            // 允许任何后缀访问
810
            $path = preg_replace('/\.' . $this->request->ext() . '$/i', '', $pathinfo);
811
        }
812
813 27
        return $path;
814
    }
815
816
    /**
817
     * 默认URL解析
818
     * @access public
819
     * @param string $url URL地址
820
     * @param  bool $completeMatch
821
     * @return Dispatch
822
     */
823 3
    public function url(string $url, bool $completeMatch): Dispatch
824
    {
825 3
        if ($this->request->method() == 'OPTIONS') {
826
            // 自动响应options请求
827
            return new Callback($this->request, $this->group, function () {
828
                return Response::create('', 'html', 204)->header(['Allow' => 'GET, POST, PUT, DELETE']);
829
            });
830
        }
831
832 3
        if (!empty($this->config['default_route'])) {
833
            [$rule, $route] = $this->config['default_route'];
834 3
        } elseif (!empty($this->config['use_multi_module'])) {
835
            $rule  = '[:module]/[:controller]/[:action]';
836
            $route = ':module.:controller/:action';
837
        } else {
838 3
            $rule  = '[:controller]/[:action]';
839 3
            $route = ':controller/:action';
840
        }
841
842 3
        $ruleItem = new RuleItem($this, $this->group, '_default_route_', $rule, $route, '*');
843
844 3
        return $ruleItem->default([
845 3
            'module'     => $this->config['default_module'],
846 3
            'controller' => $this->config['default_controller'],
847 3
            'action'     => $this->config['default_action'],
848 3
        ])->pattern([
849 3
            'module'     =>  '[A-Za-z0-9\.\_]+',
850 3
            'controller' =>  '[A-Za-z0-9\.\_]+',
851 3
            'action'     =>  '[A-Za-z0-9\_]+',
852 3
        ])->check($this->request, $url, $completeMatch);
853
    }
854
855
    /**
856
     * 检测域名的路由规则
857
     * @access protected
858
     * @return Domain
859
     */
860 27
    protected function checkDomain(): Domain
861
    {
862 27
        $item = false;
863
864 27
        if (count($this->domains) > 1) {
865
            // 获取当前子域名
866 3
            $subDomain = $this->request->subDomain();
867
868 3
            $domain  = $subDomain ? explode('.', $subDomain) : [];
869 3
            $domain2 = $domain ? array_pop($domain) : '';
870
871 3
            if ($domain) {
872
                // 存在三级域名
873
                $domain3 = array_pop($domain);
874
            }
875
876 3
            if (isset($this->domains[$this->host])) {
877
                // 子域名配置
878
                $item = $this->domains[$this->host];
879 3
            } elseif (isset($this->domains[$subDomain])) {
880 3
                $item = $this->domains[$subDomain];
881
            } elseif (isset($this->domains['*.' . $domain2]) && !empty($domain3)) {
882
                // 泛三级域名
883
                $item      = $this->domains['*.' . $domain2];
884
                $panDomain = $domain3;
885
            } elseif (isset($this->domains['*']) && !empty($domain2)) {
886
                // 泛二级域名
887
                if ('www' != $domain2) {
888
                    $item      = $this->domains['*'];
889
                    $panDomain = $domain2;
890
                }
891
            }
892
893 3
            if (isset($panDomain)) {
894
                // 保存当前泛域名
895
                $this->request->setPanDomain($panDomain);
896
            }
897
        }
898
899 27
        if (false === $item) {
900
            // 检测全局域名规则
901 24
            $item = $this->domains['-'];
902
        }
903
904 27
        if (is_string($item)) {
905
            $item = $this->domains[$item];
906
        }
907
908 27
        return $item;
909
    }
910
911
    /**
912
     * URL生成 支持路由反射
913
     * @access public
914
     * @param string $url  路由地址
915
     * @param array  $vars 参数 ['a'=>'val1', 'b'=>'val2']
916
     * @return UrlBuild
917
     */
918
    public function buildUrl(string $url = '', array $vars = []): UrlBuild
919
    {
920
        return $this->app->make(UrlBuild::class, [$this, $this->app, $url, $vars], true);
921
    }
922
923
    /**
924
     * 设置全局的路由分组参数
925
     * @access public
926
     * @param string $method 方法名
927
     * @param array  $args   调用参数
928
     * @return RuleGroup
929
     */
930
    public function __call($method, $args)
931
    {
932
        return call_user_func_array([$this->group, $method], $args);
933
    }
934
}
935