Passed
Push — 8.0 ( 790d0e...d6540b )
by liu
11:17
created

Rule   F

Complexity

Total Complexity 136

Size/Duplication

Total Lines 959
Duplicated Lines 0 %

Test Coverage

Coverage 34.33%

Importance

Changes 15
Bugs 0 Features 0
Metric Value
eloc 239
c 15
b 0
f 0
dl 0
loc 959
ccs 92
cts 268
cp 0.3433
rs 2
wmc 136

54 Methods

Rating   Name   Duplication   Size   Complexity  
A config() 0 3 1
A model() 0 13 4
A method() 0 3 1
A option() 0 5 1
A getParent() 0 3 1
A getRouter() 0 3 1
A append() 0 5 1
A getMethod() 0 3 1
A getVars() 0 3 1
A middleware() 0 11 4
A getRule() 0 3 1
A getDomain() 0 3 2
A pattern() 0 5 1
A caseUrl() 0 3 1
A filter() 0 5 1
A setOption() 0 5 1
A getRoute() 0 3 1
A getPattern() 0 13 3
A regex() 0 5 1
A getOption() 0 22 6
A name() 0 5 1
A ext() 0 3 1
A denyExt() 0 3 1
A getName() 0 3 2
A domain() 0 4 1
A validate() 0 5 1
A view() 0 3 1
A dispatchMethod() 0 12 3
A removeSlash() 0 3 1
B buildRuleRegex() 0 28 9
A allowCrossDomain() 0 3 1
A withoutMiddleware() 0 5 1
A depr() 0 3 1
B parseRule() 0 48 10
A parseUrlPath() 0 14 2
B dispatch() 0 23 9
A match() 0 3 1
A crossDomainRule() 0 4 1
A parseUrlParams() 0 6 2
A token() 0 3 1
A completeMatch() 0 3 1
A dispatchController() 0 9 2
A mergeOptions() 0 4 1
A __debugInfo() 0 10 1
A cache() 0 3 1
A __wakeup() 0 3 1
A ajax() 0 3 1
A __sleep() 0 3 1
A __call() 0 8 2
A pjax() 0 3 1
D checkOption() 0 54 29
B buildNameRegex() 0 38 9
A json() 0 3 1
A https() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Rule often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Rule, and based on these observations, apply Extract Interface, too.

1
<?php
2
// +----------------------------------------------------------------------
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2023 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\route;
14
15
use Closure;
16
use think\Container;
17
use think\middleware\AllowCrossDomain;
18
use think\middleware\CheckRequestCache;
19
use think\middleware\FormTokenCheck;
20
use think\Request;
21
use think\Route;
22
use think\route\dispatch\Callback as CallbackDispatch;
23
use think\route\dispatch\Controller as ControllerDispatch;
24
25
/**
26
 * 路由规则基础类
27
 */
