Passed
Push — 8.0 ( 1d487f...814364 )
by liu
02:38
created

Route::setGroup()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
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\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
use think\route\UrlRuleItem;
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
        'default_route'         =>  [],
81
        // 默认控制器名
82
        'default_controller'    => 'Index',
83
        // 默认操作名
84
        'default_action'        => 'index',
85
        // 操作方法后缀
86
        'action_suffix'         => '',
87
        // 非路由变量是否使用普通参数方式(用于URL生成)
88
        'url_common_param'      => true,
89
    ];
90
91
    /**
92
     * 请求对象
93
     * @var Request
94
     */
95
    protected $request;
96
97
    /**
98
     * @var RuleName
99
     */
100
    protected $ruleName;
101
102
    /**
103
     * 当前HOST
104
     * @var string
105
     */
106
    protected $host;
107
108
    /**
109
     * 当前分组对象
110
     * @var RuleGroup
111
     */
112
    protected $group;
113
114
    /**
115
     * 路由绑定
116
     * @var array
117
     */
118
    protected $bind = [];
119
120
    /**
121
     * 域名对象
122
     * @var Domain[]
123
     */
124
    protected $domains = [];
125
126
    /**
127
     * 跨域路由规则
128
     * @var RuleGroup
129
     */
130
    protected $cross;
131
132
    /**
133
     * 路由是否延迟解析
134
     * @var bool
135
     */
136
    protected $lazy = false;
137
138
    /**
139
     * (分组)路由规则是否合并解析
140
     * @var bool
141
     */
142
    protected $mergeRuleRegex = false;
143
144
    /**
145
     * 是否去除URL最后的斜线
146
     * @var bool
147
     */
148
    protected $removeSlash = false;
149
150 27
    public function __construct(protected App $app)
151
    {
152 27
        $this->ruleName = new RuleName();
153 27
        $this->setDefaultDomain();
154
155 27
        if (is_file($this->app->getRuntimePath() . 'route.php')) {
156
            // 读取路由映射文件
157
            $this->import(include $this->app->getRuntimePath() . 'route.php');
158
        }
159
160 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

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

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

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