Passed
Push — 6.0 ( e895fd...394018 )
by liu
03:23
created

Route::buildUrl()   F

Complexity

Conditions 46
Paths > 20000

Size

Total Lines 139
Code Lines 73

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2162

Importance

Changes 0
Metric Value
cc 46
eloc 73
nc 660660
nop 4
dl 0
loc 139
ccs 0
cts 69
cp 0
crap 2162
rs 0
c 0
b 0
f 0

How to fix   Long Method    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
/**
29
 * 路由管理类
30
 */
5 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @package tag in class comment
Loading history...
Coding Style introduced by
Missing @author tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
31
class Route
32
{
33
    /**
34
     * REST定义
35
     * @var array
36
     */
37
    protected $rest = [
38
        'index'  => ['get', '', 'index'],
39
        'create' => ['get', '/create', 'create'],
40
        'edit'   => ['get', '/<id>/edit', 'edit'],
41
        'read'   => ['get', '/<id>', 'read'],
42
        'save'   => ['post', '', 'save'],
43
        'update' => ['put', '/<id>', 'update'],
44
        'delete' => ['delete', '/<id>', 'delete'],
45
    ];
46
47
    /**
48
     * 配置参数
49
     * @var array
50
     */
51
    protected $config = [
52
        // pathinfo分隔符
53
        'pathinfo_depr'         => '/',
54
        // 是否开启路由延迟解析
55
        'url_lazy_route'        => false,
56
        // 是否强制使用路由
57
        'url_route_must'        => false,
58
        // 合并路由规则
59
        'route_rule_merge'      => false,
60
        // 路由是否完全匹配
61
        'route_complete_match'  => false,
62
        // 使用注解路由
63
        'route_annotation'      => false,
64
        // 路由缓存设置
65
        'route_check_cache'     => false,
66
        'route_cache_option'    => [],
67
        'route_check_cache_key' => '',
68
        // 是否自动转换URL中的控制器和操作名
69
        'url_convert'           => true,
70
        // 默认的路由变量规则
71
        'default_route_pattern' => '[\w\.]+',
72
        // URL伪静态后缀
73
        'url_html_suffix'       => 'html',
74
        // 访问控制器层名称
75
        'controller_layer'      => 'controller',
76
        // 空控制器名
77
        'empty_controller'      => 'Error',
78
        // 是否使用控制器后缀
79
        'controller_suffix'     => false,
80
        // 默认控制器名
81
        'default_controller'    => 'Index',
82
        // 默认操作名
83
        'default_action'        => 'index',
84
        // 操作方法后缀
85
        'action_suffix'         => '',
86
        // 是否开启路由检测缓存
87
        'route_check_cache'     => false,
88
        // 非路由变量是否使用普通参数方式(用于URL生成)
89
        'url_common_param'      => true,
90
    ];
91
92
    /**
93
     * 当前应用
94
     * @var App
95
     */
96
    protected $app;
97
98
    /**
99
     * 请求对象
100
     * @var Request
101
     */
102
    protected $request;
103
104
    /**
105
     * 缓存
106
     * @var Driver
107
     */
108
    protected $cache;
109
110
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
111
     * @var RuleName
112
     */
113
    protected $ruleName;
114
115
    /**
116
     * 当前HOST
117
     * @var string
118
     */
119
    protected $host;
120
121
    /**
122
     * 当前分组对象
123
     * @var RuleGroup
124
     */
125
    protected $group;
126
127
    /**
128
     * 路由绑定
129
     * @var array
130
     */
131
    protected $bind = [];
132
133
    /**
134
     * 域名对象
135
     * @var array
136
     */
137
    protected $domains = [];
138
139
    /**
140
     * 跨域路由规则
141
     * @var RuleGroup
142
     */
143
    protected $cross;
144
145
    /**
146
     * 路由是否延迟解析
147
     * @var bool
148
     */
149
    protected $lazy = true;
150
151
    /**
152
     * 路由是否测试模式
153
     * @var bool
154
     */
155
    protected $isTest = false;
156
157
    /**
158
     * (分组)路由规则是否合并解析
159
     * @var bool
160
     */
161
    protected $mergeRuleRegex = false;
162
163
    /**
164
     * 显示域名
165
     * @var bool
166
     */
167
    protected $showDomain;
168
169
    public function __construct(App $app)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __construct()
Loading history...
170
    {
171
        $this->app      = $app;
172
        $this->config   = array_merge($this->config, $app->config->get('route'));
173
        $this->ruleName = new RuleName();
174
175
        $this->lazy($this->config['url_lazy_route']);
176
177
        if ($this->config['route_check_cache']) {
178
            if (!empty($this->config['route_cache_option'])) {
179
                $this->cache = $app->cache->connect($this->config['route_cache_option']);
180
            } else {
181
                $this->cache = $app->cache->init();
182
            }
183
        }
184
185
        if (is_file($app->getRuntimePath() . 'route.php')) {
186
            // 读取路由映射文件
187
            $this->import(include $app->getRuntimePath() . 'route.php');
188
        }
189
190
        $this->setDefaultDomain();
191
    }
192
193
    public function config(string $name = null)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function config()
Loading history...
194
    {
195
        if (is_null($name)) {
196
            return $this->config;
197
        }
198
199
        return $this->config[$name] ?? null;
200
    }
201
202
    /**
203
     * 设置路由域名及分组(包括资源路由)是否延迟解析
204
     * @access public
205
     * @param bool $lazy 路由是否延迟解析
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
206
     * @return $this
207
     */
208
    public function lazy(bool $lazy = true)
209
    {
210
        $this->lazy = $lazy;
211
        return $this;
212
    }
213
214
    /**
215
     * 设置路由为测试模式
216
     * @access public
217
     * @param bool $test 路由是否测试模式
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
218
     * @return void
219
     */
220
    public function setTestMode(bool $test): void
221
    {
222
        $this->isTest = $test;
223
    }
224
225
    /**
226
     * 检查路由是否为测试模式
227
     * @access public
228
     * @return bool
229
     */
230
    public function isTest(): bool
231
    {
232
        return $this->isTest;
233
    }
234
235
    /**
236
     * 设置路由域名及分组(包括资源路由)是否合并解析
237
     * @access public
238
     * @param bool $merge 路由是否合并解析
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
239
     * @return $this
240
     */
241
    public function mergeRuleRegex(bool $merge = true)
242
    {
243
        $this->mergeRuleRegex = $merge;
244
        $this->group->mergeRuleRegex($merge);
245
246
        return $this;
247
    }
248
249
    /**
250
     * 初始化默认域名
251
     * @access protected
252
     * @return void
253
     */
254
    protected function setDefaultDomain(): void
255
    {
256
        // 注册默认域名
257
        $domain = new Domain($this);
258
259
        $this->domains['-'] = $domain;
260
261
        // 默认分组
262
        $this->group = $domain;
263
    }
264
265
    /**
266
     * 设置当前分组
267
     * @access public
268
     * @param RuleGroup $group 域名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
269
     * @return void
270
     */
271
    public function setGroup(RuleGroup $group): void
272
    {
273
        $this->group = $group;
274
    }
275
276
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $name should have a doc-comment as per coding-style.
Loading history...
277
     * 获取指定标识的路由分组 不指定则获取当前分组
278
     * @access public
279
     * @return RuleGroup
280
     */
281
    public function getGroup(string $name = null)
282
    {
283
        return $name ? $this->ruleName->getGroup($name) : $this->group;
284
    }
285
286
    /**
287
     * 注册变量规则
288
     * @access public
289
     * @param array $pattern 变量规则
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
290
     * @return $this
291
     */
292
    public function pattern(array $pattern)
293
    {
294
        $this->group->pattern($pattern);
295
296
        return $this;
297
    }
298
299
    /**
300
     * 注册路由参数
301
     * @access public
302
     * @param array $option 参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
303
     * @return $this
304
     */
305
    public function option(array $option)
306
    {
307
        $this->group->option($option);
308
309
        return $this;
310
    }
311
312
    /**
313
     * 注册域名路由
314
     * @access public
315
     * @param string|array $name 子域名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
316
     * @param mixed        $rule 路由规则
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
317
     * @return Domain
318
     */
319
    public function domain($name, $rule = null): Domain
320
    {
321
        // 支持多个域名使用相同路由规则
322
        $domainName = is_array($name) ? array_shift($name) : $name;
323
324
        if (!isset($this->domains[$domainName])) {
325
            $domain = (new Domain($this, $domainName, $rule))
326
                ->lazy($this->lazy)
327
                ->mergeRuleRegex($this->mergeRuleRegex);
328
329
            $this->domains[$domainName] = $domain;
330
        } else {
331
            $domain = $this->domains[$domainName];
332
            $domain->parseGroupRule($rule);
333
        }
334
335
        if (is_array($name) && !empty($name)) {
336
            foreach ($name as $item) {
337
                $this->domains[$item] = $domainName;
338
            }
339
        }
340
341
        // 返回域名对象
342
        return $domain;
343
    }
344
345
    /**
346
     * 获取域名
347
     * @access public
348
     * @return array
349
     */
350
    public function getDomains(): array
351
    {
352
        return $this->domains;
353
    }
354
355
    /**
356
     * 获取RuleName对象
357
     * @access public
358
     * @return RuleName
359
     */
360
    public function getRuleName(): RuleName
361
    {
362
        return $this->ruleName;
363
    }
364
365
    /**
366
     * 设置路由绑定
367
     * @access public
368
     * @param string $bind   绑定信息
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
369
     * @param string $domain 域名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
370
     * @return $this
371
     */
372
    public function bind(string $bind, string $domain = null)
373
    {
374
        $domain = is_null($domain) ? '-' : $domain;
375
376
        $this->bind[$domain] = $bind;
377
378
        return $this;
379
    }
380
381
    /**
382
     * 读取路由绑定信息
383
     * @access public
384
     * @return array
385
     */
386
    public function getBind(): array
387
    {
388
        return $this->bind;
389
    }
390
391
    /**
392
     * 读取路由绑定
393
     * @access public
394
     * @param string $domain 域名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
395
     * @return string|null
396
     */
397
    public function getDomainBind(string $domain = null)
398
    {
399
        if (is_null($domain)) {
400
            $domain = $this->host;
401
        } elseif (false === strpos($domain, '.')) {
402
            $domain .= '.' . $this->request->rootDomain();
403
        }
404
405
        $subDomain = $this->request->subDomain();
406
407
        if (strpos($subDomain, '.')) {
408
            $name = '*' . strstr($subDomain, '.');
409
        }
410
411
        if (isset($this->bind[$domain])) {
412
            $result = $this->bind[$domain];
413
        } elseif (isset($name) && isset($this->bind[$name])) {
414
            $result = $this->bind[$name];
415
        } elseif (!empty($subDomain) && isset($this->bind['*'])) {
416
            $result = $this->bind['*'];
417
        } else {
418
            $result = null;
419
        }
420
421
        return $result;
422
    }
423
424
    /**
425
     * 读取路由标识
426
     * @access public
427
     * @param string $name   路由标识
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
428
     * @param string $domain 域名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
429
     * @param string $method 请求类型
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
430
     * @return RuleItem[]
431
     */
432
    public function getName(string $name = null, string $domain = null, string $method = '*'): array
433
    {
434
        return $this->ruleName->getName($name, $domain, $method);
435
    }
436
437
    /**
438
     * 批量导入路由标识
439
     * @access public
440
     * @param array $name 路由标识
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
441
     * @return $this
442
     */
443
    public function import(array $name): void
444
    {
445
        $this->ruleName->import($name);
446
    }
447
448
    /**
449
     * 注册路由标识
450
     * @access public
451
     * @param string       $name  路由标识
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
452
     * @param string|array $value 路由规则
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
453
     * @param bool         $first 是否置顶
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
454
     * @return void
455
     */
456
    public function setName(string $name, $value, bool $first = false): void
457
    {
458
        $this->ruleName->setName($name, $value, $first);
459
    }
460
461
    /**
462
     * 保存路由规则
463
     * @access public
464
     * @param string $rule   路由规则
1 ignored issue
show
Coding Style introduced by
Expected 5 spaces after parameter name; 3 found
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
465
     * @param RuleItem $ruleItem RuleItem对象
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
466
     * @return void
467
     */
468
    public function setRule(string $rule, RuleItem $ruleItem = null): void
469
    {
470
        $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

470
        $this->ruleName->setRule($rule, /** @scrutinizer ignore-type */ $ruleItem);
Loading history...
471
    }
472
473
    /**
474
     * 读取路由
475
     * @access public
476
     * @param string $rule   路由规则
1 ignored issue
show
Coding Style introduced by
Expected 1 spaces after parameter name; 3 found
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
477
     * @return RuleItem[]
478
     */
479
    public function getRule(string $rule): array
480
    {
481
        return $this->ruleName->getRule($rule);
482
    }
483
484
    /**
485
     * 读取路由列表
486
     * @access public
487
     * @return array
488
     */
489
    public function getRuleList(): array
490
    {
491
        return $this->ruleName->getRuleList();
492
    }
493
494
    /**
495
     * 清空路由规则
496
     * @access public
497
     * @return void
498
     */
499
    public function clear(): void
500
    {
501
        $this->ruleName->clear();
502
503
        if ($this->group) {
504
            $this->group->clear();
505
        }
506
    }
507
508
    /**
509
     * 注册路由规则
510
     * @access public
511
     * @param string $rule   路由规则
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
512
     * @param mixed  $route  路由地址
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
513
     * @param string $method 请求类型
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
514
     * @return RuleItem
515
     */
516
    public function rule(string $rule, $route = null, string $method = '*'): RuleItem
517
    {
518
        return $this->group->addRule($rule, $route, $method);
519
    }
520
521
    /**
522
     * 设置跨域有效路由规则
523
     * @access public
524
     * @param Rule   $rule   路由规则
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
525
     * @param string $method 请求类型
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
526
     * @return $this
527
     */
528
    public function setCrossDomainRule(Rule $rule, string $method = '*')
529
    {
530
        if (!isset($this->cross)) {
531
            $this->cross = (new RuleGroup($this))->mergeRuleRegex($this->mergeRuleRegex);
532
        }
533
534
        $this->cross->addRuleItem($rule, $method);
535
536
        return $this;
537
    }
538
539
    /**
540
     * 注册路由分组
541
     * @access public
542
     * @param string|\Closure $name  分组名称或者参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
543
     * @param mixed           $route 分组路由
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
544
     * @return RuleGroup
545
     */
546
    public function group($name, $route = null): RuleGroup
547
    {
548
        if ($name instanceof \Closure) {
549
            $route = $name;
550
            $name  = '';
551
        }
552
553
        return (new RuleGroup($this, $this->group, $name, $route))
554
            ->lazy($this->lazy)
555
            ->mergeRuleRegex($this->mergeRuleRegex);
556
    }
557
558
    /**
559
     * 注册路由
560
     * @access public
561
     * @param string $rule  路由规则
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
562
     * @param mixed  $route 路由地址
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
563
     * @return RuleItem
564
     */
565
    public function any(string $rule, $route): RuleItem
566
    {
567
        return $this->rule($rule, $route, '*');
568
    }
569
570
    /**
571
     * 注册GET路由
572
     * @access public
573
     * @param string $rule  路由规则
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
574
     * @param mixed  $route 路由地址
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
575
     * @return RuleItem
576
     */
577
    public function get(string $rule, $route): RuleItem
578
    {
579
        return $this->rule($rule, $route, 'GET');
580
    }
581
582
    /**
583
     * 注册POST路由
584
     * @access public
585
     * @param string $rule  路由规则
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
586
     * @param mixed  $route 路由地址
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
587
     * @return RuleItem
588
     */
589
    public function post(string $rule, $route): RuleItem
590
    {
591
        return $this->rule($rule, $route, 'POST');
592
    }
593
594
    /**
595
     * 注册PUT路由
596
     * @access public
597
     * @param string $rule  路由规则
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
598
     * @param mixed  $route 路由地址
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
599
     * @return RuleItem
600
     */
601
    public function put(string $rule, $route): RuleItem
602
    {
603
        return $this->rule($rule, $route, 'PUT');
604
    }
605
606
    /**
607
     * 注册DELETE路由
608
     * @access public
609
     * @param string $rule  路由规则
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
610
     * @param mixed  $route 路由地址
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
611
     * @return RuleItem
612
     */
613
    public function delete(string $rule, $route): RuleItem
614
    {
615
        return $this->rule($rule, $route, 'DELETE');
616
    }
617
618
    /**
619
     * 注册PATCH路由
620
     * @access public
621
     * @param string $rule  路由规则
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
622
     * @param mixed  $route 路由地址
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
623
     * @return RuleItem
624
     */
625
    public function patch(string $rule, $route): RuleItem
626
    {
627
        return $this->rule($rule, $route, 'PATCH');
628
    }
629
630
    /**
631
     * 注册资源路由
632
     * @access public
633
     * @param string $rule  路由规则
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
634
     * @param string $route 路由地址
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
635
     * @return Resource
636
     */
637
    public function resource(string $rule, string $route): Resource
638
    {
639
        return (new Resource($this, $this->group, $rule, $route, $this->rest))
640
            ->lazy($this->lazy);
641
    }
642
643
    /**
644
     * 注册视图路由
645
     * @access public
646
     * @param string|array $rule     路由规则
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
647
     * @param string       $template 路由模板地址
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
648
     * @param array        $vars     模板变量
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
649
     * @return RuleItem
650
     */
651
    public function view(string $rule, string $template = '', array $vars = []): RuleItem
652
    {
653
        return $this->rule($rule, $template, 'GET')->view($vars);
654
    }
655
656
    /**
657
     * 注册重定向路由
658
     * @access public
659
     * @param string|array $rule   路由规则
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
660
     * @param string       $route  路由地址
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
661
     * @param int          $status 状态码
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
662
     * @return RuleItem
663
     */
664
    public function redirect(string $rule, string $route = '', int $status = 301): RuleItem
665
    {
666
        return $this->rule($rule, $route, '*')->redirect()->status($status);
667
    }
668
669
    /**
670
     * rest方法定义和修改
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
671
     * @access public
672
     * @param string|array $name     方法名称
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
673
     * @param array|bool   $resource 资源
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
674
     * @return $this
675
     */
676
    public function rest($name, $resource = [])
677
    {
678
        if (is_array($name)) {
679
            $this->rest = $resource ? $name : array_merge($this->rest, $name);
680
        } else {
681
            $this->rest[$name] = $resource;
682
        }
683
684
        return $this;
685
    }
686
687
    /**
688
     * 获取rest方法定义的参数
689
     * @access public
690
     * @param string $name 方法名称
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
691
     * @return array|null
692
     */
693
    public function getRest(string $name = null)
694
    {
695
        if (is_null($name)) {
696
            return $this->rest;
697
        }
698
699
        return $this->rest[$name] ?? null;
700
    }
701
702
    /**
703
     * 注册未匹配路由规则后的处理
704
     * @access public
705
     * @param string|Closure $route  路由地址
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
706
     * @param string         $method 请求类型
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
707
     * @return RuleItem
708
     */
709
    public function miss($route, string $method = '*'): RuleItem
710
    {
711
        return $this->group->miss($route, $method);
712
    }
713
714
    /**
715
     * 路由调度
716
     * @param Request $request
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
717
     * @param Closure $withRoute
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
718
     * @return Response
719
     */
720
    public function dispatch(Request $request, $withRoute = null)
721
    {
722
        $this->request = $request;
723
        $this->host    = $this->request->host(true);
724
725
        if ($withRoute) {
726
            $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...
727
                //加载路由
728
                $withRoute();
729
                return $this->check();
730
            };
731
732
            if ($this->config['route_check_cache']) {
733
                $dispatch = $this->cache
734
                    ->tag('route_cache')
735
                    ->remember($this->getRouteCacheKey($request), $checkCallback);
736
            } else {
737
                $dispatch = $checkCallback();
738
            }
739
        } else {
740
            $dispatch = $this->url($this->path());
741
        }
742
743
        $dispatch->init($this->app);
744
745
        $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...
746
            try {
747
                $response = $dispatch->run();
748
            } catch (HttpResponseException $exception) {
749
                $response = $exception->getResponse();
750
            }
751
            return $response;
752
        });
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...
753
754
        return $this->app->middleware->dispatch($request);
