Passed
Push — 8.0 ( a85345...fadba0 )
by liu
02:39
created

Rule::parseRule()   B

Complexity

Conditions 11
Paths 96

Size

Total Lines 52
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 29.1367

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 11
eloc 30
c 2
b 0
f 0
nc 96
nop 6
dl 0
loc 52
ccs 15
cts 32
cp 0.4688
crap 29.1367
rs 7.3166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
// +----------------------------------------------------------------------
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
     *
166
     * @access public
167
     * @param  string $name 变量名
168
     * @param  mixed  $rule 变量规则
169
     * @return $this
170
     */
171
    public function when(string | array $name, $rule = null)
172
    {
173
        if (is_array($name)) {
0 ignored issues
show
introduced by
The condition is_array($name) is always true.
Loading history...
174
            $this->option['var_rule'] = $name;
175
        } else {
176
            $this->option['var_rule'][$name] = $rule;
177
        }
178
179
        return $this;
180
    }
181
182
    /**
183
     * 设置标识
184
     * @access public
185
     * @param  string $name 标识名
186
     * @return $this
187
     */
188
    public function name(string $name)
189
    {
190
        $this->name = $name;
191
192
        return $this;
193
    }
194
195
    /**
196
     * 获取路由对象
197
     * @access public
198
     * @return Route
199
     */
200
    public function getRouter(): Route
201
    {
202
        return $this->router;
203
    }
204
205
    /**
206
     * 获取Name
207
     * @access public
208
     * @return string
209
     */
210
    public function getName(): string
211
    {
212
        return $this->name ?: '';
213
    }
214
215
    /**
216
     * 获取当前路由规则
217
     * @access public
218
     * @return mixed
219
     */
220 9
    public function getRule()
221
    {
222 9
        return $this->rule;
223
    }
224
225
    /**
226
     * 获取当前路由地址
227
     * @access public
228
     * @return mixed
229
     */
230 27
    public function getRoute()
231
    {
232 27
        return $this->route;
233
    }
234
235
    /**
236
     * 获取当前路由的变量
237
     * @access public
238
     * @return array
239
     */
240 3
    public function getVars(): array
241
    {
242 3
        return $this->vars;
243
    }
244
245
    /**
246
     * 获取Parent对象
247
     * @access public
248
     * @return $this|null
249
     */
250
    public function getParent()
251
    {
252
        return $this->parent;
253
    }
254
255
    /**
256
     * 获取路由所在域名
257
     * @access public
258
     * @return string
259
     */
260 9
    public function getDomain(): string
261
    {
262 9
        return $this->domain ?: $this->parent->getDomain();
263
    }
264
265
    /**
266
     * 获取路由参数
267
     * @access public
268
     * @param  string $name 变量名
269
     * @return mixed
270
     */
271 27
    public function config(string $name = '')
272
    {
273 27
        return $this->router->config($name);
274
    }
275
276
    /**
277
     * 获取变量规则定义
278
     * @access public
279
     * @param  string $name 变量名
280
     * @return mixed
281
     */
282 24
    public function getPattern(string $name = '')
283
    {
284 24
        $pattern = $this->pattern;
285
286 24
        if ($this->parent) {
287 24
            $pattern = array_merge($this->parent->getPattern(), $pattern);
288
        }
289
290 24
        if ('' === $name) {
291 24
            return $pattern;
292
        }
293
294
        return $pattern[$name] ?? null;
295
    }
296
297
    /**
298
     * 获取路由参数定义
299
     * @access public
300
     * @param  string $name 参数名
301
     * @param  mixed  $default 默认值
302
     * @return mixed
303
     */
304 27
    public function getOption(string $name = '', $default = null)
305
    {
306 27
        $option = $this->option;
307
308 27
        if ($this->parent) {
309 24
            $parentOption = $this->parent->getOption();
310
311
            // 合并分组参数
312 24
            foreach ($this->mergeOptions as $item) {
313 24
                if (isset($parentOption[$item]) && isset($option[$item])) {
314
                    $option[$item] = array_merge($parentOption[$item], $option[$item]);
315
                }
316
            }
317
318 24
            $option = array_merge($parentOption, $option);
319
        }
320
321 27
        if ('' === $name) {
322 27
            return $option;
323
        }
324
325 9
        return $option[$name] ?? $default;
326
    }
327
328
    /**
329
     * 获取当前路由的请求类型
330
     * @access public
331
     * @return string
332
     */
