Passed
Push — 8.0 ( d0e1cd...7f09d5 )
by liu
02:13
created

Rule::checkOption()   D

Complexity

Conditions 29
Paths 79

Size

Total Lines 54
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 147.2512

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 29
eloc 24
c 3
b 0
f 0
nc 79
nop 2
dl 0
loc 54
ccs 12
cts 25
cp 0.48
crap 147.2512
rs 4.1666

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 3
    public function pattern(array $pattern)
157
    {
158 3
        $this->pattern = array_merge($this->pattern, $pattern);
159
160 3
        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 12
    public function getRule()
221
    {
222 12
        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 12
    public function getDomain(): string
261
    {
262 12
        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 27
    public function getPattern(string $name = '')
283
    {
284 27
        $pattern = $this->pattern;
285
286 27
        if ($this->parent) {
287 27
            $pattern = array_merge($this->parent->getPattern(), $pattern);
288
        }
289
290 27
        if ('' === $name) {
291 27
            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 27
            $parentOption = $this->parent->getOption();
310
311
            // 合并分组参数
312 27
            foreach ($this->mergeOptions as $item) {
313 27
                if (isset($parentOption[$item]) && isset($option[$item])) {
314
                    $option[$item] = array_merge($parentOption[$item], $option[$item]);
315
                }
316
            }
317
318 27
            $option = array_merge($parentOption, $option);
319
        }
320
321 27
        if ('' === $name) {
322 27
            return $option;
323
        }
324
325 12
        return $option[$name] ?? $default;
326
    }
327
328
    /**
329
     * 获取当前路由的请求类型
330
     * @access public
331
     * @return string
332
     */
333 27
    public function getMethod(): string
334
    {
335 27
        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 3
    public function default(array $default)
414
    {
415 3
        $this->option['default'] = $default;
416
417 3
        return $this;
418
    }
419
420
    /**
421
     * 绑定模型
422
     * @access public
423
     * @param  array|string|Closure $var  路由变量名 多个使用 & 分割
424
     * @param  string|Closure|null  $model 绑定模型类
425
     * @param  bool                 $exception 是否抛出异常
426
     * @return $this
427
     */
428
    public function model(array | string | Closure $var, string | Closure | null $model = null, bool $exception = true)
429
    {
430
        if ($var instanceof Closure) {
0 ignored issues
show
introduced by
$var is never a sub-type of Closure.
Loading history...
431
            $this->option['model'][] = $var;
432
        } elseif (is_array($var)) {
0 ignored issues
show
introduced by
The condition is_array($var) is always true.
Loading history...
433
            $this->option['model'] = $var;
434
        } elseif (is_null($model)) {
435
            $this->option['model']['id'] = [$var, true];
436
        } else {
437
            $this->option['model'][$var] = [$model, $exception];
438
        }
439
440
        return $this;
441
    }
442
443
    /**
444
     * 附加路由隐式参数
445
     * @access public
446
     * @param  array $append 追加参数
447
     * @return $this
448
     */
449
    public function append(array $append = [])
450
    {
451
        $this->option['append'] = $append;
452
453
        return $this;
454
    }
455
456
    /**
457
     * 绑定验证
458
     * @access public
459
     * @param  mixed        $validate 验证器类
460
     * @param  string|array $scene 验证场景
461
     * @param  array        $message 验证提示
462
     * @param  bool         $batch 批量验证
463
     * @return $this
464
     */
465
    public function validate($validate, string | array $scene = '', array $message = [], bool $batch = false)
466
    {
467
        $this->option['validate'] = [$validate, $scene, $message, $batch];
468
469
        return $this;
470
    }
471
472
    /**
473
     * 指定路由中间件
474
     * @access public
475
     * @param string|array|Closure $middleware 中间件
476
     * @param mixed $params 参数
477
     * @return $this
478
     */
479 3
    public function middleware(string | array | Closure $middleware, ...$params)
480
    {
481 3
        if (empty($params) && is_array($middleware)) {
482
            $this->option['middleware'] = $middleware;
483
        } else {
484 3
            foreach ((array) $middleware as $item) {
485 3
                $this->option['middleware'][] = [$item, $params];
486
            }
487
        }
488
489 3
        return $this;
490
    }
491
492
    /**
493
     * 设置不使用的中间件 留空则为全部不用
494
     * @access public
495
     * @param array $middleware 中间件
496
     * @return $this
497
     */
498
    public function withoutMiddleware(array $middleware = [])
499
    {
500
        $this->option['without_middleware'] = $middleware;
501
502
        return $this;
503
    }
504
505
    /**
506
     * 允许跨域
507
     * @access public
508
     * @param  array $header 自定义Header
509
     * @return $this
510
     */
511 3
    public function allowCrossDomain(array $header = [])
512
    {
513 3
        return $this->middleware(AllowCrossDomain::class, $header);
514
    }
515
516
    /**
517
     * 表单令牌验证
518
     * @access public
519
     * @param  string $token 表单令牌token名称
520
     * @return $this
521
     */
522
    public function token(string $token = '__token__')
523
    {
524
        return $this->middleware(FormTokenCheck::class, $token);
525
    }
526
527
    /**
528
     * 设置路由缓存
529
     * @access public
530
     * @param  array|string|int $cache 缓存
531
     * @return $this
532
     */
533
    public function cache(array | string | int $cache)
534
    {
535
        return $this->middleware(CheckRequestCache::class, $cache);
536
    }
537
538
    /**
539
     * 检查URL分隔符
540
     * @access public
541
     * @param  string $depr URL分隔符
542
     * @return $this
543
     */
544
    public function depr(string $depr)
545
    {
546
        return $this->setOption('param_depr', $depr);
547
    }
548
549
    /**
550
     * 设置需要合并的路由参数
551
     * @access public
552
     * @param  array $option 路由参数
553
     * @return $this
554
     */
555
    public function mergeOptions(array $option = [])
556
    {
557
        $this->mergeOptions = array_merge($this->mergeOptions, $option);
558
        return $this;
559
    }
560
561
    /**
562
     * 检查是否为HTTPS请求
563
     * @access public
564
     * @param  bool $https 是否为HTTPS
565
     * @return $this
566
     */
567
    public function https(bool $https = true)
568
    {
569
        return $this->setOption('https', $https);
570
    }
571
572
    /**
573
     * 检查是否为JSON请求
574
     * @access public
575
     * @param  bool $json 是否为JSON
576
     * @return $this
577
     */
578
    public function json(bool $json = true)
579
    {
580
        return $this->setOption('json', $json);
581
    }
582
583
    /**
584
     * 检查是否为AJAX请求
585
     * @access public
586
     * @param  bool $ajax 是否为AJAX
587
     * @return $this
588
     */
589
    public function ajax(bool $ajax = true)
590
    {
591
        return $this->setOption('ajax', $ajax);
592
    }
593
594
    /**
595
     * 检查是否为PJAX请求
596
     * @access public
597
     * @param  bool $pjax 是否为PJAX
598
     * @return $this
599
     */
600
    public function pjax(bool $pjax = true)
601
    {
602
        return $this->setOption('pjax', $pjax);
603
    }
604
605
    /**
606
     * 路由到一个模板地址 需要额外传入的模板变量
607
     * @access public
608
     * @param  array $view 视图
609
     * @return $this
610
     */
611
    public function view(array $view = [])
612
    {
613
        return $this->setOption('view', $view);
614
    }
615
616
    /**
617
     * 通过闭包检查路由是否匹配
618
     * @access public
619
     * @param  callable $match 闭包
620
     * @return $this
621
     */
622
    public function match(callable $match)
623
    {
624
        return $this->setOption('match', $match);
625
    }
626
627
    /**
628
     * 设置路由完整匹配
629
     * @access public
630
     * @param  bool $match 是否完整匹配
631
     * @return $this
632
     */
633
    public function completeMatch(bool $match = true)
634
    {
635
        return $this->setOption('complete_match', $match);
636
    }
637
638
    /**
639
     * 是否去除URL最后的斜线
640
     * @access public
641
     * @param  bool $remove 是否去除最后斜线
642
     * @return $this
643
     */
644 27
    public function removeSlash(bool $remove = true)
645
    {
646 27
        return $this->setOption('remove_slash', $remove);
647
    }
648
649
    /**
650
     * 设置路由规则全局有效
651
     * @access public
652
     * @return $this
653
     */
654
    public function crossDomainRule()
655
    {
656
        $this->router->setCrossDomainRule($this);
657
        return $this;
658
    }
659
660
    /**
661
     * 解析匹配到的规则路由
662
     * @access public
663
     * @param  Request $request 请求对象
664
     * @param  string  $rule 路由规则
665
     * @param  mixed   $route 路由地址
666
     * @param  string  $url URL地址
667
     * @param  array   $option 路由参数
668
     * @param  array   $matches 匹配的变量
669
     * @return Dispatch
670
     */
671 27
    public function parseRule(Request $request, string $rule, $route, string $url, array $option = [], array $matches = []): Dispatch
672
    {
673 27
        if (is_string($route) && isset($option['prefix'])) {
674
            // 路由地址前缀
675
            $route = $option['prefix'] . $route;
676
        }
677
678
        // 替换路由地址中的变量
679 27
        $extraParams = true;
680 27
        $search      = $replace      = [];
681 27
        $depr        = $this->config('pathinfo_depr');
682 27
        foreach ($matches as $key => $value) {
683 3
            $search[]  = '<' . $key . '>';
684 3
            $replace[] = $value;
685
686 3
            $search[]  = ':' . $key;
687 3
            $replace[] = $value;
688
689 3
            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

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