755
    }
756
757
    /**
758
     * 获取路由缓存Key
759
     * @access protected
760
     * @param Request $request
1 ignored issue
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
761
     * @return string
762
     */
763
    protected function getRouteCacheKey(Request $request): string
764
    {
765
        if (!empty($this->config['route_check_cache_key'])) {
766
            $closure  = $this->config['route_check_cache_key'];
767
            $routeKey = $closure($request);
768
        } else {
769
            $routeKey = md5($request->baseUrl(true) . ':' . $request->method());
770
        }
771
772
        return $routeKey;
773
    }
774
775
    /**
776
     * 检测URL路由
777
     * @access public
778
     * @return Dispatch
779
     * @throws RouteNotFoundException
780
     */
781
    public function check(): Dispatch
782
    {
783
        // 自动检测域名路由
784
        $url = str_replace($this->config['pathinfo_depr'], '|', $this->path());
785
786
        $completeMatch = $this->config['route_complete_match'];
787
788
        $result = $this->checkDomain()->check($this->request, $url, $completeMatch);
789
790
        if (false === $result && !empty($this->cross)) {
791
            // 检测跨域路由
792
            $result = $this->cross->check($this->request, $url, $completeMatch);
793
        }
794
795
        if (false !== $result) {
796
            return $result;
797
        } elseif ($this->config['url_route_must']) {
798
            throw new RouteNotFoundException();
799
        }
800
801
        return $this->url($url);
802
    }
