Completed
Push — 6.0 ( caeff7...3b655b )
by liu
03:11
created

Route::getRuleUrl()   C

Complexity

Conditions 12
Paths 23

Size

Total Lines 44
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 156

Importance

Changes 0
Metric Value
cc 12
eloc 26
nc 23
nop 3
dl 0
loc 44
ccs 0
cts 26
cp 0
crap 156
rs 6.9666
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 doc comment for class Route
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 RuleGroup
121
     */
122
    protected $group;
123
124
    /**
125
     * 路由绑定
126
     * @var array
127
     */
128
    protected $bind = [];
129
130
    /**
131
     * 域名对象
132
     * @var array
133
     */
134
    protected $domains = [];
135
136
    /**
137
     * 跨域路由规则
138
     * @var RuleGroup
139
     */
140
    protected $cross;
141
142
    /**
143
     * 路由是否延迟解析
144
     * @var bool
145
     */
146
    protected $lazy = true;
147
148
    /**
149
     * 路由是否测试模式
150
     * @var bool
151
     */
152
    protected $isTest = false;
153
154
    /**
155
     * (分组)路由规则是否合并解析
156
     * @var bool
157
     */
158
    protected $mergeRuleRegex = true;
159
160
    /**
161
     * 显示域名
162
     * @var bool
163
     */
164
    protected $showDomain;
165
166
    public function __construct(App $app, Cache $cache)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __construct()
Loading history...
167
    {
168
        $this->app    = $app;
169
        $this->config = array_merge($this->config, $app->config->get('route'));
170
171
        if (!empty($this->config['route_cache_option'])) {
172
            $this->cache = $cache->connect($this->config['route_cache_option']);
173
        } else {
174
            $this->cache = $cache->init();
175
        }
176
177
        if (is_file($app->getRuntimePath() . 'route.php')) {
178
            // 读取路由映射文件
179
            $this->import(include $app->getRuntimePath() . 'route.php');
180
        }
181
182
        $this->setDefaultDomain();
183
184
        $this->ruleName = new RuleName();
185
    }
186
187
    public function config(string $name = null)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function config()
Loading history...
188
    {
189
        if (is_null($name)) {
190
            return $this->config;
191
        }
192
193
        return $this->config[$name] ?? null;
194
    }
195
196
    /**
197
     * 设置路由域名及分组(包括资源路由)是否延迟解析
198
     * @access public
199
     * @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...
200
     * @return $this
201
     */
202
    public function lazy(bool $lazy = true)
203
    {
204
        $this->lazy = $lazy;
205
        return $this;
206
    }
207
208
    /**
209
     * 设置路由为测试模式
210
     * @access public
211
     * @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...
212
     * @return void
213
     */
214
    public function setTestMode(bool $test): void
215
    {
216
        $this->isTest = $test;
217
    }
218
219
    /**
220
     * 检查路由是否为测试模式
221
     * @access public
222
     * @return bool
223
     */
224
    public function isTest(): bool
225
    {
226
        return $this->isTest;
227
    }
228
229
    /**
230
     * 设置路由域名及分组(包括资源路由)是否合并解析
231
     * @access public
232
     * @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...
233
     * @return $this
234
     */
235
    public function mergeRuleRegex(bool $merge = true)
236
    {
237
        $this->mergeRuleRegex = $merge;
238
        $this->group->mergeRuleRegex($merge);
239
240
        return $this;
241
    }
242
243
    /**
244
     * 初始化默认域名
245
     * @access protected
246
     * @return void
247
     */
248
    protected function setDefaultDomain(): void
249
    {
250
        // 注册默认域名
251
        $domain = new Domain($this);
252
253
        $this->domains['-'] = $domain;
254
255
        // 默认分组
256
        $this->group = $domain;
257
    }
258
259
    /**
260
     * 设置当前域名
261
     * @access public
262
     * @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...
263
     * @return void
264
     */
265
    public function setGroup(RuleGroup $group): void
266
    {
267
        $this->group = $group;
268
    }
269
270
    /**
271
     * 获取当前分组
272
     * @access public
273
     * @return RuleGroup
274
     */
275
    public function getGroup(): RuleGroup
276
    {
277
        return $this->group;
278
    }
279
280
    /**
281
     * 注册变量规则
282
     * @access public
283
     * @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...
284
     * @return $this
285
     */