28
abstract class Rule
29
{
30
    /**
31
     * 路由标识
32
     * @var string
33
     */
34
    protected $name;
35
36
    /**
37
     * 所在域名
38
     * @var string
39
     */
40
    protected $domain;
41
42
    /**
43
     * 路由对象
44
     * @var Route
45
     */
46
    protected $router;
47
48
    /**
49
     * 路由所属分组
50
     * @var RuleGroup
51
     */
52
    protected $parent;
53
54
    /**
55
     * 路由规则
56
     * @var mixed
57
     */
58
    protected $rule;
59
60
    /**
61
     * 路由地址
62
     * @var string|Closure
63
     */
64
    protected $route;
65
66
    /**
67
     * 请求类型
68
     * @var string
69
     */
70
    protected $method = '*';
71
72
    /**
73
     * 路由变量
74
     * @var array
75
     */
76
    protected $vars = [];
77
78
    /**
79
     * 路由参数
80
     * @var array
81
     */
82
    protected $option = [];
83
84
    /**
85
     * 路由变量规则
86
     * @var array
87
     */
88
    protected $pattern = [];
89
90
    /**
91
     * 预定义变量规则
92
     * @var array
93
     */
94
    protected $regex = [
95
        'int'       => '\d+',
96
        'float'     => '\d+\.\d+',
97
        'alpha'     => '[A-Za-z]+',
98
        'alphaNum'  => '[A-Za-z0-9]+',
99
        'alphaDash' => '[A-Za-z0-9\-\_]+',
100
    ];
101
102
    /**
103
     * 需要和分组合并的路由参数
104
     * @var array
105
     */
106
    protected $mergeOptions = ['model', 'append', 'middleware'];
107
108
    abstract public function check(Request $request, string $url, bool $completeMatch = false);
109
110
    /**
111
     * 设置路由参数
112
     * @access public
113
     * @param  array $option 参数
114
     * @return $this
115
     */
116
    public function option(array $option)
117
    {
118
        $this->option = array_merge($this->option, $option);
119
120
        return $this;
121
    }
122
123
    /**
124
     * 设置单个路由参数
125
     * @access public
126
     * @param  string $name  参数名
127
     * @param  mixed  $value 值
128
     * @return $this
129
     */
130 27
    public function setOption(string $name, $value)
131
    {
132 27
        $this->option[$name] = $value;
133
134 27
        return $this;
135
    }
136
137
    /**
138
     * 注册变量规则
139
     * @access public
140
     * @param  array $regex 变量规则
141
     * @return $this
142
     */
143
    public function regex(array $regex)
144
    {
145
        $this->regex = array_merge($this->regex, $regex);
146
147
        return $this;
148
    }
149
150
    /**
151
     * 注册变量规则
152
     * @access public
153
     * @param  array $pattern 变量规则
154
     * @return $this
155
     */
156
    public function pattern(array $pattern)
157
    {
158
        $this->pattern = array_merge($this->pattern, $pattern);
159
160
        return $this;
161
    }
162
163
    /**
164
     * 设置标识
165
     * @access public
166
     * @param  string $name 标识名
167
     * @return $this
168
     */
169
    public function name(string $name)
170
    {
171
        $this->name = $name;
172
173
        return $this;
174
    }
175
176
    /**
177
     * 获取路由对象
178
     * @access public
179
     * @return Route
180
     */
181 3
    public function getRouter(): Route
182
    {
183 3
        return $this->router;
184
    }
185
186
    /**
187
     * 获取Name
188
     * @access public
189
     * @return string
190
     */
191
    public function getName(): string
192
    {
193
        return $this->name ?: '';
194
    }
195
196
    /**
197
     * 获取当前路由规则
198
     * @access public
199
     * @return mixed
200
     */
201 9
    public function getRule()
202
    {
203 9
        return $this->rule;
204
    }
205
206
    /**
207
     * 获取当前路由地址
208
     * @access public
209
     * @return mixed
210
     */
211 27
    public function getRoute()
212
    {
213 27
        return $this->route;
214
    }
215
216
    /**
217
     * 获取当前路由的变量
218
     * @access public
219
     * @return array
220
     */
221 3
    public function getVars(): array
222
    {
223 3
        return $this->vars;
224
    }
225
226
    /**
227
     * 获取Parent对象
228
     * @access public
229
     * @return $this|null
230
     */
231
    public function getParent()
232
    {
233
        return $this->parent;
234
    }
235
236
    /**
237
     * 获取路由所在域名
238
     * @access public
239
     * @return string
240
     */
241 9
    public function getDomain(): string
242
    {
243 9
        return $this->domain ?: $this->parent->getDomain();
244
    }
245
246
    /**
247
     * 获取路由参数
248
     * @access public
249
     * @param  string $name 变量名
250
     * @return mixed
251
     */
252 27
    public function config(string $name = '')
253
    {
254 27
        return $this->router->config($name);
255
    }
256
257
    /**
258
     * 获取变量规则定义
259
     * @access public
260
     * @param  string $name 变量名
261
     * @return mixed
262
     */
263 24
    public function getPattern(string $name = '')
264
    {
265 24
        $pattern = $this->pattern;
266
267 24
        if ($this->parent) {
268 24
            $pattern = array_merge($this->parent->getPattern(), $pattern);
269
        }
270
271 24
        if ('' === $name) {
272 24
            return $pattern;
273
        }
274
275
        return $pattern[$name] ?? null;
276
    }
277
278
    /**
279
     * 获取路由参数定义
280
     * @access public
281
     * @param  string $name 参数名
282
     * @param  mixed  $default 默认值
283
     * @return mixed
284
     */
285 27
    public function getOption(string $name = '', $default = null)
286
    {
287 27
        $option = $this->option;
288
289 27
        if ($this->parent) {
290 24
            $parentOption = $this->parent->getOption();
291
292
            // 合并分组参数
293 24
            foreach ($this->mergeOptions as $item) {
294 24
                if (isset($parentOption[$item]) && isset($option[$item])) {
295
                    $option[$item] = array_merge($parentOption[$item], $option[$item]);
296
                }
297
            }
298
299 24
            $option = array_merge($parentOption, $option);
300
        }
301
302 27
        if ('' === $name) {
303 27
            return $option;
304
        }
305
306 9
        return $option[$name] ?? $default;
307
    }
308
309
    /**
310
     * 获取当前路由的请求类型
311
     * @access public
312
     * @return string
313
     */
314 24
    public function getMethod(): string
315
    {
316 24
        return strtolower($this->method);
317
    }
318
319
    /**
320
     * 设置路由请求类型
321
     * @access public
322
     * @param  string $method 请求类型
323
     * @return $this
324
     */
325
    public function method(string $method)
326
    {
327
        return $this->setOption('method', strtolower($method));
328
    }
329
330
    /**
331
     * 检查后缀
332
     * @access public
333
     * @param  string $ext URL后缀
334
     * @return $this
335
     */
336
    public function ext(string $ext = '')
337
    {
338
        return $this->setOption('ext', $ext);
339
    }
340
341
    /**
342
     * 检查禁止后缀
343
     * @access public
344
     * @param  string $ext URL后缀
345
     * @return $this
346
     */
347
    public function denyExt(string $ext = '')
348
    {
349
        return $this->setOption('deny_ext', $ext);
350
    }
351
352
    /**
353
     * 检查域名
354
     * @access public
355
     * @param  string $domain 域名
356
     * @return $this
357
     */
358
    public function domain(string $domain)
359
    {
360
        $this->domain = $domain;
361
        return $this->setOption('domain', $domain);
362
    }
363
364
    /**
365
     * 是否区分大小写
366
     * @access public
367
     * @param  bool $case 是否区分
368
     * @return $this
369
     */
370
    public function caseUrl(bool $case)
371
    {
372
        return $this->setOption('case_sensitive', $case);
373
    }
374
375
    /**
376
     * 设置参数过滤检查
377
     * @access public
378
     * @param  array $filter 参数过滤
379
     * @return $this
380
     */
381
    public function filter(array $filter)
382
    {
383
        $this->option['filter'] = $filter;
384
385
        return $this;
386
    }
387
388
    /**
389
     * 绑定模型
390
     * @access public
391
     * @param  array|string|Closure $var  路由变量名 多个使用 & 分割
392
     * @param  string|Closure       $model 绑定模型类
393
     * @param  bool                  $exception 是否抛出异常
394
     * @return $this
395
     */
396
    public function model(array | string | Closure $var, string | Closure $model = null, bool $exception = true)
397
    {
398
        if ($var instanceof Closure) {
0 ignored issues
show
introduced by
$var is never a sub-type of Closure.
Loading history...
399
            $this->option['model'][] = $var;
400
        } elseif (is_array($var)) {
0 ignored issues
show
introduced by
The condition is_array($var) is always true.
Loading history...
401
            $this->option['model'] = $var;
402
        } elseif (is_null($model)) {
403
            $this->option['model']['id'] = [$var, true];
404
        } else {
405
            $this->option['model'][$var] = [$model, $exception];
406
        }
407
408
        return $this;
409
    }
410
411
    /**
412
     * 附加路由隐式参数
413
     * @access public
414
     * @param  array $append 追加参数
415
     * @return $this
416
     */
417
    public function append(array $append = [])
418
    {
419
        $this->option['append'] = $append;
420
421
        return $this;
422
    }
423
424
    /**
425
     * 绑定验证
426
     * @access public
427
     * @param  mixed  $validate 验证器类
428
     * @param  string $scene 验证场景
429
     * @param  array  $message 验证提示
430
     * @param  bool   $batch 批量验证
431
     * @return $this
432
     */
433
    public function validate($validate, string $scene = null, array $message = [], bool $batch = false)
434
    {
435
        $this->option['validate'] = [$validate, $scene, $message, $batch];
436
437
        return $this;
438
    }
439
440
    /**
441
     * 指定路由中间件
442
     * @access public
443
     * @param string|array|Closure $middleware 中间件
444
     * @param mixed $params 参数
445
     * @return $this
446
     */
447 3
    public function middleware(string | array | Closure $middleware, ...$params)
448
    {
449 3
        if (empty($params) && is_array($middleware)) {
450
            $this->option['middleware'] = $middleware;
451
        } else {
452 3
            foreach ((array) $middleware as $item) {
453 3
                $this->option['middleware'][] = [$item, $params];
454
            }
455
        }
456
457 3
        return $this;
458
    }
459
460
    /**
461
     * 不使用中间件
462
     * @access public
463
     * @return $this
464
     */
465
    public function withoutMiddleware()
466
    {
467
        $this->option['without_middleware'] = true;
468
469
        return $this;
470
    }
471
472
    /**
473
     * 允许跨域
474
     * @access public
475
     * @param  array $header 自定义Header
476
     * @return $this
477
     */
478 3
    public function allowCrossDomain(array $header = [])
479
    {
480 3
        return $this->middleware(AllowCrossDomain::class, $header);
481
    }
482
483
    /**
484
     * 表单令牌验证
485
     * @access public
486
     * @param  string $token 表单令牌token名称
487
     * @return $this
488
     */
489
    public function token(string $token = '__token__')
490
    {
491
        return $this->middleware(FormTokenCheck::class, $token);
492
    }
493
494
    /**
495
     * 设置路由缓存
496
     * @access public
497
     * @param  array|string|int $cache 缓存
498
     * @return $this
499
     */
500
    public function cache(array | string | int $cache)
501
    {
502
        return $this->middleware(CheckRequestCache::class, $cache);
503
    }
504
505
    /**
506
     * 检查URL分隔符
507
     * @access public
508
     * @param  string $depr URL分隔符
509
     * @return $this
510
     */
511
    public function depr(string $depr)
512
    {
513
        return $this->setOption('param_depr', $depr);
514
    }
515
516
    /**
517
     * 设置需要合并的路由参数
518
     * @access public
519
     * @param  array $option 路由参数
520
     * @return $this
521
     */
522
    public function mergeOptions(array $option = [])
523
    {
524
        $this->mergeOptions = array_merge($this->mergeOptions, $option);
525
        return $this;
526
    }
527
528
    /**
529
     * 检查是否为HTTPS请求
530
     * @access public
531
     * @param  bool $https 是否为HTTPS
532
     * @return $this
533
     */
534
    public function https(bool $https = true)
535
    {
536
        return $this->setOption('https', $https);
537
    }
538
539
    /**
540
     * 检查是否为JSON请求
541
     * @access public
542
     * @param  bool $json 是否为JSON
543
     * @return $this
544
     */
545
    public function json(bool $json = true)
546
    {
547
        return $this->setOption('json', $json);
548
    }
549
550
    /**
551
     * 检查是否为AJAX请求
552
     * @access public
553
     * @param  bool $ajax 是否为AJAX
554
     * @return $this
555
     */
556
    public function ajax(bool $ajax = true)
557
    {
558
        return $this->setOption('ajax', $ajax);
559
    }
560
561
    /**
562
     * 检查是否为PJAX请求
563
     * @access public
564
     * @param  bool $pjax 是否为PJAX
565
     * @return $this
566
     */
567
    public function pjax(bool $pjax = true)
568
    {
569
        return $this->setOption('pjax', $pjax);
570
    }
571
572
    /**
573
     * 路由到一个模板地址 需要额外传入的模板变量
574
     * @access public
575
     * @param  array $view 视图
576
     * @return $this
577
     */
578
    public function view(array $view = [])
579
    {
580
        return $this->setOption('view', $view);
581
    }
582
583
    /**
584
     * 通过闭包检查路由是否匹配
585
     * @access public
586
     * @param  callable $match 闭包
587
     * @return $this
588
     */
589
    public function match(callable $match)
590
    {
591
        return $this->setOption('match', $match);
592
    }
593
594
    /**
595
     * 设置路由完整匹配
596
     * @access public
597
     * @param  bool $match 是否完整匹配
598
     * @return $this
599
     */
600
    public function completeMatch(bool $match = true)
601
    {
602
        return $this->setOption('complete_match', $match);
603
    }
604
605
    /**
606
     * 是否去除URL最后的斜线
607
     * @access public
608
     * @param  bool $remove 是否去除最后斜线
609
     * @return $this
610
     */
611 27
    public function removeSlash(bool $remove = true)
612
    {
613 27
        return $this->setOption('remove_slash', $remove);
614
    }
615
616
    /**
617
     * 设置路由规则全局有效
618
     * @access public
619
     * @return $this
620
     */
621
    public function crossDomainRule()
622
    {
623
        $this->router->setCrossDomainRule($this);
624
        return $this;
625
    }
626
627
    /**
628
     * 解析匹配到的规则路由
629
     * @access public
630
     * @param  Request $request 请求对象
631
     * @param  string  $rule 路由规则
632
     * @param  mixed   $route 路由地址
633
     * @param  string  $url URL地址
634
     * @param  array   $option 路由参数
635
     * @param  array   $matches 匹配的变量
636
     * @return Dispatch
637
     */
638 24
    public function parseRule(Request $request, string $rule, $route, string $url, array $option = [], array $matches = []): Dispatch
639
    {
640 24
        if (is_string($route) && isset($option['prefix'])) {
641
            // 路由地址前缀
642
            $route = $option['prefix'] . $route;
643
        }
644
645
        // 替换路由地址中的变量
646 24
        $extraParams = true;
647 24
        $search      = $replace      = [];
648 24
        $depr        = $this->config('pathinfo_depr');
649 24
        foreach ($matches as $key => $value) {
650
            $search[]  = '<' . $key . '>';
651
            $replace[] = $value;
652
653
            $search[]  = ':' . $key;
654
            $replace[] = $value;
655
656
            if (str_contains($value, $depr)) {
0 ignored issues
show
Bug introduced by
It seems like $depr can also be of type null; however, parameter $needle of str_contains() does only seem to accept string, 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

656
            if (str_contains($value, /** @scrutinizer ignore-type */ $depr)) {
Loading history...
657
                $extraParams = false;
658
            }
659
        }
660
661 24
        if (is_string($route)) {
662 9
            $route = str_replace($search, $replace, $route);
663
        }
664
665
        // 解析额外参数
666 24
        if ($extraParams) {
667 24
            $count = substr_count($rule, '/');
668 24
            $url   = array_slice(explode('|', $url), $count + 1);
669 24
            $this->parseUrlParams(implode('|', $url), $matches);
670
        }
671
672 24
        foreach ($matches as $key => &$val) {
673
            if (isset($this->pattern[$key]) && in_array($this->pattern[$key], ['\d+', 'int', 'float'])) {
674
                $val = match ($this->pattern[$key]) {
675
                    'int', '\d+' => (int) $val,
676
                    'float'      => (float) $val,
677
                    default      => $val,
678
                };
679
            }
680
        }
681
682 24
        $this->vars = $matches;
683
684
        // 发起路由调度
685 24
        return $this->dispatch($request, $route, $option);
686
    }
687
688
    /**
689
     * 发起路由调度
690
     * @access protected
691
     * @param  Request $request Request对象
692
     * @param  mixed   $route  路由地址
693
     * @param  array   $option 路由参数
694
     * @return Dispatch
695
     */
696 24
    protected function dispatch(Request $request, $route, array $option): Dispatch
697
    {
698 24
        if (isset($option['dispatcher']) && is_subclass_of($option['dispatcher'], Dispatch::class)) {
699
            // 指定分组的调度处理对象
700
            $result = new $option['dispatcher']($request, $this, $route, $this->vars);
701 24
        } elseif (is_subclass_of($route, Dispatch::class)) {
702
            $result = new $route($request, $this, $route, $this->vars);
703 24
        } elseif ($route instanceof Closure) {
704
            // 执行闭包
705 15
            $result = new CallbackDispatch($request, $this, $route, $this->vars);
706 9
        } elseif (is_array($route)) {
707
            // 路由到类的方法
708
            $result = $this->dispatchMethod($request, $route);
709 9
        } elseif (str_contains($route, '@') || str_contains($route, '::') || str_contains($route, '\\')) {
710
            // 路由到类的方法
711
            $route  = str_replace('::', '@', $route);
712
            $result = $this->dispatchMethod($request, $route);
713
        } else {
714
            // 路由到控制器/操作
715 9
            $result = $this->dispatchController($request, $route);
716
        }
717
718 24
        return $result;
719
    }
720
721
    /**
722
     * 调度到类的方法
723
     * @access protected
724
     * @param  Request $request Request对象
725
     * @param  string|array  $route 路由地址
726
     * @return CallbackDispatch
727
     */
728
    protected function dispatchMethod(Request $request, string | array $route): CallbackDispatch
729
    {
730
        if (is_string($route)) {
0 ignored issues
show
introduced by
The condition is_string($route) is always false.
Loading history...
731
            $path = $this->parseUrlPath($route);
732
733
            $route  = str_replace('/', '@', implode('/', $path));
734
            $method = str_contains($route, '@') ? explode('@', $route) : $route;
735
        } else {
736
            $method = $route;
737
        }
738
739
        return new CallbackDispatch($request, $this, $method, $this->vars);
740
    }
741
742
    /**
743
     * 调度到控制器方法 规则:模块/控制器/操作
744
     * @access protected
745
     * @param  Request $request Request对象
746
     * @param  string  $route 路由地址
747
     * @return ControllerDispatch
748
     */
749 9
    protected function dispatchController(Request $request, string $route): ControllerDispatch
750
    {
751 9
        $path = $this->parseUrlPath($route);
752
753 9
        $action     = array_pop($path);
754 9
        $controller = !empty($path) ? array_pop($path) : null;
755
756
        // 路由到模块/控制器/操作
757 9
        return new ControllerDispatch($request, $this, [$controller, $action], $this->vars);
758
    }
759
760
    /**
761
     * 路由检查
762
     * @access protected
763
     * @param  array   $option 路由参数
764
     * @param  Request $request Request对象
765
     * @return bool
766
     */
767 27
    protected function checkOption(array $option, Request $request): bool
768
    {
769
        // 检查当前路由是否匹配
770 27
        if (isset($option['match']) && is_callable($option['match'])) {
771
            if (false === $option['match']($this, $request)) {
772
                return false;
773
            }
774
        }
775
776
        // 请求类型检测
777 27
        if (!empty($option['method'])) {
778
            if (is_string($option['method']) && false === stripos($option['method'], $request->method())) {
779
                return false;
780
            }
781
        }
782
783
        // AJAX PJAX 请求检查
784 27
        foreach (['ajax', 'pjax', 'json'] as $item) {
785 27
            if (isset($option[$item])) {
786
                $call = 'is' . $item;
787
                if ($option[$item] && !$request->$call() || !$option[$item] && $request->$call()) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($option[$item] && ! $re...m] && $request->$call(), Probably Intended Meaning: $option[$item] && (! $re...] && $request->$call())
Loading history...
788
                    return false;
789
                }
790
            }
791
        }
792
793
        // 伪静态后缀检测
794 27
        if ($request->url() != '/' && ((isset($option['ext']) && false === stripos('|' . $option['ext'] . '|', '|' . $request->ext() . '|'))
795 27
            || (isset($option['deny_ext']) && false !== stripos('|' . $option['deny_ext'] . '|', '|' . $request->ext() . '|')))) {
796
            return false;
797
        }
798
799
        // 域名检查
800 27
        if ((isset($option['domain']) && !in_array($option['domain'], [$request->host(true), $request->subDomain()]))) {
801
            return false;
802
        }
803
804
        // HTTPS检查
805 27
        if ((isset($option['https']) && $option['https'] && !$request->isSsl())
806 27
            || (isset($option['https']) && !$option['https'] && $request->isSsl())
807
        ) {
808
            return false;
809
        }
810
811
        // 请求参数检查
812 27
        if (isset($option['filter'])) {
813
            foreach ($option['filter'] as $name => $value) {
814
                if ($request->param($name, '') != $value) {
815
                    return false;
816
                }
817
            }
818
        }
819
820 27
        return true;
821
    }