803
804
    /**
805
     * 获取当前请求URL的pathinfo信息(不含URL后缀)
806
     * @access protected
807
     * @return string
808
     */
809
    protected function path(): string
810
    {
811
        $suffix   = $this->config['url_html_suffix'];
812
        $pathinfo = $this->request->pathinfo();
813
814
        if (false === $suffix) {
815
            // 禁止伪静态访问
816
            $path = $pathinfo;
817
        } elseif ($suffix) {
818
            // 去除正常的URL后缀
819
            $path = preg_replace('/\.(' . ltrim($suffix, '.') . ')$/i', '', $pathinfo);
820
        } else {
821
            // 允许任何后缀访问
822
            $path = preg_replace('/\.' . $this->request->ext() . '$/i', '', $pathinfo);
823
        }
824
825
        return $path;
826
    }
827
828
    /**
829
     * 默认URL解析
830
     * @access public
831
     * @param string $url URL地址
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
832
     * @return Dispatch
833
     */
834
    public function url(string $url): UrlDispatch
835
    {
836
        return new UrlDispatch($this->request, $this->group, $url);
837
    }
838
839
    /**
840
     * 检测域名的路由规则
841
     * @access protected
842
     * @return Domain
843
     */
844
    protected function checkDomain(): Domain
845
    {
846
        $item = false;
847
848
        if (count($this->domains) > 1) {
849
            // 获取当前子域名
850
            $subDomain = $this->request->subDomain();
851
852
            $domain  = $subDomain ? explode('.', $subDomain) : [];
853
            $domain2 = $domain ? array_pop($domain) : '';
854
855
            if ($domain) {
856
                // 存在三级域名
857
                $domain3 = array_pop($domain);
858
            }
859
860
            if (isset($this->domains[$this->host])) {
861
                // 子域名配置
862
                $item = $this->domains[$this->host];
863
            } elseif (isset($this->domains[$subDomain])) {
864
                $item = $this->domains[$subDomain];
865
            } elseif (isset($this->domains['*.' . $domain2]) && !empty($domain3)) {
866
                // 泛三级域名
867
                $item      = $this->domains['*.' . $domain2];
868
                $panDomain = $domain3;
869
            } elseif (isset($this->domains['*']) && !empty($domain2)) {
870
                // 泛二级域名
871
                if ('www' != $domain2) {
872
                    $item      = $this->domains['*'];
873
                    $panDomain = $domain2;
874
                }
875
            }
876
877
            if (isset($panDomain)) {
878
                // 保存当前泛域名
879
                $this->request->setPanDomain($panDomain);
880
            }
881
        }
882
883
        if (false === $item) {
884
            // 检测全局域名规则
885
            $item = $this->domains['-'];
886
        }
887
888
        if (is_string($item)) {
889
            $item = $this->domains[$item];
890
        }
891
892
        return $item;
893
    }
