Passed
Push — 6.0 ( 8df141...6a6d62 )
by liu
02:46
created

Route::parseDomain()   D

Complexity

Conditions 21
Paths 49

Size

Total Lines 49
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 21
eloc 29
nc 49
nop 2
dl 0
loc 49
rs 4.1666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
// +----------------------------------------------------------------------
1 ignored issue
show
Coding Style introduced by
You must use "/**" style comments for a file comment
Loading history...
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2019 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\cache\Driver;
17
use think\exception\HttpResponseException;
18
use think\exception\RouteNotFoundException;
19
use think\route\Dispatch;
20
use think\route\dispatch\Url as UrlDispatch;
21
use think\route\Domain;
22
use think\route\Resource;
23
use think\route\Rule;
24
use think\route\RuleGroup;
25
use think\route\RuleItem;
26
use think\route\RuleName;
27
28
class Route
1 ignored issue
show
Coding Style introduced by
Missing class doc comment
Loading history...
29
{
30
    /**
31
     * REST定义
32
     * @var array
33
     */
34
    protected $rest = [
35
        'index'  => ['get', '', 'index'],
36
        'create' => ['get', '/create', 'create'],
37
        'edit'   => ['get', '/<id>/edit', 'edit'],
38
        'read'   => ['get', '/<id>', 'read'],
39
        'save'   => ['post', '', 'save'],
40
        'update' => ['put', '/<id>', 'update'],
41
        'delete' => ['delete', '/<id>', 'delete'],
42
    ];
43
44
    /**
45
     * 配置参数
46
     * @var array
47
     */
48
    protected $config = [
49
        // pathinfo分隔符
50
        'pathinfo_depr'         => '/',
51
        // 是否开启路由延迟解析
52
        'url_lazy_route'        => false,
53
        // 是否强制使用路由
54
        'url_route_must'        => false,
55
        // 合并路由规则
56
        'route_rule_merge'      => false,
57
        // 路由是否完全匹配
58
        'route_complete_match'  => false,
59
        // 使用注解路由
60
        'route_annotation'      => false,
61
        // 路由缓存设置
62
        'route_check_cache'     => false,
63
        'route_cache_option'    => [],
64
        'route_check_cache_key' => '',
65
        // 是否自动转换URL中的控制器和操作名
66
        'url_convert'           => true,
67
        // 默认的路由变量规则
68
        'default_route_pattern' => '[\w\.]+',
69
        // URL伪静态后缀
70
        'url_html_suffix'       => 'html',
71
        // 访问控制器层名称
72
        'controller_layer'      => 'controller',
73
        // 空控制器名
74
        'empty_controller'      => 'Error',
75
        // 是否使用控制器后缀
76
        'controller_suffix'     => false,
77
        // 默认控制器名
78
        'default_controller'    => 'Index',
79
        // 默认操作名
80
        'default_action'        => 'index',
81
        // 操作方法后缀
82
        'action_suffix'         => '',
83
        // 是否开启路由检测缓存
84
        'route_check_cache'     => false,
85
        // 非路由变量是否使用普通参数方式(用于URL生成)
86
        'url_common_param'      => false,
87
    ];
88
89
    /**
90
     * 当前应用
91
     * @var App
92
     */
93
    protected $app;
94
95
    /**
96
     * 请求对象
97
     * @var Request
98
     */
99
    protected $request;
100
101
    /**
102
     * 缓存
103
     * @var Driver
104
     */
105
    protected $cache;
106
107
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
108
     * @var RuleName
109
     */
110
    protected $ruleName;
111
112
    /**
113
     * 当前HOST
114
     * @var string
115
     */
116
    protected $host;
117
118
    /**
119
     * 当前域名
120
     * @var string
121
     */
122
    protected $domain;
123
124
    /**
125
     * 当前分组对象
126
     * @var RuleGroup
127
     */
128
    protected $group;
129
130
    /**
131
     * 路由绑定
132
     * @var array
133
     */
134
    protected $bind = [];
135
136
    /**
137
     * 域名对象
138
     * @var array
139
     */
140
    protected $domains = [];
141
142
    /**
143
     * 跨域路由规则
144
     * @var RuleGroup
145
     */
146
    protected $cross;
147
148
    /**
149
     * 路由是否延迟解析
150
     * @var bool
151
     */
152
    protected $lazy = true;
153
154
    /**
155
     * 路由是否测试模式
156
     * @var bool
157
     */
158
    protected $isTest = false;
159
160
    /**
161
     * (分组)路由规则是否合并解析
162
     * @var bool
163
     */
164
    protected $mergeRuleRegex = true;
165
166
    /**
167
     * 显示域名
168
     * @var bool
169
     */
170
    protected $showDomain;
171
172
    public function __construct(App $app, Cache $cache)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
173
    {
174
        $this->app    = $app;
175
        $this->config = array_merge($this->config, $app->config->get('route'));
176
177
        if (!empty($this->config['route_cache_option'])) {
178
            $this->cache = $cache->connect($this->config['route_cache_option']);
179
        } else {
180
            $this->cache = $cache->init();
181
        }
182
183
        if (is_file($app->getRuntimePath() . 'route.php')) {
184
            // 读取路由映射文件
185
            $this->import(include $app->getRuntimePath() . 'route.php');
186
        }
187
188
        $this->ruleName = new RuleName();
189
    }
190
191
    public function config(string $name = null)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
192
    {
193
        if (is_null($name)) {
194
            return $this->config;
195
        }
196
197
        return $this->config[$name] ?? null;
198
    }
199
200
    /**
201
     * 设置路由域名及分组(包括资源路由)是否延迟解析
202
     * @access public
203
     * @param bool $lazy 路由是否延迟解析
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
204
     * @return $this
205
     */
206
    public function lazy(bool $lazy = true)
207
    {
208
        $this->lazy = $lazy;
209
        return $this;
210
    }
211
212
    /**
213
     * 设置路由为测试模式
214
     * @access public
215
     * @param bool $test 路由是否测试模式
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
216
     * @return void
217
     */
218
    public function setTestMode(bool $test): void
219
    {
220
        $this->isTest = $test;
221
    }
222
223
    /**
224
     * 检查路由是否为测试模式
225
     * @access public
226
     * @return bool
227
     */
228
    public function isTest(): bool
229
    {
230
        return $this->isTest;
231
    }
232
233
    /**
234
     * 设置路由域名及分组(包括资源路由)是否合并解析
235
     * @access public
236
     * @param bool $merge 路由是否合并解析
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
237
     * @return $this
238
     */
239
    public function mergeRuleRegex(bool $merge = true)
240
    {
241
        $this->mergeRuleRegex = $merge;
242
        $this->group->mergeRuleRegex($merge);
243
244
        return $this;
245
    }
246
247
    /**
248
     * 初始化默认域名
249
     * @access protected
250
     * @return void
251
     */
252
    protected function setDefaultDomain(): void
253
    {
254
        // 默认域名
255
        $this->domain = $this->host;
256
257
        // 注册默认域名
258
        $domain = new Domain($this, $this->host);
259
260
        $this->domains[$this->host] = $domain;
261
262
        // 默认分组
263
        $this->group = $domain;
264
    }
265
266
    /**
267
     * 设置当前域名
268
     * @access public
269
     * @param RuleGroup $group 域名
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
270
     * @return void
271
     */
272
    public function setGroup(RuleGroup $group): void
273
    {
274
        $this->group = $group;
275
    }
276
277
    /**
278
     * 获取当前分组
279
     * @access public
280
     * @return RuleGroup
281
     */
282
    public function getGroup(): RuleGroup
283
    {
284
        return $this->group;
285
    }
286
287
    /**
288
     * 注册变量规则
289
     * @access public
290
     * @param array $pattern 变量规则
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
291
     * @return $this
292
     */
293
    public function pattern(array $pattern)
294
    {
295
        $this->group->pattern($pattern);
296
297
        return $this;
298
    }
299
300
    /**
301
     * 注册路由参数
302
     * @access public
303
     * @param array $option 参数
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
304
     * @return $this
305
     */
306
    public function option(array $option)
307
    {
308
        $this->group->option($option);
309
310
        return $this;
311
    }
312
313
    /**
314
     * 注册域名路由
315
     * @access public
316
     * @param string|array $name 子域名
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
317
     * @param mixed        $rule 路由规则
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
318
     * @return Domain
319
     */
320
    public function domain($name, $rule = null): Domain
321
    {
322
        // 支持多个域名使用相同路由规则
323
        $domainName = is_array($name) ? array_shift($name) : $name;
324
325
        if ('*' != $domainName && false === strpos($domainName, '.')) {
326
            $domainName .= '.' . $this->request->rootDomain();
327
        }
328
329
        if (!isset($this->domains[$domainName])) {
330
            $domain = (new Domain($this, $domainName, $rule))
331
                ->lazy($this->lazy)
332
                ->mergeRuleRegex($this->mergeRuleRegex);
333
334
            $this->domains[$domainName] = $domain;
335
        } else {
336
            $domain = $this->domains[$domainName];
337
            $domain->parseGroupRule($rule);
338
        }
339
340
        if (is_array($name) && !empty($name)) {
341
            $root = $this->request->rootDomain();
342
            foreach ($name as $item) {
343
                if (false === strpos($item, '.')) {
344
                    $item .= '.' . $root;
345
                }
346
347
                $this->domains[$item] = $domainName;
348
            }
349
        }
350
351
        // 返回域名对象
352
        return $domain;
353
    }
354
355
    /**
356
     * 获取域名
357
     * @access public
358
     * @return array
359
     */
360
    public function getDomains(): array
361
    {
362
        return $this->domains;
363
    }
364
365
    /**
366
     * 获取域名
367
     * @access public
368
     * @return array
369
     */
370
    public function getRuleName(): RuleName
371
    {
372
        return $this->ruleName;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->ruleName returns the type think\route\RuleName which is incompatible with the documented return type array.
Loading history...
373
    }
374
375
    /**
376
     * 设置路由绑定
377
     * @access public
378
     * @param string $bind   绑定信息
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
379
     * @param string $domain 域名
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
380
     * @return $this
381
     */
382
    public function bind(string $bind, string $domain = null)
383
    {
384
        $domain = is_null($domain) ? $this->domain : $domain;
385
386
        $this->bind[$domain] = $bind;
387
388
        return $this;
389
    }
390
391
    /**
392
     * 读取路由绑定信息
393
     * @access public
394
     * @return array
395
     */
396
    public function getBind(): array
397
    {
398
        return $this->bind;
399
    }
400
401
    /**
402
     * 读取路由绑定
403
     * @access public
404
     * @param string $domain 域名
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
405
     * @return string|null
406
     */
407
    public function getDomainBind(string $domain = null)
408
    {
409
        if (is_null($domain)) {
410
            $domain = $this->domain;
411
        } elseif (false === strpos($domain, '.')) {
412
            $domain .= '.' . $this->request->rootDomain();
413
        }
414
415
        $subDomain = $this->request->subDomain();
416
417
        if (strpos($subDomain, '.')) {
418
            $name = '*' . strstr($subDomain, '.');
419
        }
420
421
        if (isset($this->bind[$domain])) {
422
            $result = $this->bind[$domain];
423
        } elseif (isset($name) && isset($this->bind[$name])) {
424
            $result = $this->bind[$name];
425
        } elseif (!empty($subDomain) && isset($this->bind['*'])) {
426
            $result = $this->bind['*'];
427
        } else {
428
            $result = null;
429
        }
430
431
        return $result;
432
    }
433
434
    /**
435
     * 读取路由标识
436
     * @access public
437
     * @param string $name   路由标识
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
438
     * @param string $domain 域名
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
439
     * @param string $method 请求类型
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
440
     * @return RuleItem[]
441
     */
442
    public function getName(string $name = null, string $domain = null, string $method = '*'): array
443
    {
444
        return $this->ruleName->getName($name, $domain, $method);
445
    }
446
447
    /**
448
     * 批量导入路由标识
449
     * @access public
450
     * @param array $name 路由标识
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
451
     * @return $this
452
     */
453
    public function import(array $name)
454
    {
455
        $this->ruleName->import($name);
456
        return $this;
457
    }
458
459
    /**
460
     * 注册路由标识
461
     * @access public
462
     * @param string       $name  路由标识
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
463
     * @param string|array $value 路由规则
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
464
     * @param bool         $first 是否置顶
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
465
     * @return Route
466
     */
467
    public function setName(string $name, $value, bool $first = false): Route
468
    {
469
        $this->ruleName->setName($name, $value, $first);
470
        return $this;
471
    }
472
473
    /**
474
     * 读取路由
475
     * @access public
476
     * @param string $rule   路由规则
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
477
     * @param string $domain 域名
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
478
     * @return RuleItem[]
479
     */
480
    public function getRule(string $rule, string $domain = null): array
481
    {
482
        if (is_null($domain)) {
483
            $domain = $this->domain;
484
        }
485
486
        return $this->ruleName->getRule($rule, $domain);
487
    }
488
489
    /**
490
     * 读取路由列表
491
     * @access public
492
     * @param string $domain 域名
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
493
     * @return array
494
     */
495
    public function getRuleList(string $domain = null): array
496
    {
497
        return $this->ruleName->getRuleList($domain);
498
    }
499
500
    /**
501
     * 清空路由规则
502
     * @access public
503
     * @return void
504
     */
505
    public function clear(): void
506
    {
507
        $this->ruleName->clear();
508
509
        if ($this->group) {
510
            $this->group->clear();
511
        }
512
    }
513
514
    /**
515
     * 注册路由规则
516
     * @access public
517
     * @param string $rule   路由规则
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
518
     * @param mixed  $route  路由地址
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
519
     * @param string $method 请求类型
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
520
     * @return RuleItem
521
     */
522
    public function rule(string $rule, $route = null, string $method = '*'): RuleItem
523
    {
524
        return $this->group->addRule($rule, $route, $method);
525
    }
526
527
    /**
528
     * 设置跨域有效路由规则
529
     * @access public
530
     * @param Rule   $rule   路由规则
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
531
     * @param string $method 请求类型
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
532
     * @return $this
533
     */
534
    public function setCrossDomainRule(Rule $rule, string $method = '*')
535
    {
536
        if (!isset($this->cross)) {
537
            $this->cross = (new RuleGroup($this))->mergeRuleRegex($this->mergeRuleRegex);
538
        }
539
540
        $this->cross->addRuleItem($rule, $method);
541
542
        return $this;
543
    }
544
545
    /**
546
     * 注册路由分组
547
     * @access public
548
     * @param string|\Closure $name  分组名称或者参数
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
549
     * @param mixed           $route 分组路由
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
550
     * @return RuleGroup
551
     */
552
    public function group($name, $route = null): RuleGroup
553
    {
554
        if ($name instanceof \Closure) {
555
            $route = $name;
556
            $name  = '';
557
        }
558
559
        return (new RuleGroup($this, $this->group, $name, $route))
560
            ->lazy($this->lazy)
561
            ->mergeRuleRegex($this->mergeRuleRegex);
562
    }
563
564
    /**
565
     * 注册路由
566
     * @access public
567
     * @param string $rule  路由规则
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
568
     * @param mixed  $route 路由地址
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
569
     * @return RuleItem
570
     */
571
    public function any(string $rule, $route): RuleItem
572
    {
573
        return $this->rule($rule, $route, '*');
574
    }
575
576
    /**
577
     * 注册GET路由
578
     * @access public
579
     * @param string $rule  路由规则
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
580
     * @param mixed  $route 路由地址
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
581
     * @return RuleItem
582
     */
583
    public function get(string $rule, $route): RuleItem
584
    {
585
        return $this->rule($rule, $route, 'GET');
586
    }
587
588
    /**
589
     * 注册POST路由
590
     * @access public
591
     * @param string $rule  路由规则
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
592
     * @param mixed  $route 路由地址
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
593
     * @return RuleItem
594
     */
595
    public function post(string $rule, $route): RuleItem
596
    {
597
        return $this->rule($rule, $route, 'POST');
598
    }
599
600
    /**
601
     * 注册PUT路由
602
     * @access public
603
     * @param string $rule  路由规则
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
604
     * @param mixed  $route 路由地址
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
605
     * @return RuleItem
606
     */
607
    public function put(string $rule, $route): RuleItem
608
    {
609
        return $this->rule($rule, $route, 'PUT');
610
    }
611
612
    /**
613
     * 注册DELETE路由
614
     * @access public
615
     * @param string $rule  路由规则
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
616
     * @param mixed  $route 路由地址
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
617
     * @return RuleItem
618
     */
619
    public function delete(string $rule, $route): RuleItem
620
    {
621
        return $this->rule($rule, $route, 'DELETE');
622
    }
623
624
    /**
625
     * 注册PATCH路由
626
     * @access public
627
     * @param string $rule  路由规则
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
628
     * @param mixed  $route 路由地址
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
629
     * @return RuleItem
630
     */
631
    public function patch(string $rule, $route): RuleItem
632
    {
633
        return $this->rule($rule, $route, 'PATCH');
634
    }
635
636
    /**
637
     * 注册资源路由
638
     * @access public
639
     * @param string $rule  路由规则
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
640
     * @param string $route 路由地址
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
641
     * @return Resource
642
     */
643
    public function resource(string $rule, string $route): Resource
644
    {
645
        return (new Resource($this, $this->group, $rule, $route, $this->rest))
646
            ->lazy($this->lazy);
647
    }
648
649
    /**
650
     * 注册视图路由
651
     * @access public
652
     * @param string|array $rule     路由规则
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
653
     * @param string       $template 路由模板地址
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
654
     * @param array        $vars     模板变量
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
655
     * @return RuleItem
656
     */
657
    public function view(string $rule, string $template = '', array $vars = []): RuleItem
658
    {
659
        return $this->rule($rule, $template, 'GET')->view($vars);
660
    }
661
662
    /**
663
     * 注册重定向路由
664
     * @access public
665
     * @param string|array $rule   路由规则
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
666
     * @param string       $route  路由地址
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
667
     * @param int          $status 状态码
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
668
     * @return RuleItem
669
     */
670
    public function redirect(string $rule, string $route = '', int $status = 301): RuleItem
671
    {
672
        return $this->rule($rule, $route, '*')->redirect()->status($status);
673
    }
674
675
    /**
676
     * rest方法定义和修改
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
677
     * @access public
678
     * @param string|array $name     方法名称
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
679
     * @param array|bool   $resource 资源
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
680
     * @return $this
681
     */
682
    public function rest($name, $resource = [])
683
    {
684
        if (is_array($name)) {
685
            $this->rest = $resource ? $name : array_merge($this->rest, $name);
686
        } else {
687
            $this->rest[$name] = $resource;
688
        }
689
690
        return $this;
691
    }
692
693
    /**
694
     * 获取rest方法定义的参数
695
     * @access public
696
     * @param string $name 方法名称
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
697
     * @return array|null
698
     */
699
    public function getRest(string $name = null)
700
    {
701
        if (is_null($name)) {
702
            return $this->rest;
703
        }
704
705
        return $this->rest[$name] ?? null;
706
    }
707
708
    /**
709
     * 注册未匹配路由规则后的处理
710
     * @access public
711
     * @param string|Closure $route  路由地址
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
712
     * @param string         $method 请求类型
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
713
     * @return RuleItem
714
     */
715
    public function miss($route, string $method = '*'): RuleItem
716
    {
717
        return $this->group->miss($route, $method);
718
    }
719
720
    /**
721
     * 路由调度
722
     * @param Request $request
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
723
     * @param Closure $withRoute
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
724
     * @return Response
725
     */
726
    public function dispatch(Request $request, $withRoute = null)
727
    {
728
        $this->request = $request;
729
        $this->host    = $this->request->host(true);
730
731
        $this->setDefaultDomain();
732
733
        if ($withRoute) {
734
            $checkCallback = function () use ($request, $withRoute) {
0 ignored issues
show
Unused Code introduced by
The import $request is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
735
                //加载路由
736
                $withRoute();
737
                return $this->check();
738
            };
739
740
            if ($this->config['route_check_cache']) {
741
                $dispatch = $this->cache
742
                    ->tag('route_cache')
743
                    ->remember($this->getRouteCacheKey($request), $checkCallback);
744
            } else {
745
                $dispatch = $checkCallback();
746
            }
747
        } else {
748
            $dispatch = $this->url($request->path());
749
        }
750
751
        $this->app->middleware->add(function () use ($dispatch) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
752
            try {
753
                $response = $dispatch->run();
754
            } catch (HttpResponseException $exception) {
755
                $response = $exception->getResponse();
756
            }
757
            return $response;
758
        });
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
759
760
        return $this->app->middleware->dispatch($request);
761
    }