822
823
    /**
824
     * 解析URL地址中的参数Request对象
825
     * @access protected
826
     * @param  string $rule 路由规则
827
     * @param  array  $var 变量
828
     * @return void
829
     */
830 24
    protected function parseUrlParams(string $url, array &$var = []): void
831
    {
832 24
        if ($url) {
833
            preg_replace_callback('/(\w+)\|([^\|]+)/', function ($match) use (&$var) {
834
                $var[$match[1]] = strip_tags($match[2]);
835
            }, $url);
836
        }
837
    }
838
839
    /**
840
     * 解析URL的pathinfo参数
841
     * @access public
842
     * @param  string $url URL地址
843
     * @return array
844
     */
845 12
    public function parseUrlPath(string $url): array
846
    {
847
        // 分隔符替换 确保路由定义使用统一的分隔符
848 12
        $url = str_replace('|', '/', $url);
849 12
        $url = trim($url, '/');
850
851 12
        if (str_contains($url, '/')) {
852
            // [控制器/操作]
853 9
            $path = explode('/', $url);
854
        } else {
855 3
            $path = [$url];
856
        }
857
858 12
        return $path;
859
    }
860
861
    /**
862
     * 生成路由的正则规则
863
     * @access protected
864
     * @param  string $rule 路由规则
865
     * @param  array  $match 匹配的变量
866
     * @param  array  $pattern   路由变量规则
867
     * @param  array  $option    路由参数
868
     * @param  bool   $completeMatch   路由是否完全匹配
869
     * @param  string $suffix   路由正则变量后缀
870
     * @return string
871
     */
