Passed
Push — 5.2 ( 94f706...1b9ee1 )
by liu
03:53
created

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