762
763
    /**
764
     * 获取路由缓存Key
765
     * @access protected
766
     * @param Request $request
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
767
     * @return string
768
     */
769
    protected function getRouteCacheKey(Request $request): string
770
    {
771
        if (!empty($this->config['route_check_cache_key'])) {
772
            $closure  = $this->config['route_check_cache_key'];
773
            $routeKey = $closure($request);
774
        } else {
775
            $routeKey = md5($request->baseUrl(true) . ':' . $request->method());
776
        }
777
778
        return $routeKey;
779
    }
780
781
    /**
782
     * 检测URL路由
783
     * @access public
784
     * @return Dispatch
785
     * @throws RouteNotFoundException
786
     */
787
    public function check(): Dispatch
788
    {
789
        // 自动检测域名路由
790
        $url = str_replace($this->config['pathinfo_depr'], '|', $this->request->path());
791
792
        $completeMatch = $this->config['route_complete_match'];
793
794
        $result = $this->checkDomain()->check($this->request, $url, $completeMatch);
795
796
        if (false === $result && !empty($this->cross)) {
797
            // 检测跨域路由
798
            $result = $this->cross->check($this->request, $url, $completeMatch);
799
        }
800
801
        if (false !== $result) {
802
            return $result;
803
        } elseif ($this->config['url_route_must']) {
804
            throw new RouteNotFoundException();
805
        }
806
807
        return $this->url($url);
808
    }