333 24
    public function getMethod(): string
334
    {
335 24
        return strtolower($this->method);
336
    }
337
338
    /**
339
     * 设置路由请求类型
340
     * @access public
341
     * @param  string $method 请求类型
342
     * @return $this
343
     */
344
    public function method(string $method)
345
    {
346
        return $this->setOption('method', strtolower($method));
347
    }
348
349
    /**
350
     * 检查后缀
351
     * @access public
352
     * @param  string $ext URL后缀
353
     * @return $this
354
     */
355
    public function ext(string $ext = '')
356
    {
357
        return $this->setOption('ext', $ext);
358
    }
359
360
    /**
361
     * 检查禁止后缀
362
     * @access public
363
     * @param  string $ext URL后缀
364
     * @return $this
365
     */
366
    public function denyExt(string $ext = '')
367
    {
368
        return $this->setOption('deny_ext', $ext);
369
    }
370
371
    /**
372
     * 检查域名
373
     * @access public
374
     * @param  string $domain 域名
375
     * @return $this
376
     */
377
    public function domain(string $domain)
378
    {
379
        $this->domain = $domain;
380
        return $this->setOption('domain', $domain);
381
    }
382
383
    /**
384
     * 是否区分大小写
385
     * @access public
386
     * @param  bool $case 是否区分
387
     * @return $this
388
     */
389
    public function caseUrl(bool $case)
390
    {
391
        return $this->setOption('case_sensitive', $case);
392
    }
393
394
    /**
395
     * 设置参数过滤检查
396
     * @access public
397
     * @param  array $filter 参数过滤
398
     * @return $this
399
     */
400
    public function filter(array $filter)
401
    {
402
        $this->option['filter'] = $filter;
403
404
        return $this;
405
    }
406
407
    /**
408
     * 设置路由变量默认值
409
     * @access public
410
     * @param  array $default 可选路由变量默认值
411
     * @return $this
412
     */
413
    public function default(array $default)
414
    {
415
        $this->option['default'] = $default;
416
417
        return $this;
418
    }
419
420
    /**
421
     * 设置路由自动注册中间件
422
     * @access public
423
     * @param  bool $auto 
424
     * @return $this
425
     */
426
    public function autoMiddleware(bool $auto = true)
427
    {
428
        $this->option['auto_middleware'] = $auto;
429
430
        return $this;
431
    }
432
433
    /**
434
     * 绑定模型
435
     * @access public
436
     * @param  array|string|Closure $var  路由变量名 多个使用 & 分割
437
     * @param  string|Closure|null  $model 绑定模型类
438
     * @param  bool                 $exception 是否抛出异常
439
     * @return $this
440
     */
441
    public function model(array | string | Closure $var, string | Closure | null $model = null, bool $exception = true)
442
    {
443
        if ($var instanceof Closure) {
0 ignored issues
show
introduced by
$var is never a sub-type of Closure.
Loading history...
444
            $this->option['model'][] = $var;
445
        } elseif (is_array($var)) {
0 ignored issues
show
introduced by
The condition is_array($var) is always true.
Loading history...
446
            $this->option['model'] = $var;
447
        } elseif (is_null($model)) {
448
            $this->option['model']['id'] = [$var, true];
449
        } else {
450
            $this->option['model'][$var] = [$model, $exception];
451
        }
452
453
        return $this;
454
    }
455
456
    /**
457
     * 附加路由隐式参数
458
     * @access public
459
     * @param  array $append 追加参数
460
     * @return $this
461
     */
462
    public function append(array $append = [])
463
    {
464
        $this->option['append'] = $append;
465
466
        return $this;
467
    }
468
469
    /**
470
     * 绑定验证
471
     * @access public
472
     * @param  mixed        $validate 验证器类
473
     * @param  string|array $scene 验证场景
474
     * @param  array        $message 验证提示
475
     * @param  bool         $batch 批量验证
476
     * @return $this
477
     */
478
    public function validate($validate, string | array $scene = '', array $message = [], bool $batch = false)
479
    {
480
        $this->option['validate'] = [$validate, $scene, $message, $batch];
481
482
        return $this;
483
    }
484
485
    /**
486
     * 指定路由中间件
487
     * @access public
488
     * @param string|array|Closure $middleware 中间件
489
     * @param mixed $params 参数
490
     * @return $this
491
     */