286
    public function pattern(array $pattern)
287
    {
288
        $this->group->pattern($pattern);
289
290
        return $this;
291
    }
292
293
    /**
294
     * 注册路由参数
295
     * @access public
296
     * @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...
297
     * @return $this
298
     */
299
    public function option(array $option)
300
    {
301
        $this->group->option($option);
302
303
        return $this;
304
    }
305
306
    /**
307
     * 注册域名路由
308
     * @access public
309
     * @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...
310
     * @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...
311
     * @return Domain
312
     */
313
    public function domain($name, $rule = null): Domain
314
    {
315
        // 支持多个域名使用相同路由规则
316
        $domainName = is_array($name) ? array_shift($name) : $name;
317
318
        if ('*' != $domainName && false === strpos($domainName, '.')) {
319
            $domainName .= '.' . $this->request->rootDomain();
320
        }
321
322
        if (!isset($this->domains[$domainName])) {
323
            $domain = (new Domain($this, $domainName, $rule))
324
                ->lazy($this->lazy)
325
                ->mergeRuleRegex($this->mergeRuleRegex);
326
327
            $this->domains[$domainName] = $domain;
328
        } else {
329
            $domain = $this->domains[$domainName];
330
            $domain->parseGroupRule($rule);
331
        }
332
333
        if (is_array($name) && !empty($name)) {
334
            $root = $this->request->rootDomain();
335
            foreach ($name as $item) {
336
                if (false === strpos($item, '.')) {
337
                    $item .= '.' . $root;
338
                }
339
340
                $this->domains[$item] = $domainName;
341
            }
342
        }
343
344
        // 返回域名对象
345
        return $domain;
346
    }
347
348
    /**
349
     * 获取域名
350
     * @access public
351
     * @return array
352
     */
353
    public function getDomains(): array
354
    {
355
        return $this->domains;
356
    }
357
358
    /**
359
     * 获取域名
360
     * @access public
361
     * @return array
362
     */
363
    public function getRuleName(): RuleName
364
    {
365
        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...
366
    }
367
368
    /**
369
     * 设置路由绑定
370
     * @access public
371
     * @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...
372
     * @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...
373
     * @return $this
374
     */
375
    public function bind(string $bind, string $domain = null)
376
    {
377
        $domain = is_null($domain) ? '-' : $domain;
378
379
        $this->bind[$domain] = $bind;
380
381
        return $this;
382
    }
383
384
    /**
385
     * 读取路由绑定信息
386
     * @access public
387
     * @return array
388
     */
389
    public function getBind(): array
390
    {
391
        return $this->bind;
392
    }
393
394
    /**
395
     * 读取路由绑定
396
     * @access public
397
     * @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...
398
     * @return string|null
399
     */
400
    public function getDomainBind(string $domain = null)
401
    {
402
        if (is_null($domain)) {
403
            $domain = '-';
404
        } elseif (false === strpos($domain, '.')) {
405
            $domain .= '.' . $this->request->rootDomain();
406
        }
407
408
        $subDomain = $this->request->subDomain();
409
410
        if (strpos($subDomain, '.')) {
411
            $name = '*' . strstr($subDomain, '.');
412
        }
413
414
        if (isset($this->bind[$domain])) {
415
            $result = $this->bind[$domain];
416
        } elseif (isset($name) && isset($this->bind[$name])) {
417
            $result = $this->bind[$name];
418
        } elseif (!empty($subDomain) && isset($this->bind['*'])) {
419
            $result = $this->bind['*'];
420
        } else {
421
            $result = null;
422
        }
423
424
        return $result;
425
    }
426
427
    /**
428
     * 读取路由标识
429
     * @access public
430
     * @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...
431
     * @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...
432
     * @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...
433
     * @return RuleItem[]
434
     */
435
    public function getName(string $name = null, string $domain = null, string $method = '*'): array
436
    {
437
        return $this->ruleName->getName($name, $domain, $method);
438
    }
439
440
    /**
441
     * 批量导入路由标识
442
     * @access public
443
     * @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...
444
     * @return $this
445
     */
446
    public function import(array $name): void
447
    {
448
        $this->ruleName->import($name);
449
    }
450
451
    /**
452
     * 注册路由标识
453
     * @access public
454
     * @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...
455
     * @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...
456
     * @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...
457
     * @return void
458
     */