809
810
    /**
811
     * 默认URL解析
812
     * @access public
813
     * @param string $url URL地址
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
814
     * @return Dispatch
815
     */
816
    public function url(string $url): UrlDispatch
817
    {
818
        return new UrlDispatch($this->request, $this->group, $url);
819
    }
820
821
    /**
822
     * 检测域名的路由规则
823
     * @access protected
824
     * @return Domain
825
     */
826
    protected function checkDomain(): Domain
827
    {
828
        // 获取当前子域名
829
        $subDomain = $this->request->subDomain();
830
831
        $item = false;
832
833
        if ($subDomain && count($this->domains) > 1) {
834
            $domain  = explode('.', $subDomain);
835
            $domain2 = array_pop($domain);
836
837
            if ($domain) {
838
                // 存在三级域名
839
                $domain3 = array_pop($domain);
840
            }
841
842
            if ($subDomain && isset($this->domains[$subDomain])) {
843
                // 子域名配置
844
                $item = $this->domains[$subDomain];
845
            } elseif (isset($this->domains['*.' . $domain2]) && !empty($domain3)) {
846
                // 泛三级域名
847
                $item      = $this->domains['*.' . $domain2];
848
                $panDomain = $domain3;
849
            } elseif (isset($this->domains['*']) && !empty($domain2)) {
850
                // 泛二级域名
851
                if ('www' != $domain2) {
852
                    $item      = $this->domains['*'];
853
                    $panDomain = $domain2;
854
                }
855
            }
856
857
            if (isset($panDomain)) {
858
                // 保存当前泛域名
859
                $this->request->setPanDomain($panDomain);
860
            }
861
        }
862
863
        if (false === $item) {
864
            // 检测当前完整域名
865
            $item = $this->domains[$this->host];
866
        }
867
868
        if (is_string($item)) {
869
            $item = $this->domains[$item];
870
        }
871
872
        return $item;
873
    }
