Passed
Push — 5.2 ( 54339d...f3e95d )
by liu
02:34
created

Route::getBind()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
// +----------------------------------------------------------------------
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 think\exception\RouteNotFoundException;
16
use think\facade\RuleName;
17
use think\route\Dispatch;
18
use think\route\dispatch\Url as UrlDispatch;
19
use think\route\Domain;
20
use think\route\Resource;
21
use think\route\Rule;
22
use think\route\RuleGroup;
23
use think\route\RuleItem;
24
25
class Route
26
{
27
    /**
28
     * REST定义
29
     * @var array
30
     */
31
    protected $rest = [
32
        'index'  => ['get', '', 'index'],
33
        'create' => ['get', '/create', 'create'],
34
        'edit'   => ['get', '/<id>/edit', 'edit'],
35
        'read'   => ['get', '/<id>', 'read'],
36
        'save'   => ['post', '', 'save'],
37
        'update' => ['put', '/<id>', 'update'],
38
        'delete' => ['delete', '/<id>', 'delete'],
39
    ];
40
41
    /**
42
     * 配置参数
43
     * @var array
44
     */
45
    protected $config = [
46
        // pathinfo分隔符
47
        'pathinfo_depr'         => '/',
48
        // 是否开启路由延迟解析
49
        'url_lazy_route'        => false,
50
        // 是否强制使用路由
51
        'url_route_must'        => false,
52
        // 合并路由规则
53
        'route_rule_merge'      => false,
54
        // 路由是否完全匹配
55
        'route_complete_match'  => false,
56
        // 使用注解路由
57
        'route_annotation'      => false,
58
        // 是否自动转换URL中的控制器和操作名
59
        'url_convert'           => true,
60
        // 默认的路由变量规则
61
        'default_route_pattern' => '\w+',
62
        // 默认输出类型
63
        'default_return_type'   => 'html',
64
        // 默认AJAX 数据返回格式,可选json xml ...
65
        'default_ajax_return'   => 'json',
66
        // 默认控制器名
67
        'default_controller'    => 'Index',
68
        // 默认操作名
69
        'default_action'        => 'index',
70
        // 操作方法后缀
71
        'action_suffix'         => '',
72
    ];
73
74
    /**
75
     * 请求对象
76
     * @var Request
77
     */
78
    protected $request;
79
80
    /**
81
     * 当前HOST
82
     * @var string
83
     */
84
    protected $host;
85
86
    /**
87
     * 当前域名
88
     * @var string
89
     */
90
    protected $domain;
91
92
    /**
93
     * 当前分组对象
94
     * @var RuleGroup
95
     */
96
    protected $group;
97
98
    /**
99
     * 路由绑定
100
     * @var array
101
     */
102
    protected $bind = [];
103
104
    /**
105
     * 域名对象
106
     * @var array
107
     */
108
    protected $domains = [];
109
110
    /**
111
     * 跨域路由规则
112
     * @var RuleGroup
113
     */
114
    protected $cross;
115
116
    /**
117
     * 路由是否延迟解析
118
     * @var bool
119
     */
120
    protected $lazy = true;
121
122
    /**
123
     * 路由是否测试模式
124
     * @var bool
125
     */
126
    protected $isTest = false;
127
128
    /**
129
     * (分组)路由规则是否合并解析
130
     * @var bool
131
     */
132
    protected $mergeRuleRegex = true;
133
134
    public function __construct(Request $request, array $config = [])
135
    {
136
        $this->request = $request;
137
        $this->config  = array_merge($this->config, $config);
138
        $this->host    = $this->request->host(true);
139
140
        $this->setDefaultDomain();
141
    }
142
143
    public function config(string $name = null)
144
    {
145
        return $this->config[$name] ?? null;
146
    }
147
148
    public static function __make(Request $request, Config $config)
149
    {
150
        $config = $config->get('route');
151
        return new static($request, $config);
0 ignored issues
show
Bug introduced by
It seems like $config can also be of type null; however, parameter $config of think\Route::__construct() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

151
        return new static($request, /** @scrutinizer ignore-type */ $config);
Loading history...
152
    }
153
154
    /**
155
     * 设置路由的请求对象实例
156
     * @access public
157
     * @param  Request     $request   请求对象实例
158
     * @return void
159
     */
160
    public function setRequest(Request $request): void
161
    {
162
        $this->request = $request;
163
    }
164
165
    /**
166
     * 设置路由域名及分组(包括资源路由)是否延迟解析
167
     * @access public
168
     * @param  bool     $lazy   路由是否延迟解析
169
     * @return $this
170
     */
171
    public function lazy(bool $lazy = true)
172
    {
173
        $this->lazy = $lazy;
174
        return $this;
175
    }
176
177
    /**
178
     * 设置路由为测试模式
179
     * @access public
180
     * @param  bool     $test   路由是否测试模式
181
     * @return void
182
     */
183
    public function setTestMode(bool $test): void
184
    {
185
        $this->isTest = $test;
186
    }
187
188
    /**
189
     * 检查路由是否为测试模式
190
     * @access public
191
     * @return bool
192
     */
193
    public function isTest(): bool
194
    {
195
        return $this->isTest;
196
    }
197
198
    /**
199
     * 设置路由域名及分组(包括资源路由)是否合并解析
200
     * @access public
201
     * @param  bool     $merge   路由是否合并解析
202
     * @return $this
203
     */
204
    public function mergeRuleRegex(bool $merge = true)
205
    {
206
        $this->mergeRuleRegex = $merge;
207
        $this->group->mergeRuleRegex($merge);
208
209
        return $this;
210
    }
211
212
    /**
213
     * 初始化默认域名
214
     * @access protected
215
     * @return void
216
     */
217
    protected function setDefaultDomain(): void
218
    {
219
        // 默认域名
220
        $this->domain = $this->host;
221
222
        // 注册默认域名
223
        $domain = new Domain($this, $this->host);
224
225
        $this->domains[$this->host] = $domain;
226
227
        // 默认分组
228
        $this->group = $domain;
229
    }
230
231
    /**
232
     * 设置当前域名
233
     * @access public
234
     * @param  RuleGroup    $group 域名
235
     * @return void
236
     */
237
    public function setGroup(RuleGroup $group): void
238
    {
239
        $this->group = $group;
240
    }
241
242
    /**
243
     * 获取当前分组
244
     * @access public
245
     * @return RuleGroup
246
     */
247
    public function getGroup(): RuleGroup
248
    {
249
        return $this->group;
250
    }
251
252
    /**
253
     * 注册变量规则
254
     * @access public
255
     * @param  array  $pattern 变量规则
256
     * @return $this
257
     */
258
    public function pattern(array $pattern)
259
    {
260
        $this->group->pattern($pattern);
261
262
        return $this;
263
    }
264
265
    /**
266
     * 注册路由参数
267
     * @access public
268
     * @param  array  $option  参数
269
     * @return $this
270
     */
271
    public function option(array $option)
272
    {
273
        $this->group->option($option);
274
275
        return $this;
276
    }
277
278
    /**
279
     * 注册域名路由
280
     * @access public
281
     * @param  string|array  $name 子域名
282
     * @param  mixed         $rule 路由规则
283
     * @return Domain
284
     */
285
    public function domain($name, $rule = null): Domain
286
    {
287
        // 支持多个域名使用相同路由规则
288
        $domainName = is_array($name) ? array_shift($name) : $name;
289
290
        if ('*' != $domainName && false === strpos($domainName, '.')) {
291
            $domainName .= '.' . $this->request->rootDomain();
292
        }
293
294
        if (!isset($this->domains[$domainName])) {
295
            $domain = (new Domain($this, $domainName, $rule))
296
                ->lazy($this->lazy)
297
                ->mergeRuleRegex($this->mergeRuleRegex);
298
299
            $this->domains[$domainName] = $domain;
300
        } else {
301
            $domain = $this->domains[$domainName];
302
            $domain->parseGroupRule($rule);
303
        }
304
305
        if (is_array($name) && !empty($name)) {
306
            $root = $this->request->rootDomain();
307
            foreach ($name as $item) {
308
                if (false === strpos($item, '.')) {
309
                    $item .= '.' . $root;
310
                }
311
312
                $this->domains[$item] = $domainName;
313
            }
314
        }
315
316
        // 返回域名对象
317
        return $domain;
318
    }
319
320
    /**
321
     * 获取域名
322
     * @access public
323
     * @return array
324
     */
325
    public function getDomains(): array
326
    {
327
        return $this->domains;
328
    }
329
330
    /**
331
     * 设置路由绑定
332
     * @access public
333
     * @param  string     $bind 绑定信息
334
     * @param  string     $domain 域名
335
     * @return $this
336
     */
337
    public function bind(string $bind, string $domain = null)
338
    {
339
        $domain = is_null($domain) ? $this->domain : $domain;
340
341
        $this->bind[$domain] = $bind;
342
343
        return $this;
344
    }
345
346
    /**
347
     * 读取路由绑定信息
348
     * @access public
349
     * @return array
350
     */
351
    public function getBind(): array
352
    {
353
        return $this->bind;
354
    }
355
356
    /**
357
     * 读取路由绑定
358
     * @access public
359
     * @param  string    $domain 域名
360
     * @return string|null
361
     */
362
    public function getDomainBind(string $domain = null)
363
    {
364
        if (is_null($domain)) {
365
            $domain = $this->domain;
366
        } elseif (false === strpos($domain, '.')) {
367
            $domain .= '.' . $this->request->rootDomain();
368
        }
369
370
        $subDomain = $this->request->subDomain();
371
372
        if (strpos($subDomain, '.')) {
373
            $name = '*' . strstr($subDomain, '.');
374
        }
375
376
        if (isset($this->bind[$domain])) {
377
            $result = $this->bind[$domain];
378
        } elseif (isset($name) && isset($this->bind[$name])) {
379
            $result = $this->bind[$name];
380
        } elseif (!empty($subDomain) && isset($this->bind['*'])) {
381
            $result = $this->bind['*'];
382
        } else {
383
            $result = null;
384
        }
385
386
        return $result;
387
    }
388
389
    /**
390
     * 读取路由标识
391
     * @access public
392
     * @param  string    $name 路由标识
393
     * @param  string    $domain 域名
394
     * @param  string    $method 请求类型
395
     * @return array
396
     */
397
    public function getName(string $name = null, string $domain = null, string $method = '*'): array
398
    {
399
        return RuleName::getName($name, $domain, $method);
0 ignored issues
show
Unused Code introduced by
The call to think\facade\RuleName::getName() has too many arguments starting with $name. ( Ignorable by Annotation )

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

399
        return RuleName::/** @scrutinizer ignore-call */ getName($name, $domain, $method);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
400
    }
401
402
    /**
403
     * 批量导入路由标识
404
     * @access public
405
     * @param  array    $name 路由标识
406
     * @return $this
407
     */
408
    public function setName(array $name)
409
    {
410
        RuleName::import($name);
411
        return $this;
412
    }
413
414
    /**
415
     * 读取路由
416
     * @access public
417
     * @param  string    $rule 路由规则
418
     * @param  string    $domain 域名
419
     * @return array
420
     */
421
    public function getRule(string $rule, string $domain = null): array
422
    {
423
        if (is_null($domain)) {
424
            $domain = $this->domain;
425
        }
426
427
        return RuleName::getRule($rule, $domain);
428
    }
429
430
    /**
431
     * 读取路由列表
432
     * @access public
433
     * @param  string    $domain 域名
434
     * @return array
435
     */
436
    public function getRuleList(string $domain = null): array
437
    {
438
        return RuleName::getRuleList($domain);
439
    }
440
441
    /**
442
     * 清空路由规则
443
     * @access public
444
     * @return void
445
     */
446
    public function clear(): void
447
    {
448
        RuleName::clear();
449
        $this->group->clear();
450
    }
451
452
    /**
453
     * 注册路由规则
454
     * @access public
455
     * @param  string    $rule       路由规则
456
     * @param  mixed     $route      路由地址
457
     * @param  string    $method     请求类型
458
     * @return RuleItem
459
     */
460
    public function rule(string $rule, $route = null, string $method = '*'): RuleItem
461
    {
462
        return $this->group->addRule($rule, $route, $method);
463
    }
464
465
    /**
466
     * 设置跨域有效路由规则
467
     * @access public
468
     * @param  Rule      $rule      路由规则
469
     * @param  string    $method    请求类型
470
     * @return $this
471
     */
472
    public function setCrossDomainRule(Rule $rule, string $method = '*')
473
    {
474
        if (!isset($this->cross)) {
475
            $this->cross = (new RuleGroup($this))->mergeRuleRegex($this->mergeRuleRegex);
476
        }
477
478
        $this->cross->addRuleItem($rule, $method);
479
480
        return $this;
481
    }
482
483
    /**
484
     * 注册路由分组
485
     * @access public
486
     * @param  string|\Closure    $name       分组名称或者参数
487
     * @param  mixed              $route      分组路由
488
     * @return RuleGroup
489
     */
490
    public function group($name, $route = null): RuleGroup
491
    {
492
        if ($name instanceof \Closure) {
493
            $route = $name;
494
            $name  = '';
495
        }
496
497
        return (new RuleGroup($this, $this->group, $name, $route))
498
            ->lazy($this->lazy)
499
            ->mergeRuleRegex($this->mergeRuleRegex);
500
    }
501
502
    /**
503
     * 注册路由
504
     * @access public
505
     * @param  string    $rule 路由规则
506
     * @param  mixed     $route 路由地址
507
     * @return RuleItem
508
     */
509
    public function any(string $rule, $route): RuleItem
510
    {
511
        return $this->rule($rule, $route, '*');
512
    }
513
514
    /**
515
     * 注册GET路由
516
     * @access public
517
     * @param  string    $rule 路由规则
518
     * @param  mixed     $route 路由地址
519
     * @return RuleItem
520
     */
521
    public function get(string $rule, $route): RuleItem
522
    {
523
        return $this->rule($rule, $route, 'GET');
524
    }
525
526
    /**
527
     * 注册POST路由
528
     * @access public
529
     * @param  string    $rule 路由规则
530
     * @param  mixed     $route 路由地址
531
     * @return RuleItem
532
     */
533
    public function post(string $rule, $route): RuleItem
534
    {
535
        return $this->rule($rule, $route, 'POST');
536
    }
537
538
    /**
539
     * 注册PUT路由
540
     * @access public
541
     * @param  string    $rule 路由规则
542
     * @param  mixed     $route 路由地址
543
     * @return RuleItem
544
     */
545
    public function put(string $rule, $route): RuleItem
546
    {
547
        return $this->rule($rule, $route, 'PUT');
548
    }
549
550
    /**
551
     * 注册DELETE路由
552
     * @access public
553
     * @param  string    $rule 路由规则
554
     * @param  mixed     $route 路由地址
555
     * @return RuleItem
556
     */
557
    public function delete(string $rule, $route): RuleItem
558
    {
559
        return $this->rule($rule, $route, 'DELETE');
560
    }
561
562
    /**
563
     * 注册PATCH路由
564
     * @access public
565
     * @param  string    $rule 路由规则
566
     * @param  mixed     $route 路由地址
567
     * @return RuleItem
568
     */
569
    public function patch(string $rule, $route): RuleItem
570
    {
571
        return $this->rule($rule, $route, 'PATCH');
572
    }
573
574
    /**
575
     * 注册资源路由
576
     * @access public
577
     * @param  string    $rule 路由规则
578
     * @param  string    $route 路由地址
579
     * @return Resource
580
     */
581
    public function resource(string $rule, string $route): Resource
582
    {
583
        return (new Resource($this, $this->group, $rule, $route, $this->rest))
584
            ->lazy($this->lazy);
585
    }
586
587
    /**
588
     * 注册视图路由
589
     * @access public
590
     * @param  string|array $rule 路由规则
591
     * @param  string       $template 路由模板地址
592
     * @param  array        $vars 模板变量
593
     * @return RuleItem
594
     */
595
    public function view(string $rule, string $template = '', array $vars = []): RuleItem
596
    {
597
        return $this->rule($rule, $template, 'GET')->view($vars);
598
    }
599
600
    /**
601
     * 注册重定向路由
602
     * @access public
603
     * @param  string|array $rule 路由规则
604
     * @param  string       $route 路由地址
605
     * @param  array        $status 状态码
606
     * @return RuleItem
607
     */
608
    public function redirect(string $rule, string $route = '', int $status = 301): RuleItem
609
    {
610
        return $this->rule($rule, $route, '*')->redirect()->status($status);
611
    }
612
613
    /**
614
     * rest方法定义和修改
615
     * @access public
616
     * @param  string|array  $name 方法名称
617
     * @param  array|bool    $resource 资源
618
     * @return $this
619
     */
620
    public function rest($name, $resource = [])
621
    {
622
        if (is_array($name)) {
623
            $this->rest = $resource ? $name : array_merge($this->rest, $name);
624
        } else {
625
            $this->rest[$name] = $resource;
626
        }
627
628
        return $this;
629
    }
630
631
    /**
632
     * 获取rest方法定义的参数
633
     * @access public
634
     * @param  string        $name 方法名称
635
     * @return array|null
636
     */
637
    public function getRest(string $name = null)
638
    {
639
        if (is_null($name)) {
640
            return $this->rest;
641
        }
642
643
        return $this->rest[$name] ?? null;
644
    }
645
646
    /**
647
     * 注册未匹配路由规则后的处理
648
     * @access public
649
     * @param  string    $route 路由地址
650
     * @param  string    $method 请求类型
651
     * @return RuleItem
652
     */
653
    public function miss(string $route, string $method = '*'): RuleItem
654
    {
655
        return $this->group->miss($route, $method);
656
    }
657
658
    /**
659
     * 注册一个自动解析的URL路由
660
     * @access public
661
     * @param  string    $route 路由地址
662
     * @return void
663
     */
664
    public function auto(string $route): void
665
    {
666
        $this->group->addAutoRule($route);
667
    }
668
669
    /**
670
     * 检测URL路由
671
     * @access public
672
     * @param  string    $url URL地址
673
     * @return Dispatch
674
     * @throws RouteNotFoundException
675
     */
676
    public function check(string $url): Dispatch
677
    {
678
        // 自动检测域名路由
679
        $url = str_replace($this->config['pathinfo_depr'], '|', $url);
680
681
        $completeMatch = $this->config['route_complete_match'];
682
683
        $result = $this->checkDomain()->check($this->request, $url, $completeMatch);
684
685
        if (false === $result && !empty($this->cross)) {
686
            // 检测跨域路由
687
            $result = $this->cross->check($this->request, $url, $completeMatch);
688
        }
689
690
        if (false !== $result) {
691
            return $result;
692
        } elseif ($this->config['url_route_must']) {
693
            throw new RouteNotFoundException();
694
        }
695
696
        return $this->url($url);
697
    }
698
699
    /**
700
     * 默认URL解析
701
     * @access public
702
     * @param  string    $url URL地址
703
     * @return Dispatch
704
     */
705
    public function url(string $url): UrlDispatch
706
    {
707
        return new UrlDispatch($this->request, $this->group, $url);
708
    }
709
710
    /**
711
     * 检测域名的路由规则
712
     * @access protected
713
     * @return Domain
714
     */
715
    protected function checkDomain(): Domain
716
    {
717
        // 获取当前子域名
718
        $subDomain = $this->request->subDomain();
719
720
        $item = false;
721
722
        if ($subDomain && count($this->domains) > 1) {
723
            $domain  = explode('.', $subDomain);
724
            $domain2 = array_pop($domain);
725
726
            if ($domain) {
727
                // 存在三级域名
728
                $domain3 = array_pop($domain);
729
            }
730
731
            if ($subDomain && isset($this->domains[$subDomain])) {
732
                // 子域名配置
733
                $item = $this->domains[$subDomain];
734
            } elseif (isset($this->domains['*.' . $domain2]) && !empty($domain3)) {
735
                // 泛三级域名
736
                $item      = $this->domains['*.' . $domain2];
737
                $panDomain = $domain3;
738
            } elseif (isset($this->domains['*']) && !empty($domain2)) {
739
                // 泛二级域名
740
                if ('www' != $domain2) {
741
                    $item      = $this->domains['*'];
742
                    $panDomain = $domain2;
743
                }
744
            }
745
746
            if (isset($panDomain)) {
747
                // 保存当前泛域名
748
                $this->request->setPanDomain($panDomain);
749
            }
750
        }
751
752
        if (false === $item) {
753
            // 检测当前完整域名
754
            $item = $this->domains[$this->host];
755
        }
756
757
        if (is_string($item)) {
758
            $item = $this->domains[$item];
759
        }
760
761
        return $item;
762
    }
763
764
    /**
765
     * 设置全局的路由分组参数
766
     * @access public
767
     * @param  string    $method     方法名
768
     * @param  array     $args       调用参数
769
     * @return RuleGroup
770
     */
771
    public function __call($method, $args)
772
    {
773
        return call_user_func_array([$this->group, $method], $args);
774
    }
775
}
776