Completed
Push — 6.0 ( cb9c04...e3fbef )
by liu
03:49
created

Route::path()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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

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