874
875
    /**
876
     * URL生成 支持路由反射
877
     * @access public
878
     * @param  string       $url 路由地址
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
879
     * @param  array|string $vars 参数 ['a'=>'val1', 'b'=>'val2']
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
880
     * @param  string|bool  $suffix 伪静态后缀,默认为true表示获取配置值
881
     * @param  bool|string  $domain 是否显示域名 或者直接传入域名
882
     * @return string
883
     */
884
    public function buildUrl(string $url = '', $vars = [], $suffix = true, $domain = false): string
885
    {
886
        // 解析URL
887
        if (0 === strpos($url, '[') && $pos = strpos($url, ']')) {
888
            // [name] 表示使用路由命名标识生成URL
889
            $name = substr($url, 1, $pos - 1);
890
            $url  = 'name' . substr($url, $pos + 1);
891
        }
892
893
        if (false === strpos($url, '://') && 0 !== strpos($url, '/')) {
894
            $info = parse_url($url);
895
            $url  = !empty($info['path']) ? $info['path'] : '';
896
897
            if (isset($info['fragment'])) {
898
                // 解析锚点
899
                $anchor = $info['fragment'];
900
901
                if (false !== strpos($anchor, '?')) {
902
                    // 解析参数
903
                    list($anchor, $info['query']) = explode('?', $anchor, 2);
904
                }
905
906
                if (false !== strpos($anchor, '@')) {
907
                    // 解析域名
908
                    list($anchor, $domain) = explode('@', $anchor, 2);
909
                }
910
            } elseif (strpos($url, '@') && false === strpos($url, '\\')) {
911
                // 解析域名
912
                list($url, $domain) = explode('@', $url, 2);
913
            }
914
        }
915
916
        $this->showDomain = false === $domain ? false : true;
917
918
        // 解析参数
919
        if (is_string($vars)) {
920
            // aaa=1&bbb=2 转换成数组
921
            parse_str($vars, $vars);
0 ignored issues
show
Bug introduced by
$vars of type string is incompatible with the type array|null expected by parameter $arr of parse_str(). ( Ignorable by Annotation )

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

921
            parse_str($vars, /** @scrutinizer ignore-type */ $vars);
Loading history...
922
        }
923
924
        if ($url) {
925
            $checkName   = isset($name) ? $name : $url . (isset($info['query']) ? '?' . $info['query'] : '');
926
            $checkDomain = $domain && is_string($domain) ? $domain : null;
927
928
            $rule = $this->getName($checkName, $checkDomain);
929
930
            if (is_null($rule) && isset($info['query'])) {
0 ignored issues
show
introduced by
The condition is_null($rule) is always false.
Loading history...
931
                $rule = $this->getName($url, $checkDomain);
932
                // 解析地址里面参数 合并到vars
933
                parse_str($info['query'], $params);
934
                $vars = array_merge($params, $vars);
935
                unset($info['query']);
936
            }
937
        }
938
939
        if (!empty($rule) && $match = $this->getRuleUrl($rule, $vars, $domain)) {
940
            // 匹配路由命名标识
941
            $url = $match[0];
942
943
            if (!empty($match[1])) {
944
                $domain = $match[1];
945
            }
946
947
            if (!is_null($match[2])) {
948
                $suffix = $match[2];
949
            }
950
951
            if ($this->request->app() && !$this->app->http->isBindDomain()) {
952
                $url = $this->request->app() . '/' . $url;
953
            }
954
        } elseif (!empty($rule) && isset($name)) {
955
            throw new \InvalidArgumentException('route name not exists:' . $name);
956
        } else {
957
            // 检测URL绑定
958
            if (!$this->bindCheck) {
959
                $bind = $this->getDomainBind($domain && is_string($domain) ? $domain : null);
960
961
                if ($bind && 0 === strpos($url, $bind)) {
962
                    $url = substr($url, strlen($bind) + 1);
963
                } else {
964
                    $binds = $this->getBind();
965
966
                    foreach ($binds as $key => $val) {
967
                        if (is_string($val) && 0 === strpos($url, $val) && substr_count($val, '/') > 1) {
968
                            $url    = substr($url, strlen($val) + 1);
969
                            $domain = $key;
970
                            break;
971
                        }
972
                    }
973
                }
974
            }
975
976
            // 路由标识不存在 直接解析
977
            $url = $this->parseUrl($url, $domain);
978
979
            if (isset($info['query'])) {
980
                // 解析地址里面参数 合并到vars
981
                parse_str($info['query'], $params);
982
                $vars = array_merge($params, $vars);
983
            }
984
        }
985
986
        // 还原URL分隔符
987
        $depr = $this->config['pathinfo_depr'];
988
        $url  = str_replace('/', $depr, $url);
989
990
        $file = $this->request->baseFile();
991
        if ($file && 0 !== strpos($this->request->url(), $file)) {
992
            $file = str_replace('\\', '/', dirname($file));
993
        }
994
995
        $url = rtrim($file, '/') . '/' . $url;
996
997
        // URL后缀
998
        if ('/' == substr($url, -1) || '' == $url) {
999
            $suffix = '';
1000
        } else {
1001
            $suffix = $this->parseSuffix($suffix);
1002
        }
1003
1004
        // 锚点
1005
        $anchor = !empty($anchor) ? '#' . $anchor : '';
1006
1007
        // 参数组装
1008
        if (!empty($vars)) {
1009
            // 添加参数
1010
            if ($this->config['url_common_param']) {
1011
                $vars = http_build_query($vars);
1012
                $url .= $suffix . '?' . $vars . $anchor;
1013
            } else {
1014
                foreach ($vars as $var => $val) {
1015
                    if ('' !== $val) {
1016
                        $url .= $depr . $var . $depr . urlencode((string) $val);
1017
                    }
1018
                }
1019
1020
                $url .= $suffix . $anchor;
1021
            }
1022
        } else {
1023
            $url .= $suffix . $anchor;
1024
        }
1025
1026
        // 检测域名
1027
        $domain = $this->parseDomain($url, $domain);
1028
1029
        // URL组装
1030
        $url = $domain . '/' . ltrim($url, '/');
1031
1032
        $this->bindCheck = false;
0 ignored issues
show
Bug Best Practice introduced by
The property bindCheck does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1033
1034
        return $url;
1035
    }