459
    public function setName(string $name, $value, bool $first = false): void
460
    {
461
        $this->ruleName->setName($name, $value, $first);
462
    }
463
464
    /**
465
     * 保存路由规则
466
     * @access public
467
     * @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...
468
     * @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...
469
     * @return void
470
     */
471
    public function setRule(string $rule, RuleItem $ruleItem = null): void
472
    {
473
        $this->ruleName->setRule($rule, $ruleItem);
0 ignored issues
show
Bug introduced by
It seems like $ruleItem can also be of type null; however, parameter $route 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

473
        $this->ruleName->setRule($rule, /** @scrutinizer ignore-type */ $ruleItem);
Loading history...
474
    }
475
476
    /**
477
     * 读取路由
478
     * @access public
479
     * @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...
480
     * @return RuleItem[]
481
     */
482
    public function getRule(string $rule): array
483
    {
484
        return $this->ruleName->getRule($rule);
485
    }
486
487
    /**
488
     * 读取路由列表
489
     * @access public
490
     * @return array
491
     */
492
    public function getRuleList(): array
493
    {
494
        return $this->ruleName->getRuleList();
495
    }
496
497
    /**
498
     * 清空路由规则
499
     * @access public
500
     * @return void
501
     */
502
    public function clear(): void
503
    {
504
        $this->ruleName->clear();
505
506
        if ($this->group) {
507
            $this->group->clear();
508
        }
509
    }
510
511
    /**
512
     * 注册路由规则
513
     * @access public
514
     * @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...
515
     * @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...
516
     * @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...
517
     * @return RuleItem
518
     */
519
    public function rule(string $rule, $route = null, string $method = '*'): RuleItem
520
    {
521
        return $this->group->addRule($rule, $route, $method);
522
    }
523
524
    /**
525
     * 设置跨域有效路由规则
526
     * @access public
527
     * @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...
528
     * @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...
529
     * @return $this
530
     */
531
    public function setCrossDomainRule(Rule $rule, string $method = '*')
532
    {
533
        if (!isset($this->cross)) {
534
            $this->cross = (new RuleGroup($this))->mergeRuleRegex($this->mergeRuleRegex);
535
        }
536
537
        $this->cross->addRuleItem($rule, $method);
538
539
        return $this;
540
    }
541
542
    /**
543
     * 注册路由分组
544
     * @access public
545
     * @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...
546
     * @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...
547
     * @return RuleGroup
548
     */
549
    public function group($name, $route = null): RuleGroup
550
    {
551
        if ($name instanceof \Closure) {
552
            $route = $name;
553
            $name  = '';
554
        }
555
556
        return (new RuleGroup($this, $this->group, $name, $route))
557
            ->lazy($this->lazy)
558
            ->mergeRuleRegex($this->mergeRuleRegex);
559
    }
560
561
    /**
562
     * 注册路由
563
     * @access public
564
     * @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...
565
     * @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...
566
     * @return RuleItem
567
     */
568
    public function any(string $rule, $route): RuleItem
569
    {
570
        return $this->rule($rule, $route, '*');
571
    }
572
573
    /**
574
     * 注册GET路由
575
     * @access public
576
     * @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...
577
     * @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...
578
     * @return RuleItem
579
     */
580
    public function get(string $rule, $route): RuleItem
581
    {
582
        return $this->rule($rule, $route, 'GET');
583
    }
584
585
    /**
586
     * 注册POST路由
587
     * @access public
588
     * @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...
589
     * @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...
590
     * @return RuleItem
591
     */
592
    public function post(string $rule, $route): RuleItem
593
    {
594
        return $this->rule($rule, $route, 'POST');
595
    }
596
597
    /**
598
     * 注册PUT路由
599
     * @access public
600
     * @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...
601
     * @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...
602
     * @return RuleItem
603
     */
604
    public function put(string $rule, $route): RuleItem
605
    {
606
        return $this->rule($rule, $route, 'PUT');
607
    }
608
609
    /**
610
     * 注册DELETE路由
611
     * @access public
612
     * @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...
613
     * @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...
614
     * @return RuleItem
615
     */
616
    public function delete(string $rule, $route): RuleItem
617
    {
618
        return $this->rule($rule, $route, 'DELETE');
619
    }