492 3
    public function middleware(string | array | Closure $middleware, ...$params)
493
    {
494 3
        if (empty($params) && is_array($middleware)) {
495
            $this->option['middleware'] = $middleware;
496
        } else {
497 3
            foreach ((array) $middleware as $item) {
498 3
                $this->option['middleware'][] = [$item, $params];
499
            }
500
        }
501
502 3
        return $this;
503
    }
504
505
    /**
506
     * 设置不使用的中间件 留空则为全部不用
507
     * @access public
508
     * @param array $middleware 中间件
509
     * @return $this
510
     */
511
    public function withoutMiddleware(array $middleware = [])
512
    {
513
        $this->option['without_middleware'] = $middleware;
514
515
        return $this;
516
    }
517
518
    /**
519
     * 允许跨域
520
     * @access public
521
     * @param  array $header 自定义Header
522
     * @return $this
523
     */
524 3
    public function allowCrossDomain(array $header = [])
525
    {
526 3
        return $this->middleware(AllowCrossDomain::class, $header);
527
    }
528
529
    /**
530
     * 表单令牌验证
531
     * @access public
532
     * @param  string $token 表单令牌token名称
533
     * @return $this
534
     */
535
    public function token(string $token = '__token__')
536
    {
537
        return $this->middleware(FormTokenCheck::class, $token);
538
    }
539
540
    /**
541
     * 设置路由缓存
542
     * @access public
543
     * @param  array|string|int $cache 缓存
544
     * @return $this
545
     */
546
    public function cache(array | string | int $cache)
547
    {
548
        return $this->middleware(CheckRequestCache::class, $cache);
549
    }
550
551
    /**
552
     * 检查URL分隔符
553
     * @access public
554
     * @param  string $depr URL分隔符
555
     * @return $this
556
     */
557
    public function depr(string $depr)
558
    {
559
        return $this->setOption('param_depr', $depr);
560
    }
561
562
    /**
563
     * 设置需要合并的路由参数
564
     * @access public
565
     * @param  array $option 路由参数
566
     * @return $this
567
     */
568
    public function mergeOptions(array $option = [])
569
    {
570
        $this->mergeOptions = array_merge($this->mergeOptions, $option);
571
        return $this;
572
    }
573
574
    /**
575
     * 检查是否为HTTPS请求
576
     * @access public
577
     * @param  bool $https 是否为HTTPS
578
     * @return $this
579
     */
580
    public function https(bool $https = true)
581
    {
582
        return $this->setOption('https', $https);
583
    }
584
585
    /**
586
     * 检查是否为JSON请求
587
     * @access public
588
     * @param  bool $json 是否为JSON
589
     * @return $this
590
     */
591
    public function json(bool $json = true)
592
    {
593
        return $this->setOption('json', $json);
594
    }
595
596
    /**
597
     * 检查是否为AJAX请求
598
     * @access public
599
     * @param  bool $ajax 是否为AJAX
600
     * @return $this
601
     */
602
    public function ajax(bool $ajax = true)
603
    {
604
        return $this->setOption('ajax', $ajax);
605
    }
606
607
    /**
608
     * 检查是否为PJAX请求
609
     * @access public
610
     * @param  bool $pjax 是否为PJAX
611
     * @return $this
612
     */
613
    public function pjax(bool $pjax = true)
614
    {
615
        return $this->setOption('pjax', $pjax);
616
    }
617
618
    /**
619
     * 路由到一个模板地址 需要额外传入的模板变量
620
     * @access public
621
     * @param  array $view 视图
622
     * @return $this
623
     */
624
    public function view(array $view = [])
625
    {
626
        return $this->setOption('view', $view);
627
    }
628
629
    /**
630
     * 通过闭包检查路由是否匹配
631
     * @access public
632
     * @param  callable $match 闭包
633
     * @return $this
634
     */
635
    public function match(callable $match)
636
    {
637
        return $this->setOption('match', $match);
638
    }
639
640
    /**
641
     * 设置路由完整匹配
642
     * @access public
643
     * @param  bool $match 是否完整匹配
644
     * @return $this
645
     */
646
    public function completeMatch(bool $match = true)
647
    {
648
        return $this->setOption('complete_match', $match);
649
    }
650
651
    /**
652
     * 是否去除URL最后的斜线
653
     * @access public
654
     * @param  bool $remove 是否去除最后斜线
655
     * @return $this
656
     */