894
895
    /**
896
     * URL生成 支持路由反射
897
     * @access public
898
     * @param  string       $url 路由地址
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
899
     * @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...
900
     * @param  string|bool  $suffix 伪静态后缀,默认为true表示获取配置值
901
     * @param  bool|string  $domain 是否显示域名 或者直接传入域名
902
     * @return string
903
     */
904
    public function buildUrl(string $url = '', array $vars = [], $suffix = true, $domain = false): string
905
    {
906
        // 解析URL
907
        if (0 === strpos($url, '[') && $pos = strpos($url, ']')) {
908
            // [name] 表示使用路由命名标识生成URL
909
            $name = substr($url, 1, $pos - 1);
910
            $url  = 'name' . substr($url, $pos + 1);
911
        }
912
913
        if (false === strpos($url, '://') && 0 !== strpos($url, '/')) {
914
            $info = parse_url($url);
915
            $url  = !empty($info['path']) ? $info['path'] : '';
916
917
            if (isset($info['fragment'])) {
918
                // 解析锚点
919
                $anchor = $info['fragment'];
920
921
                if (false !== strpos($anchor, '?')) {
922
                    // 解析参数
923
                    list($anchor, $info['query']) = explode('?', $anchor, 2);
924
                }
925
926
                if (false !== strpos($anchor, '@')) {
927
                    // 解析域名
928
                    list($anchor, $domain) = explode('@', $anchor, 2);
929
                }
930
            } elseif (strpos($url, '@') && false === strpos($url, '\\')) {
931
                // 解析域名
932
                list($url, $domain) = explode('@', $url, 2);
933
            }
934
        }
935
936
        $this->showDomain = false === $domain ? false : true;
937
938
        if ($url) {
939
            $checkName   = isset($name) ? $name : $url . (isset($info['query']) ? '?' . $info['query'] : '');
940
            $checkDomain = $domain && is_string($domain) ? $domain : null;
941
942
            $rule = $this->getName($checkName, $checkDomain);
943
944
            if (empty($rule) && isset($info['query'])) {
945
                $rule = $this->getName($url, $checkDomain);
946
                // 解析地址里面参数 合并到vars
947
                parse_str($info['query'], $params);
948
                $vars = array_merge($params, $vars);
949
                unset($info['query']);
950
            }
951
        }
952
953
        if (!empty($rule) && $match = $this->getRuleUrl($rule, $vars, $domain)) {
954
            // 匹配路由命名标识
955
            $url = $match[0];
956
957
            if (!empty($match[1])) {
958
                $domain = $match[1];
959
            }
960
961
            if (!is_null($match[2])) {
962
                $suffix = $match[2];
963
            }
964
965
            if ($this->request->app() && $this->app->config->get('app.auto_multi_app') && !$this->app->http->isBindDomain()) {
966
                $url = $this->request->app() . '/' . $url;
967
            }
968
        } elseif (!empty($rule) && isset($name)) {
969
            throw new \InvalidArgumentException('route name not exists:' . $name);
970
        } else {
971
            // 检测URL绑定
972
            $bind = $this->getDomainBind($domain && is_string($domain) ? $domain : null);
973
974
            if ($bind && 0 === strpos($url, $bind)) {
975
                $url = substr($url, strlen($bind) + 1);
976
            } else {
977
                $binds = $this->getBind();
978
979
                foreach ($binds as $key => $val) {
980
                    if (is_string($val) && 0 === strpos($url, $val) && substr_count($val, '/') > 1) {
981
                        $url    = substr($url, strlen($val) + 1);
982
                        $domain = $key;
983
                        break;
984
                    }
985
                }
986
            }
987
988
            // 路由标识不存在 直接解析
989
            $url = $this->parseUrl($url, $domain);
990
991
            if (isset($info['query'])) {
992
                // 解析地址里面参数 合并到vars
993
                parse_str($info['query'], $params);
994
                $vars = array_merge($params, $vars);
995
            }
996
        }
997
998
        // 还原URL分隔符
999
        $depr = $this->config['pathinfo_depr'];
1000
        $url  = str_replace('/', $depr, $url);
1001
1002
        $file = $this->request->baseFile();
1003
        if ($file && 0 !== strpos($this->request->url(), $file)) {
1004
            $file = str_replace('\\', '/', dirname($file));
1005
        }
1006
1007
        $url = rtrim($file, '/') . '/' . $url;
1008
1009
        // URL后缀
1010
        if ('/' == substr($url, -1) || '' == $url) {
1011
            $suffix = '';
1012
        } else {
1013
            $suffix = $this->parseSuffix($suffix);
1014
        }
1015
1016
        // 锚点
1017
        $anchor = !empty($anchor) ? '#' . $anchor : '';
1018
1019
        // 参数组装
1020
        if (!empty($vars)) {
1021
            // 添加参数
1022
            if ($this->config['url_common_param']) {
1023
                $vars = http_build_query($vars);
1024
                $url .= $suffix . '?' . $vars . $anchor;
1025
            } else {
1026
                foreach ($vars as $var => $val) {
1027
                    if ('' !== $val) {
1028
                        $url .= $depr . $var . $depr . urlencode((string) $val);
1029
                    }
1030
                }
1031
1032
                $url .= $suffix . $anchor;
1033
            }
1034
        } else {
1035
            $url .= $suffix . $anchor;
1036
        }
1037
1038
        // 检测域名
1039
        $domain = $this->parseDomain($url, $domain);
1040
1041
        // URL组装
1042
        return $domain . '/' . ltrim($url, '/');
1043
    }