620
621
    /**
622
     * 注册PATCH路由
623
     * @access public
624
     * @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...
625
     * @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...
626
     * @return RuleItem
627
     */
628
    public function patch(string $rule, $route): RuleItem
629
    {
630
        return $this->rule($rule, $route, 'PATCH');
631
    }
632
633
    /**
634
     * 注册资源路由
635
     * @access public
636
     * @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...
637
     * @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...
638
     * @return Resource
639
     */
640
    public function resource(string $rule, string $route): Resource
641
    {
642
        return (new Resource($this, $this->group, $rule, $route, $this->rest))
643
            ->lazy($this->lazy);
644
    }
645
646
    /**
647
     * 注册视图路由
648
     * @access public
649
     * @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...
650
     * @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...
651
     * @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...
652
     * @return RuleItem
653
     */
654
    public function view(string $rule, string $template = '', array $vars = []): RuleItem
655
    {
656
        return $this->rule($rule, $template, 'GET')->view($vars);
657
    }
658
659
    /**
660
     * 注册重定向路由
661
     * @access public
662
     * @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...
663
     * @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...
664
     * @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...
665
     * @return RuleItem
666
     */
667
    public function redirect(string $rule, string $route = '', int $status = 301): RuleItem
668
    {
669
        return $this->rule($rule, $route, '*')->redirect()->status($status);
670
    }
671
672
    /**
673
     * rest方法定义和修改
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
674
     * @access public
675
     * @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...
676
     * @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...
677
     * @return $this
678
     */
679
    public function rest($name, $resource = [])
680
    {
681
        if (is_array($name)) {
682
            $this->rest = $resource ? $name : array_merge($this->rest, $name);
683
        } else {
684
            $this->rest[$name] = $resource;
685
        }
686
687
        return $this;
688
    }
689
690
    /**
691
     * 获取rest方法定义的参数
692
     * @access public
693
     * @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...
694
     * @return array|null
695
     */
696
    public function getRest(string $name = null)
697
    {
698
        if (is_null($name)) {
699
            return $this->rest;
700
        }
701
702
        return $this->rest[$name] ?? null;
703
    }
704
705
    /**
706
     * 注册未匹配路由规则后的处理
707
     * @access public
708
     * @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...
709
     * @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...
710
     * @return RuleItem
711
     */
712
    public function miss($route, string $method = '*'): RuleItem
713
    {
714
        return $this->group->miss($route, $method);
715
    }
716
717
    /**
718
     * 路由调度
719
     * @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...
720
     * @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...
721
     * @return Response
722
     */
723
    public function dispatch(Request $request, $withRoute = null)
724
    {
725
        $this->request = $request;
726
        $this->host    = $this->request->host(true);
727
728
        if ($withRoute) {
729
            $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...
730
                //加载路由
731
                $withRoute();
732
                return $this->check();
733
            };
734
735
            if ($this->config['route_check_cache']) {
736
                $dispatch = $this->cache
737
                    ->tag('route_cache')
738
                    ->remember($this->getRouteCacheKey($request), $checkCallback);
739
            } else {
740
                $dispatch = $checkCallback();
741
            }
742
        } else {
743
            $dispatch = $this->url($request->path());
744
        }
745
746
        $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...
747
            try {
748
                $response = $dispatch->setApp($this->app)->run();
749
            } catch (HttpResponseException $exception) {
750
                $response = $exception->getResponse();
751
            }
752
            return $response;
753
        });
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...
754
755
        return $this->app->middleware->dispatch($request);
756
    }
757
758
    /**
759
     * 获取路由缓存Key
760
     * @access protected
761
     * @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...
762
     * @return string
763
     */
764
    protected function getRouteCacheKey(Request $request): string
765
    {
766
        if (!empty($this->config['route_check_cache_key'])) {
767
            $closure  = $this->config['route_check_cache_key'];
768
            $routeKey = $closure($request);
769
        } else {
770
            $routeKey = md5($request->baseUrl(true) . ':' . $request->method());
771
        }
772
773
        return $routeKey;
774
    }
775
776
    /**
777
     * 检测URL路由
778
     * @access public
779
     * @return Dispatch
780
     * @throws RouteNotFoundException
781
     */
782
    public function check(): Dispatch
