Passed
Push — 8.0 ( cd7e8a...0aac54 )
by liu
11:57 queued 09:21
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 1
Bugs 0 Features 0
Metric Value
cc 29
eloc 24
c 1
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
    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  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 24
    public function parseRule(Request $request, string $rule, $route, string $url, array $option = [], array $matches = []): Dispatch
672
    {
673 24
        if (is_string($route) && isset($option['prefix'])) {
674
            // 路由地址前缀
675
            $route = $option['prefix'] . $route;
676
        }
677
678
        // 替换路由地址中的变量
679 24
        $extraParams = true;
680 24
        $search      = $replace      = [];
681 24
        $depr        = $this->config('pathinfo_depr');
682 24
        foreach ($matches as $key => $value) {
683
            $search[]  = '<' . $key . '>';
684
            $replace[] = $value;
685
            $search[]  = '{' . $key . '}';
686
            $replace[] = $value;
687
            $search[]  = ':' . $key;
688
            $replace[] = $value;
689
690
            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

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