1044
1045
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $domain should have a doc-comment as per coding-style.
Loading history...
1046
     * 直接解析URL地址
1047
     * @access public
1048
     * @param  string $url URL
1049
     * @return string
1050
     */
1051
    protected function parseUrl(string $url, &$domain): string
1052
    {
1053
        $request = $this->request;
1054
1055
        if (0 === strpos($url, '/')) {
1056
            // 直接作为路由地址解析
1057
            $url = substr($url, 1);
1058
        } elseif (false !== strpos($url, '\\')) {
1059
            // 解析到类
1060
            $url = ltrim(str_replace('\\', '/', $url), '/');
1061
        } elseif (0 === strpos($url, '@')) {
1062
            // 解析到控制器
1063
            $url = substr($url, 1);
1064
        } else {
1065
            // 解析到 应用/控制器/操作
1066
            $app        = $request->app();
1067
            $controller = $request->controller();
1068
1069
            if ('' == $url) {
1070
                $action = $request->action();
1071
            } else {
1072
                $path       = explode('/', $url);
1073
                $action     = array_pop($path);
1074
                $controller = empty($path) ? $controller : array_pop($path);
1075
                $app        = empty($path) ? $app : array_pop($path);
1076
            }
1077
1078
            if ($this->config['url_convert']) {
1079
                $action     = strtolower($action);
1080
                $controller = App::parseName($controller);
1081
            }
1082
1083
            $url = $controller . '/' . $action;
1084
1085
            if ($app && $this->app->config->get('app.auto_multi_app')) {
1086
                $bind = $this->app->config->get('app.domain_bind', []);
1087
                if ($key = array_search($app, $bind)) {
1088
                    $domain = true === $domain ? $key : $domain;
1089
                } else {
1090
                    $map = $this->app->config->get('app.app_map', []);
1091
1092
                    if ($key = array_search($app, $map)) {
1093
                        $url = $key . '/' . $url;
1094
                    } else {
1095
                        $url = $app . '/' . $url;
1096
                    }
1097
                }
1098
            }
1099
        }
1100
1101
        return $url;
1102
    }