1036
1037
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $domain should have a doc-comment as per coding-style.
Loading history...
1038
     * 直接解析URL地址
1039
     * @access public
1040
     * @param  string $url URL
1041
     * @return string
1042
     */
1043
    protected function parseUrl(string $url, &$domain): string
1044
    {
1045
        $request = $this->request;
1046
1047
        if (0 === strpos($url, '/')) {
1048
            // 直接作为路由地址解析
1049
            $url = substr($url, 1);
1050
        } elseif (false !== strpos($url, '\\')) {
1051
            // 解析到类
1052
            $url = ltrim(str_replace('\\', '/', $url), '/');
1053
        } elseif (0 === strpos($url, '@')) {
1054
            // 解析到控制器
1055
            $url = substr($url, 1);
1056
        } else {
1057
            // 解析到 应用/控制器/操作
1058
            $app        = $request->app();
1059
            $controller = $request->controller();
1060
1061
            if ('' == $url) {
1062
                $action = $request->action();
1063
            } else {
1064
                $path       = explode('/', $url);
1065
                $action     = array_pop($path);
1066
                $controller = empty($path) ? $controller : array_pop($path);
1067
                $app        = empty($path) ? $app : array_pop($path);
1068
            }
1069
1070
            if ($this->config['url_convert']) {
1071
                $action     = strtolower($action);
1072
                $controller = App::parseName($controller);
1073
            }
1074
1075
            $url = $controller . '/' . $action;
1076
1077
            if ($app) {
1078
                $bind = $this->app->config->get('app.domain_bind', []);
1079
                if ($key = array_search($app, $bind)) {
1080
                    $domain = true === $domain ? $key : $domain;
1081
                } else {
1082
                    $map = $this->app->config->get('app.app_map', []);
1083
1084
                    if ($key = array_search($app, $map)) {
1085
                        $url = $key . '/' . $url;
1086
                    } else {
1087
                        $url = $app . '/' . $url;
1088
                    }
1089
                }
1090
            }
1091
        }
1092
1093
        return $url;
1094
    }