657 27
    public function removeSlash(bool $remove = true)
658
    {
659 27
        return $this->setOption('remove_slash', $remove);
660
    }
661
662
    /**
663
     * 设置路由规则全局有效
664
     * @access public
665
     * @return $this
666
     */
667
    public function crossDomainRule()
668
    {
669
        $this->router->setCrossDomainRule($this);
670
        return $this;
671
    }
672
673
    /**
674
     * 解析匹配到的规则路由
675
     * @access public
676
     * @param  Request $request 请求对象
677
     * @param  string  $rule 路由规则
678
     * @param  mixed   $route 路由地址
679
     * @param  string  $url URL地址
680
     * @param  array   $option 路由参数
681
     * @param  array   $matches 匹配的变量
682
     * @return Dispatch
683
     */
684 24
    public function parseRule(Request $request, string $rule, $route, string $url, array $option = [], array $matches = []): Dispatch
685
    {
686 24
        if (is_string($route) && isset($option['prefix'])) {
687
            // 路由地址前缀
688
            $route = $option['prefix'] . $route;
689
        }
690
691
        // 替换路由地址中的变量
692 24
        $extraParams = true;
693 24
        $search      = $replace      = [];
694 24
        $depr        = $this->config('pathinfo_depr');
695
696 24
        foreach ($matches as $key => $value) {
697
            $search[]  = '<' . $key . '>';
698
            $replace[] = $value;
699
            $search[]  = '{' . $key . '}';
700
            $replace[] = $value;
701
            $search[]  = ':' . $key;
702
            $replace[] = $value;
703
704
            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

704
            if (str_contains($value, /** @scrutinizer ignore-type */ $depr)) {
Loading history...
705
                $extraParams = false;
706
            }
707
        }
708
709 24
        if (is_string($route)) {
710 9
            $route = str_replace($search, $replace, $route);
711
        }
712
713
        // 解析额外参数
714 24
        if ($extraParams) {
715 24
            $count = substr_count($rule, '/');
716 24
            $url   = array_slice(explode('|', $url), $count + 1);
717 24
            $this->parseUrlParams(implode('/', $url), $matches);
718
        }
719
720 24
        foreach ($matches as $key => &$val) {
721
            if (isset($this->pattern[$key]) && in_array($this->pattern[$key], ['\d+', 'int', 'float'])) {
722
                $val = match ($this->pattern[$key]) {
723
                    'int', '\d+' => (int) $val,
724
                    'float'      => (float) $val,
725
                    default      => $val,
726
                };
727
            } elseif (in_array($key, ['__module__','__controller__','__action__'])) {
728
                unset($matches[$key]);
729
            }
730
        }
731
732 24
        $this->vars = $matches;
733
734
        // 发起路由调度
735 24
        return $this->dispatch($request, $route, $option);
736
    }
737
738
    /**
739
     * 发起路由调度
740
     * @access protected
741
     * @param  Request $request Request对象
742
     * @param  mixed   $route  路由地址
743
     * @param  array   $option 路由参数
744
     * @return Dispatch
745
     */
746 24
    protected function dispatch(Request $request, $route, array $option): Dispatch
747
    {
748 24
        if (isset($option['dispatcher']) && is_subclass_of($option['dispatcher'], Dispatch::class)) {
749
            // 指定分组的调度处理对象
750
            $result = new $option['dispatcher']($request, $this, $route, $this->vars, $option);
751 24
        } elseif (is_subclass_of($route, Dispatch::class)) {
752
            $result = new $route($request, $this, $route, $this->vars, $option);
753 24
        } elseif ($route instanceof Closure) {
754
            // 执行闭包
755 15
            $result = new CallbackDispatch($request, $this, $route, $this->vars, $option);
756 9
        } elseif (is_array($route)) {
757
            // 路由到类的方法
758
            $result = $this->dispatchMethod($request, $route, $option);
759 9
        } elseif (str_contains($route, '@') || str_contains($route, '::') || str_contains($route, '\\')) {
760
            // 路由到类的方法
761
            $route  = str_replace('::', '@', $route);
762
            $result = $this->dispatchMethod($request, $route, $option);
763
        } else {
764
            // 路由到模块/控制器/操作
765 9
            $result = $this->dispatchController($request, $route, $option);
766
        }
767
768 24
        return $result;
769
    }