1103
1104
    /**
1105
     * 检测域名
1106
     * @access public
1107
     * @param  string      $url URL
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
1108
     * @param  string|true $domain 域名
1109
     * @return string
1110
     */
1111
    protected function parseDomain(string &$url, $domain): string
1112
    {
1113
        if (!$domain) {
1114
            return '';
1115
        }
1116
1117
        $rootDomain = $this->request->rootDomain();
1118
        if (true === $domain) {
1119
            // 自动判断域名
1120
            $domain  = $this->request->host();
1121
            $domains = $this->getDomains();
1122
1123
            if (!empty($domains)) {
1124
                $route_domain = array_keys($domains);
1125
                foreach ($route_domain as $domain_prefix) {
1126
                    if (0 === strpos($domain_prefix, '*.') && strpos($domain, ltrim($domain_prefix, '*.')) !== false) {
1127
                        foreach ($domains as $key => $rule) {
1128
                            $rule = is_array($rule) ? $rule[0] : $rule;
1129
                            if (is_string($rule) && false === strpos($key, '*') && 0 === strpos($url, $rule)) {
1130
                                $url    = ltrim($url, $rule);
1131
                                $domain = $key;
1132
1133
                                // 生成对应子域名
1134
                                if (!empty($rootDomain)) {
1135
                                    $domain .= $rootDomain;
1136
                                }
1137
                                break;
1138
                            } elseif (false !== strpos($key, '*')) {
1139
                                if (!empty($rootDomain)) {
1140
                                    $domain .= $rootDomain;
1141
                                }
1142
1143
                                break;
1144
                            }
1145
                        }
1146
                    }
1147
                }
1148
            }
1149
        } elseif (false === strpos($domain, '.') && 0 !== strpos($domain, $rootDomain)) {
1150
            $domain .= '.' . $rootDomain;
1151
        }
1152
1153
        if (false !== strpos($domain, '://')) {
1154
            $scheme = '';
1155
        } else {
1156
            $scheme = $this->request->isSsl() ? 'https://' : 'http://';
1157
        }
1158
1159
        return $this->request->host() == $domain && !$this->showDomain ? '' : $scheme . $domain;
1160
    }