872
    protected function buildRuleRegex(string $rule, array $match, array $pattern = [], array $option = [], bool $completeMatch = false, string $suffix = ''): string
873
    {
874
        foreach ($match as $name) {
875
            $value = $this->buildNameRegex($name, $pattern, $suffix);
876
            if ($value) {
877
                $origin[]  = $name;
878
                $replace[] = $value;
879
            }
880
        }
881
882
        // 是否区分 / 地址访问
883
        if ('/' != $rule) {
884
            if (!empty($option['remove_slash'])) {
885
                $rule = rtrim($rule, '/');
886
            } elseif (str_ends_with($rule, '/')) {
887
                $rule     = rtrim($rule, '/');
888
                $hasSlash = true;
889
            }
890
        }
891
892
        $regex = isset($replace) ? str_replace($origin, $replace, $rule) : $rule;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $origin does not seem to be defined for all execution paths leading up to this point.
Loading history...
893
        $regex = str_replace([')?/', ')?-'], [')/', ')-'], $regex);
894
895
        if (isset($hasSlash)) {
896
            $regex .= '/';
897
        }
898
899
        return $regex . ($completeMatch ? '$' : '');
900
    }
901
902
    /**
903
     * 生成路由变量的正则规则
904
     * @access protected
905
     * @param  string $name    路由变量
906
     * @param  array  $pattern 变量规则
907
     * @param  string $suffix  路由正则变量后缀
908
     * @return string
909
     */