1095
1096
    /**
1097
     * 检测域名
1098
     * @access public
1099
     * @param  string      $url URL
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
1100
     * @param  string|true $domain 域名
1101
     * @return string
1102
     */
1103
    protected function parseDomain(string &$url, $domain): string
1104
    {
1105
        if (!$domain) {
1106
            return '';
1107
        }
1108
1109
        $rootDomain = $this->request->rootDomain();
1110
        if (true === $domain) {
1111
            // 自动判断域名
1112
            $domain  = $this->request->host();
1113
            $domains = $this->getDomains();
1114
1115
            if (!empty($domains)) {
1116
                $route_domain = array_keys($domains);
1117
                foreach ($route_domain as $domain_prefix) {
1118
                    if (0 === strpos($domain_prefix, '*.') && strpos($domain, ltrim($domain_prefix, '*.')) !== false) {
1119
                        foreach ($domains as $key => $rule) {
1120
                            $rule = is_array($rule) ? $rule[0] : $rule;
1121
                            if (is_string($rule) && false === strpos($key, '*') && 0 === strpos($url, $rule)) {
1122
                                $url    = ltrim($url, $rule);
1123
                                $domain = $key;
1124
1125
                                // 生成对应子域名
1126
                                if (!empty($rootDomain)) {
1127
                                    $domain .= $rootDomain;
1128
                                }
1129
                                break;
1130
                            } elseif (false !== strpos($key, '*')) {
1131
                                if (!empty($rootDomain)) {
1132
                                    $domain .= $rootDomain;
1133
                                }
1134
1135
                                break;
1136
                            }
1137
                        }
1138
                    }
1139
                }
1140
            }
1141
        } elseif (false === strpos($domain, '.') && 0 !== strpos($domain, $rootDomain)) {
1142
            $domain .= '.' . $rootDomain;
1143
        }
1144
1145
        if (false !== strpos($domain, '://')) {
1146
            $scheme = '';
1147
        } else {
1148
            $scheme = $this->request->isSsl() ? 'https://' : 'http://';
1149
        }
1150
1151
        return $this->request->host() == $domain && !$this->showDomain ? '' : $scheme . $domain;
1152
    }