770
771
    /**
772
     * 调度到类的方法
773
     * @access protected
774
     * @param  Request $request Request对象
775
     * @param  string|array  $route 路由地址
776
     * @return CallbackDispatch
777
     */
778
    protected function dispatchMethod(Request $request, string | array $route, array $option = []): CallbackDispatch
779
    {
780
        if (is_string($route)) {
0 ignored issues
show
introduced by
The condition is_string($route) is always false.
Loading history...
781
            $path = $this->parseUrlPath($route);
782
783
            $route  = str_replace('/', '@', implode('/', $path));
784
            $method = str_contains($route, '@') ? explode('@', $route) : $route;
785
        } else {
786
            $method = $route;
787
        }
788
789
        return new CallbackDispatch($request, $this, $method, $this->vars, $option);
790
    }
791
792
    /**
793
     * 调度到控制器方法 规则:模块/控制器/操作
794
     * @access protected
795
     * @param  Request $request Request对象
796
     * @param  string  $route 路由地址
797
     * @return ControllerDispatch
798
     */
799 9
    protected function dispatchController(Request $request, string $route, array $option = []): ControllerDispatch
800
    {
801 9
        $path = $this->parseUrlPath($route);
802
803
        // 路由到模块/控制器/操作
804 9
        return new ControllerDispatch($request, $this, $path, $this->vars, $option);
805
    }
806
807
    /**
808
     * 路由检查
809
     * @access protected
810
     * @param  array   $option 路由参数
811
     * @param  Request $request Request对象
812
     * @return bool
813
     */
814 27
    protected function checkOption(array $option, Request $request): bool
815
    {
816
        // 检查当前路由是否匹配
817 27
        if (isset($option['match']) && is_callable($option['match'])) {
818
            if (false === $option['match']($this, $request)) {
819
                return false;
820
            }
821
        }
822
823
        // 请求类型检测
824 27
        if (!empty($option['method'])) {
825
            if (is_string($option['method']) && false === stripos($option['method'], $request->method())) {
826
                return false;
827
            }
828
        }
829
830
        // AJAX PJAX 请求检查
831 27
        foreach (['ajax', 'pjax', 'json'] as $item) {
832 27
            if (isset($option[$item])) {
833
                $call = 'is' . $item;
834
                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...
835
                    return false;
836
                }
837
            }
838
        }
839
840
        // 伪静态后缀检测
841 27
        if ($request->url() != '/' && ((isset($option['ext']) && false === stripos('|' . $option['ext'] . '|', '|' . $request->ext() . '|'))
842 27
            || (isset($option['deny_ext']) && false !== stripos('|' . $option['deny_ext'] . '|', '|' . $request->ext() . '|')))) {
843
            return false;
844
        }
845
846
        // 域名检查
847 27
        if ((isset($option['domain']) && !in_array($option['domain'], [$request->host(true), $request->subDomain()]))) {
848
            return false;
849
        }
850
851
        // HTTPS检查
852 27
        if ((isset($option['https']) && $option['https'] && !$request->isSsl())
853 27
            || (isset($option['https']) && !$option['https'] && $request->isSsl())
854
        ) {
855
            return false;
856
        }
857
858
        // 请求参数过滤
859 27
        if (isset($option['filter'])) {
860
            foreach ($option['filter'] as $name => $value) {
861
                if ($request->param($name, '') != $value) {
862
                    return false;
863
                }
864
            }
865
        }
866
867 27
        return true;
868
    }
869
870
    /**
871
     * 解析URL地址中的参数Request对象
872
     * @access protected
873
     * @param  string $rule 路由规则
874
     * @param  array  $var 变量
875
     * @return void
876
     */
877 24
    protected function parseUrlParams(string $url, array &$var = []): void
878
    {
879 24
        if ($url) {
880
            preg_replace_callback('/(\w+)\/([^\/]+)/', function ($match) use (&$var) {
881
                $var[$match[1]] = strip_tags($match[2]);
882
            }, $url);
883
        }
884
    }
885
886
    /**
887
     * 解析URL的pathinfo参数
888
     * @access public
889
     * @param  string $url URL地址
890
     * @return array
891
     */
892 9
    public function parseUrlPath(string $url): array
893
    {
894
        // 分隔符替换 确保路由定义使用统一的分隔符
895 9
        $url = str_replace('|', '/', $url);
896 9
        $url = trim($url, '/');
897
898 9
        if (str_contains($url, '/')) {
899
            // [模块/.../控制器/操作]
900 9
            $path = explode('/', $url);
901
        } else {
902
            $path = [$url];
903
        }
904
905 9
        return $path;
906
    }