910
    protected function buildNameRegex(string $name, array $pattern, string $suffix): string
911
    {
912
        $optional = '';
913
        $slash    = substr($name, 0, 1);
914
915
        if (in_array($slash, ['/', '-'])) {
916
            $prefix = $slash;
917
            $name   = substr($name, 1);
918
            $slash  = substr($name, 0, 1);
919
        } else {
920
            $prefix = '';
921
        }
922
923
        if ('<' != $slash) {
924
            return '';
925
        }
926
927
        if (str_contains($name, '?')) {
928
            $name     = substr($name, 1, -2);
929
            $optional = '?';
930
        } elseif (str_contains($name, '>')) {
931
            $name = substr($name, 1, -1);
932
        }
933
934
        if (isset($pattern[$name])) {
935
            $nameRule = $pattern[$name];
936
            if (isset($this->regex[$nameRule])) {
937
                $nameRule = $this->regex[$nameRule];
938
            }
939
940
            if (str_starts_with($nameRule, '/') && str_ends_with($nameRule, '/')) {
941
                $nameRule = substr($nameRule, 1, -1);
942
            }
943
        } else {
944
            $nameRule = $this->config('default_route_pattern');
945
        }
946
947
        return '(' . $prefix . '(?<' . $name . $suffix . '>' . $nameRule . '))' . $optional;
948
    }
949
950
    /**
951
     * 设置路由参数
952
     * @access public
953
     * @param  string $method 方法名
954
     * @param  array  $args   调用参数
955
     * @return $this
956
     */
957
    public function __call($method, $args)
958
    {
959
        if (count($args) > 1) {
960
            $args[0] = $args;
961
        }
962
        array_unshift($args, $method);
963
964
        return call_user_func_array([$this, 'setOption'], $args);
965
    }
966
967
    public function __sleep()
968
    {
969
        return ['name', 'rule', 'route', 'method', 'vars', 'option', 'pattern'];
970
    }
971
972
    public function __wakeup()
973
    {
974
        $this->router = Container::pull('route');
975
    }
976
977
    public function __debugInfo()
978
    {
979
        return [
980
            'name'    => $this->name,
981
            'rule'    => $this->rule,
982
            'route'   => $this->route,
983
            'method'  => $this->method,
984
            'vars'    => $this->vars,
985
            'option'  => $this->option,
986
            'pattern' => $this->pattern,
987
        ];
988
    }
989
}
990