1161
1162
    /**
1163
     * 解析URL后缀
1164
     * @access public
1165
     * @param  string|bool $suffix 后缀
1166
     * @return string
1167
     */
1168
    protected function parseSuffix($suffix): string
1169
    {
1170
        if ($suffix) {
1171
            $suffix = true === $suffix ? $this->config['url_html_suffix'] : $suffix;
1172
1173
            if ($pos = strpos($suffix, '|')) {
1174
                $suffix = substr($suffix, 0, $pos);
1175
            }
1176
        }
1177
1178
        return (empty($suffix) || 0 === strpos($suffix, '.')) ? (string) $suffix : '.' . $suffix;
1179
    }
1180
1181
    /**
1182
     * 匹配路由地址
1183
     * @access public
1184
     * @param  array $rule 路由规则
0 ignored issues
show
Coding Style introduced by
Expected 8 spaces after parameter name; 1 found
Loading history...
1185
     * @param  array $vars 路由变量
0 ignored issues
show
Coding Style introduced by
Expected 8 spaces after parameter name; 1 found
Loading history...
1186
     * @param  mixed $allowDomain 允许域名
1187
     * @return array
1188
     */
1189
    public function getRuleUrl(array $rule, array &$vars = [], $allowDomain = ''): array
1190
    {
1191
        foreach ($rule as $item) {
1192
            list($url, $pattern, $domain, $suffix) = $item;
1193
1194
            if ('-' == $domain) {
1195
                $domain = $this->host;
1196
            }
1197
1198
            if (is_string($allowDomain) && $domain != $allowDomain) {
1199
                continue;
1200
            }
1201
1202
            if (!in_array($this->request->port(), [80, 443])) {
1203
                $domain .= ':' . $this->request->port();
1204
            }
1205
1206
            if (empty($pattern)) {
1207
                return [rtrim($url, '?/-'), $domain, $suffix];
1208
            }
1209
1210
            $type = $this->config['url_common_param'];
1211
1212
            foreach ($pattern as $key => $val) {
1213
                if (isset($vars[$key])) {
1214
                    $url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key, '<' . $key . '>'], $type ? $vars[$key] : urlencode((string) $vars[$key]), $url);
1215
                    unset($vars[$key]);
1216
                    $url    = str_replace(['/?', '-?'], ['/', '-'], $url);
1217
                    $result = [rtrim($url, '?/-'), $domain, $suffix];
1218
                } elseif (2 == $val) {
1219
                    $url    = str_replace(['/[:' . $key . ']', '[:' . $key . ']', '<' . $key . '?>'], '', $url);
1220
                    $url    = str_replace(['/?', '-?'], ['/', '-'], $url);
1221
                    $result = [rtrim($url, '?/-'), $domain, $suffix];
1222
                } else {
1223
                    break;
1224
                }
1225
            }
1226
1227
            if (isset($result)) {
1228
                return $result;
1229
            }
1230
        }
1231
1232
        return [];
1233
    }
1234
1235
    /**
1236
     * 设置全局的路由分组参数
1237
     * @access public
1238
     * @param string $method 方法名
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1239
     * @param array  $args   调用参数
1 ignored issue
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
1240
     * @return RuleGroup
1241
     */
1242
    public function __call($method, $args)
1243
    {
1244
        return call_user_func_array([$this->group, $method], $args);
1245
    }
1246
}
1247