783
    {
784
        // 自动检测域名路由
785
        $url = str_replace($this->config['pathinfo_depr'], '|', $this->request->path());
786
787
        $completeMatch = $this->config['route_complete_match'];
788
789
        $result = $this->checkDomain()->check($this->request, $url, $completeMatch);
790
791
        if (false === $result && !empty($this->cross)) {
792
            // 检测跨域路由
793
            $result = $this->cross->check($this->request, $url, $completeMatch);
794
        }
795
796
        if (false !== $result) {
797
            return $result;
798
        } elseif ($this->config['url_route_must']) {
799
            throw new RouteNotFoundException();
800
        }
801
802
        return $this->url($url);
803
    }
804
805
    /**
806
     * 默认URL解析
807
     * @access public
808
     * @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...
809
     * @return Dispatch
810
     */
811
    public function url(string $url): UrlDispatch
812
    {
813
        return new UrlDispatch($this->request, $this->group, $url);
814
    }
815
816
    /**
817
     * 检测域名的路由规则
818
     * @access protected
819
     * @return Domain
820
     */
821
    protected function checkDomain(): Domain
822
    {
823
        // 获取当前子域名
824
        $subDomain = $this->request->subDomain();
825
826
        $item = false;
827
828
        if ($subDomain && count($this->domains) > 1) {
829
            $domain  = explode('.', $subDomain);
830
            $domain2 = array_pop($domain);
831
832
            if ($domain) {
833
                // 存在三级域名
834
                $domain3 = array_pop($domain);
835
            }
836
837
            if ($subDomain && isset($this->domains[$subDomain])) {
838
                // 子域名配置
839
                $item = $this->domains[$subDomain];
840
            } elseif (isset($this->domains['*.' . $domain2]) && !empty($domain3)) {
841
                // 泛三级域名
842
                $item      = $this->domains['*.' . $domain2];
843
                $panDomain = $domain3;
844
            } elseif (isset($this->domains['*']) && !empty($domain2)) {
845
                // 泛二级域名
846
                if ('www' != $domain2) {
847
                    $item      = $this->domains['*'];
848
                    $panDomain = $domain2;
849
                }
850
            }
851
852
            if (isset($panDomain)) {
853
                // 保存当前泛域名
854
                $this->request->setPanDomain($panDomain);
855
            }
856
        }
857
858
        if (false === $item) {
859
            // 检测全局域名规则
860
            $item = $this->domains['-'];
861
        }
862
863
        if (is_string($item)) {
864
            $item = $this->domains[$item];
865
        }
866
867
        return $item;
868
    }
869
870
    /**
871
     * URL生成 支持路由反射
872
     * @access public
873
     * @param  string       $url 路由地址
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
874
     * @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...
875
     * @param  string|bool  $suffix 伪静态后缀,默认为true表示获取配置值
876
     * @param  bool|string  $domain 是否显示域名 或者直接传入域名
877
     * @return string
878
     */
879
    public function buildUrl(string $url = '', $vars = [], $suffix = true, $domain = false): string
880
    {
881
        // 解析URL
882
        if (0 === strpos($url, '[') && $pos = strpos($url, ']')) {
883
            // [name] 表示使用路由命名标识生成URL
884
            $name = substr($url, 1, $pos - 1);
885
            $url  = 'name' . substr($url, $pos + 1);
886
        }
887
888
        if (false === strpos($url, '://') && 0 !== strpos($url, '/')) {
889
            $info = parse_url($url);
890
            $url  = !empty($info['path']) ? $info['path'] : '';
891
892
            if (isset($info['fragment'])) {
893
                // 解析锚点
894
                $anchor = $info['fragment'];
895
896
                if (false !== strpos($anchor, '?')) {
897
                    // 解析参数
898
                    list($anchor, $info['query']) = explode('?', $anchor, 2);
899
                }
900
901
                if (false !== strpos($anchor, '@')) {
902
                    // 解析域名
903
                    list($anchor, $domain) = explode('@', $anchor, 2);
904
                }
905
            } elseif (strpos($url, '@') && false === strpos($url, '\\')) {
906
                // 解析域名
907
                list($url, $domain) = explode('@', $url, 2);
908
            }
909
        }
910
911
        $this->showDomain = false === $domain ? false : true;
912
913
        // 解析参数
914
        if (is_string($vars)) {
915
            // aaa=1&bbb=2 转换成数组
916
            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

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