907
908
    /**
909
     * 生成路由的正则规则
910
     * @access protected
911
     * @param  string $rule 路由规则
912
     * @param  array  $match 匹配的变量
913
     * @param  array  $pattern   路由变量规则
914
     * @param  array  $option    路由参数
915
     * @param  bool   $completeMatch   路由是否完全匹配
916
     * @param  string $suffix   路由正则变量后缀
917
     * @return string
918
     */
919
    protected function buildRuleRegex(string $rule, array $match, array $pattern = [], array $option = [], bool $completeMatch = false, string $suffix = ''): string
920
    {
921
        foreach ($match as $name) {
922
            $value = $this->buildNameRegex($name, $pattern, $suffix);
923
            if ($value) {
924
                $origin[]  = $name;
925
                $replace[] = $value;
926
            }
927
        }
928
929
        // 是否区分 / 地址访问
930
        if ('/' != $rule) {
931
            if (!empty($option['remove_slash'])) {
932
                $rule = rtrim($rule, '/');
933
            } elseif (str_ends_with($rule, '/')) {
934
                $rule     = rtrim($rule, '/');
935
                $hasSlash = true;
936
            }
937
        }
938
939
        $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...
940
        $regex = str_replace([')?/', ')?-'], [')/', ')-'], $regex);
941
942
        if (isset($hasSlash)) {
943
            $regex .= '/';
944
        }
945
946
        return $regex . ($completeMatch ? '$' : '');
947
    }
948
949
    /**
950
     * 生成路由变量的正则规则
951
     * @access protected
952
     * @param  string $name    路由变量
953
     * @param  array  $pattern 变量规则
954
     * @param  string $suffix  路由正则变量后缀
955
     * @return string
956
     */
957
    protected function buildNameRegex(string $name, array $pattern, string $suffix): string
958
    {
959
        $optional = '';
960
        $slash    = substr($name, 0, 1);
961
962
        if (in_array($slash, ['/', '-'])) {
963
            $prefix = $slash;
964
            $name   = substr($name, 1);
965
            $slash  = substr($name, 0, 1);
966
        } else {
967
            $prefix = '';
968
        }
969
970
        if ('<' != $slash) {
971
            return '';
972
        }
973
974
        if (str_contains($name, '?')) {
975
            $name     = substr($name, 1, -2);
976
            $optional = '?';
977
        } elseif (str_contains($name, '>')) {
978
            $name = substr($name, 1, -1);
979
        }
980
981
        if (isset($pattern[$name])) {
982
            $nameRule = $pattern[$name];
983
            if (isset($this->regex[$nameRule])) {
984
                $nameRule = $this->regex[$nameRule];
985
            }
986
987
            if (str_starts_with($nameRule, '/') && str_ends_with($nameRule, '/')) {
988
                $nameRule = substr($nameRule, 1, -1);
989
            }
990
        } else {
991
            $nameRule = $this->config('default_route_pattern');
992
        }
993
994
        return '(' . $prefix . '(?<' . $name . $suffix . '>' . $nameRule . '))' . $optional;
995
    }
996
997
    /**
998
     * 设置路由参数
999
     * @access public
1000
     * @param  string $method 方法名
1001
     * @param  array  $args   调用参数
1002
     * @return $this
1003
     */
1004
    public function __call($method, $args)
1005
    {
1006
        if (count($args) > 1) {
1007
            $args[0] = $args;
1008
        }
1009
        array_unshift($args, $method);
1010
1011
        return call_user_func_array([$this, 'setOption'], $args);
1012
    }
1013
1014
    public function __sleep()
1015
    {
1016
        return ['name', 'rule', 'route', 'method', 'vars', 'option', 'pattern'];
1017
    }
1018
1019
    public function __wakeup()
1020
    {
1021
        $this->router = Container::pull('route');
1022
    }
1023
1024
    public function __debugInfo()
1025
    {
1026
        return [
1027
            'name'    => $this->name,
1028
            'rule'    => $this->rule,
1029
            'route'   => $this->route,
1030
            'method'  => $this->method,
1031
            'vars'    => $this->vars,
1032
            'option'  => $this->option,
1033
            'pattern' => $this->pattern,
1034
        ];
1035
    }
1036
}
1037