Passed
Push — 8.0 ( f6550d...d13e09 )
by liu
02:43
created

Route::domain()   A

Complexity

Conditions 6
Paths 8

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 7.0487

Importance

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

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

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

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