1153
1154
    /**
1155
     * 解析URL后缀
1156
     * @access public
1157
     * @param  string|bool $suffix 后缀
1158
     * @return string
1159
     */
1160
    protected function parseSuffix($suffix): string
1161
    {
1162
        if ($suffix) {
1163
            $suffix = true === $suffix ? $this->config['url_html_suffix'] : $suffix;
1164
1165
            if ($pos = strpos($suffix, '|')) {
1166
                $suffix = substr($suffix, 0, $pos);
1167
            }
1168
        }
1169
1170
        return (empty($suffix) || 0 === strpos($suffix, '.')) ? (string) $suffix : '.' . $suffix;
1171
    }
1172
1173
    /**
1174
     * 匹配路由地址
1175
     * @access public
1176
     * @param  array $rule 路由规则
0 ignored issues
show
Coding Style introduced by
Expected 8 spaces after parameter name; 1 found
Loading history...
1177
     * @param  array $vars 路由变量
0 ignored issues
show
Coding Style introduced by
Expected 8 spaces after parameter name; 1 found
Loading history...
1178
     * @param  mixed $allowDomain 允许域名
1179
     * @return array
1180
     */
1181
    public function getRuleUrl(array $rule, array &$vars = [], $allowDomain = ''): array
1182
    {
1183
        foreach ($rule as $item) {
1184
            list($url, $pattern, $domain, $suffix) = $item;
1185
1186
            if (is_string($allowDomain) && $domain != $allowDomain) {
1187
                continue;
1188
            }
1189
1190
            if (!in_array($this->request->port(), [80, 443])) {
1191
                $domain .= ':' . $this->request->port();
1192
            }
1193
1194
            if (empty($pattern)) {
1195
                return [rtrim($url, '?/-'), $domain, $suffix];
1196
            }
1197
1198
            $type = $this->config['url_common_param'];
1199
1200
            foreach ($pattern as $key => $val) {
1201
                if (isset($vars[$key])) {
1202
                    $url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key, '<' . $key . '>'], $type ? $vars[$key] : urlencode((string) $vars[$key]), $url);
1203
                    unset($vars[$key]);
1204
                    $url    = str_replace(['/?', '-?'], ['/', '-'], $url);
1205
                    $result = [rtrim($url, '?/-'), $domain, $suffix];
1206
                } elseif (2 == $val) {
1207
                    $url    = str_replace(['/[:' . $key . ']', '[:' . $key . ']', '<' . $key . '?>'], '', $url);
1208
                    $url    = str_replace(['/?', '-?'], ['/', '-'], $url);
1209
                    $result = [rtrim($url, '?/-'), $domain, $suffix];
1210
                } else {
1211
                    break;
1212
                }
1213
            }
1214
1215
            if (isset($result)) {
1216
                return $result;
1217
            }
1218
        }
1219
1220
        return [];
1221
    }
1222
1223
    /**
1224
     * 设置全局的路由分组参数
1225
     * @access public
1226
     * @param string $method 方法名
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
1227
     * @param array  $args   调用参数
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
1228
     * @return RuleGroup
1229
     */
1230
    public function __call($method, $args)
1231
    {
1232
        return call_user_func_array([$this->group, $method], $args);
1233
    }
1234
}
1235