Completed
Push — 6.0 ( a75685...8c1faf )
by liu
06:09
created

Rule::ajax()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
// +----------------------------------------------------------------------
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
6
// +----------------------------------------------------------------------
7
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
8
// +----------------------------------------------------------------------
9
// | Author: liu21st <[email protected]>
10
// +----------------------------------------------------------------------
11
declare (strict_types = 1);
12
13
namespace think\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\Response;
22
use think\Route;
23
use think\route\dispatch\Callback as CallbackDispatch;
24
use think\route\dispatch\Controller as ControllerDispatch;
25
use think\route\dispatch\Redirect as RedirectDispatch;
26
use think\route\dispatch\Response as ResponseDispatch;
27
use think\route\dispatch\View as ViewDispatch;
28
29
/**
30
 * 路由规则基础类
31
 */
32
abstract class Rule
33
{
34
    /**
35
     * 路由标识
36
     * @var string
37
     */
38
    protected $name;
39
40
    /**
41
     * 路由对象
42
     * @var Route
43
     */
44
    protected $router;
45
46
    /**
47
     * 路由所属分组
48
     * @var RuleGroup
49
     */
50
    protected $parent;
51
52
    /**
53
     * 路由规则
54
     * @var mixed
55
     */
56
    protected $rule;
57
58
    /**
59
     * 路由地址
60
     * @var string|Closure
61
     */
62
    protected $route;
63
64
    /**
65
     * 请求类型
66
     * @var string
67
     */
68
    protected $method;
69
70
    /**
71
     * 路由变量
72
     * @var array
73
     */
74
    protected $vars = [];
75
76
    /**
77
     * 路由参数
78
     * @var array
79
     */
80
    protected $option = [];
81
82
    /**
83
     * 路由变量规则
84
     * @var array
85
     */
86
    protected $pattern = [];
87
88
    /**
89
     * 需要和分组合并的路由参数
90
     * @var array
91
     */
92
    protected $mergeOptions = ['model', 'append', 'middleware'];
93
94
    abstract public function check(Request $request, string $url, bool $completeMatch = false);
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function check()
Loading history...
95
96
    /**
97
     * 设置路由参数
98
     * @access public
99
     * @param  array $option 参数
100
     * @return $this
101
     */
102
    public function option(array $option)
103
    {
104
        $this->option = array_merge($this->option, $option);
105
106
        return $this;
107
    }
108
109
    /**
110
     * 设置单个路由参数
111
     * @access public
112
     * @param  string $name  参数名
113
     * @param  mixed  $value 值
114
     * @return $this
115
     */
116
    public function setOption(string $name, $value)
117
    {
118
        $this->option[$name] = $value;
119
120
        return $this;
121
    }
122
123
    /**
124
     * 注册变量规则
125
     * @access public
126
     * @param  array $pattern 变量规则
127
     * @return $this
128
     */
129
    public function pattern(array $pattern)
130
    {
131
        $this->pattern = array_merge($this->pattern, $pattern);
132
133
        return $this;
134
    }
135
136
    /**
137
     * 设置标识
138
     * @access public
139
     * @param  string $name 标识名
140
     * @return $this
141
     */
142
    public function name(string $name)
143
    {
144
        $this->name = $name;
145
146
        return $this;
147
    }
148
149
    /**
150
     * 获取路由对象
151
     * @access public
152
     * @return Route
153
     */
154
    public function getRouter(): Route
155
    {
156
        return $this->router;
157
    }
158
159
    /**
160
     * 获取Name
161
     * @access public
162
     * @return string
163
     */
164
    public function getName(): string
165
    {
166
        return $this->name ?: '';
167
    }
168
169
    /**
170
     * 获取当前路由规则
171
     * @access public
172
     * @return mixed
173
     */
174
    public function getRule()
175
    {
176
        return $this->rule;
177
    }
178
179
    /**
180
     * 获取当前路由地址
181
     * @access public
182
     * @return mixed
183
     */
184
    public function getRoute()
185
    {
186
        return $this->route;
187
    }
188
189
    /**
190
     * 获取当前路由的变量
191
     * @access public
192
     * @return array
193
     */
194
    public function getVars(): array
195
    {
196
        return $this->vars;
197
    }
198
199
    /**
200
     * 获取Parent对象
201
     * @access public
202
     * @return $this|null
203
     */
204
    public function getParent()
205
    {
206
        return $this->parent;
207
    }
208
209
    /**
210
     * 获取路由所在域名
211
     * @access public
212
     * @return string
213
     */
214
    public function getDomain(): string
215
    {
216
        return $this->parent->getDomain();
217
    }
218
219
    /**
220
     * 获取路由参数
221
     * @access public
222
     * @param  string $name 变量名
223
     * @return mixed
224
     */
225
    public function config(string $name = '')
226
    {
227
        return $this->router->config($name);
228
    }
229
230
    /**
231
     * 获取变量规则定义
232
     * @access public
233
     * @param  string $name 变量名
234
     * @return mixed
235
     */
236
    public function getPattern(string $name = '')
237
    {
238
        if ('' === $name) {
239
            return $this->pattern;
240
        }
241
242
        return $this->pattern[$name] ?? null;
243
    }
244
245
    /**
246
     * 获取路由参数定义
247
     * @access public
248
     * @param  string $name 参数名
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
249
     * @param  mixed  $default 默认值
250
     * @return mixed
251
     */
252
    public function getOption(string $name = '', $default = null)
253
    {
254
        if ('' === $name) {
255
            return $this->option;
256
        }
257
258
        return $this->option[$name] ?? $default;
259
    }
260
261
    /**
262
     * 获取当前路由的请求类型
263
     * @access public
264
     * @return string
265
     */
266
    public function getMethod(): string
267
    {
268
        return strtolower($this->method);
269
    }
270
271
    /**
272
     * 设置路由请求类型
273
     * @access public
274
     * @param  string $method 请求类型
275
     * @return $this
276
     */
277
    public function method(string $method)
278
    {
279
        return $this->setOption('method', strtolower($method));
280
    }
281
282
    /**
283
     * 检查后缀
284
     * @access public
285
     * @param  string $ext URL后缀
286
     * @return $this
287
     */
288
    public function ext(string $ext = '')
289
    {
290
        return $this->setOption('ext', $ext);
291
    }
292
293
    /**
294
     * 检查禁止后缀
295
     * @access public
296
     * @param  string $ext URL后缀
297
     * @return $this
298
     */
299
    public function denyExt(string $ext = '')
300
    {
301
        return $this->setOption('deny_ext', $ext);
302
    }
303
304
    /**
305
     * 检查域名
306
     * @access public
307
     * @param  string $domain 域名
308
     * @return $this
309
     */
310
    public function domain(string $domain)
311
    {
312
        return $this->setOption('domain', $domain);
313
    }
314
315
    /**
316
     * 设置参数过滤检查
317
     * @access public
318
     * @param  array $filter 参数过滤
319
     * @return $this
320
     */
321
    public function filter(array $filter)
322
    {
323
        $this->option['filter'] = $filter;
324
325
        return $this;
326
    }
327
328
    /**
329
     * 绑定模型
330
     * @access public
331
     * @param  array|string|Closure $var  路由变量名 多个使用 & 分割
0 ignored issues
show
Coding Style introduced by
Expected 7 spaces after parameter name; 2 found
Loading history...
332
     * @param  string|Closure       $model 绑定模型类
0 ignored issues
show
Coding Style introduced by
Expected 5 spaces after parameter name; 1 found
Loading history...
333
     * @param  bool                  $exception 是否抛出异常
334
     * @return $this
335
     */
336
    public function model($var, $model = null, bool $exception = true)
337
    {
338
        if ($var instanceof Closure) {
339
            $this->option['model'][] = $var;
340
        } elseif (is_array($var)) {
341
            $this->option['model'] = $var;
342
        } elseif (is_null($model)) {
343
            $this->option['model']['id'] = [$var, true];
344
        } else {
345
            $this->option['model'][$var] = [$model, $exception];
346
        }
347
348
        return $this;
349
    }
350
351
    /**
352
     * 附加路由隐式参数
353
     * @access public
354
     * @param  array $append 追加参数
355
     * @return $this
356
     */
357
    public function append(array $append = [])
358
    {
359
        $this->option['append'] = $append;
360
361
        return $this;
362
    }
363
364
    /**
365
     * 设置路由的所属应用
366
     * @access public
367
     * @param  string $app 应用名
368
     * @return $this
369
     */
370
    public function app(string $app)
371
    {
372
        $this->option['app'] = $app;
373
374
        return $this;
375
    }
376
377
    /**
378
     * 绑定验证
379
     * @access public
380
     * @param  mixed  $validate 验证器类
381
     * @param  string $scene 验证场景
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
382
     * @param  array  $message 验证提示
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter name; 1 found
Loading history...
383
     * @param  bool   $batch 批量验证
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
384
     * @return $this
385
     */
386
    public function validate($validate, string $scene = null, array $message = [], bool $batch = false)
387
    {
388
        $this->option['validate'] = [$validate, $scene, $message, $batch];
389
390
        return $this;
391
    }
392
393
    /**
394
     * 指定路由中间件
395
     * @access public
396
     * @param  string|array|Closure $middleware 中间件
397
     * @param  mixed                $param 参数
0 ignored issues
show
Coding Style introduced by
Expected 6 spaces after parameter name; 1 found
Loading history...
398
     * @return $this
399
     */
400
    public function middleware($middleware, $param = null)
401
    {
402
        if (is_null($param) && is_array($middleware)) {
403
            $this->option['middleware'] = $middleware;
404
        } else {
405
            foreach ((array) $middleware as $item) {
406
                $this->option['middleware'][] = [$item, $param];
407
            }
408
        }
409
410
        return $this;
411
    }
412
413
    /**
414
     * 允许跨域
415
     * @access public
416
     * @param  array $header 自定义Header
417
     * @return $this
418
     */
419
    public function allowCrossDomain(array $header = [])
420
    {
421
        return $this->middleware(AllowCrossDomain::class, $header);
422
    }
423
424
    /**
425
     * 表单令牌验证
426
     * @access public
427
     * @param  string $token 表单令牌token名称
428
     * @return $this
429
     */
430
    public function token(string $token = '__token__')
431
    {
432
        return $this->middleware(FormTokenCheck::class, $token);
433
    }
434
435
    /**
436
     * 设置路由缓存
437
     * @access public
438
     * @param  array|string $cache 缓存
439
     * @return $this
440
     */
441
    public function cache($cache)
442
    {
443
        return $this->middleware(CheckRequestCache::class, $cache);
444
    }
445
446
    /**
447
     * 检查URL分隔符
448
     * @access public
449
     * @param  string $depr URL分隔符
450
     * @return $this
451
     */
452
    public function depr(string $depr)
453
    {
454
        return $this->setOption('param_depr', $depr);
455
    }
456
457
    /**
458
     * 设置需要合并的路由参数
459
     * @access public
460
     * @param  array $option 路由参数
461
     * @return $this
462
     */
463
    public function mergeOptions(array $option = [])
464
    {
465
        $this->mergeOptions = array_merge($this->mergeOptions, $option);
466
        return $this;
467
    }
468
469
    /**
470
     * 检查是否为HTTPS请求
471
     * @access public
472
     * @param  bool $https 是否为HTTPS
473
     * @return $this
474
     */
475
    public function https(bool $https = true)
476
    {
477
        return $this->setOption('https', $https);
478
    }
479
480
    /**
481
     * 检查是否为JSON请求
482
     * @access public
483
     * @param  bool $json 是否为JSON
484
     * @return $this
485
     */
486
    public function json(bool $json = true)
487
    {
488
        return $this->setOption('json', $json);
489
    }
490
491
    /**
492
     * 检查是否为AJAX请求
493
     * @access public
494
     * @param  bool $ajax 是否为AJAX
495
     * @return $this
496
     */
497
    public function ajax(bool $ajax = true)
498
    {
499
        return $this->setOption('ajax', $ajax);
500
    }
501
502
    /**
503
     * 检查是否为PJAX请求
504
     * @access public
505
     * @param  bool $pjax 是否为PJAX
506
     * @return $this
507
     */
508
    public function pjax(bool $pjax = true)
509
    {
510
        return $this->setOption('pjax', $pjax);
511
    }
512
513
    /**
514
     * 当前路由到一个模板地址 当使用数组的时候可以传入模板变量
515
     * @access public
516
     * @param  bool|array $view 视图
517
     * @return $this
518
     */
519
    public function view($view = true)
520
    {
521
        return $this->setOption('view', $view);
522
    }
523
524
    /**
525
     * 当前路由为重定向
526
     * @access public
527
     * @param  bool $redirect 是否为重定向
528
     * @return $this
529
     */
530
    public function redirect(bool $redirect = true)
531
    {
532
        return $this->setOption('redirect', $redirect);
533
    }
534
535
    /**
536
     * 设置status
537
     * @access public
538
     * @param  int $status 状态码
539
     * @return $this
540
     */
541
    public function status(int $status)
542
    {
543
        return $this->setOption('status', $status);
544
    }
545
546
    /**
547
     * 设置路由完整匹配
548
     * @access public
549
     * @param  bool $match 是否完整匹配
550
     * @return $this
551
     */
552
    public function completeMatch(bool $match = true)
553
    {
554
        return $this->setOption('complete_match', $match);
555
    }
556
557
    /**
558
     * 是否去除URL最后的斜线
559
     * @access public
560
     * @param  bool $remove 是否去除最后斜线
561
     * @return $this
562
     */
563
    public function removeSlash(bool $remove = true)
564
    {
565
        return $this->setOption('remove_slash', $remove);
566
    }
567
568
    /**
569
     * 设置路由规则全局有效
570
     * @access public
571
     * @return $this
572
     */
573
    public function crossDomainRule()
574
    {
575
        if ($this instanceof RuleGroup) {
576
            $method = '*';
577
        } else {
578
            $method = $this->method;
579
        }
580
581
        $this->router->setCrossDomainRule($this, $method);
582
583
        return $this;
584
    }
585
586
    /**
587
     * 合并分组参数
588
     * @access public
589
     * @return array
590
     */
591
    public function mergeGroupOptions(): array
592
    {
593
        $parentOption = $this->parent->getOption();
594
        // 合并分组参数
595
        foreach ($this->mergeOptions as $item) {
596
            if (isset($parentOption[$item]) && isset($this->option[$item])) {
597
                $this->option[$item] = array_merge($parentOption[$item], $this->option[$item]);
598
            }
599
        }
600
601
        $this->option = array_merge($parentOption, $this->option);
602
603
        return $this->option;
604
    }
605
606
    /**
607
     * 解析匹配到的规则路由
608
     * @access public
609
     * @param  Request $request 请求对象
610
     * @param  string  $rule 路由规则
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
611
     * @param  mixed   $route 路由地址
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
612
     * @param  string  $url URL地址
0 ignored issues
show
Coding Style introduced by
Expected 5 spaces after parameter name; 1 found
Loading history...
613
     * @param  array   $option 路由参数
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter name; 1 found
Loading history...
614
     * @param  array   $matches 匹配的变量
615
     * @return Dispatch
616
     */
617
    public function parseRule(Request $request, string $rule, $route, string $url, array $option = [], array $matches = []): Dispatch
618
    {
619
        if (is_string($route) && isset($option['prefix'])) {
620
            // 路由地址前缀
621
            $route = $option['prefix'] . $route;
622
        }
623
624
        // 替换路由地址中的变量
625
        if (is_string($route) && !empty($matches)) {
626
            $search = $replace = [];
627
628
            foreach ($matches as $key => $value) {
629
                $search[]  = '<' . $key . '>';
630
                $replace[] = $value;
631
632
                $search[]  = ':' . $key;
633
                $replace[] = $value;
634
            }
635
636
            $route = str_replace($search, $replace, $route);
637
        }
638
639
        // 解析额外参数
640
        $count = substr_count($rule, '/');
641
        $url   = array_slice(explode('|', $url), $count + 1);
642
        $this->parseUrlParams(implode('|', $url), $matches);
643
644
        $this->vars = $matches;
645
646
        // 发起路由调度
647
        return $this->dispatch($request, $route, $option);
648
    }
649
650
    /**
651
     * 发起路由调度
652
     * @access protected
653
     * @param  Request $request Request对象
654
     * @param  mixed   $route  路由地址
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 2 found
Loading history...
655
     * @param  array   $option 路由参数
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter name; 1 found
Loading history...
656
     * @return Dispatch
657
     */
658
    protected function dispatch(Request $request, $route, array $option): Dispatch
659
    {
660
        if ($route instanceof Dispatch) {
661
            $result = $route;
662
        } elseif ($route instanceof Closure) {
663
            // 执行闭包
664
            $result = new CallbackDispatch($request, $this, $route, $this->vars);
665
        } elseif ($route instanceof Response) {
666
            $result = new ResponseDispatch($request, $this, $route);
667
        } elseif (isset($option['view']) && false !== $option['view']) {
668
            $result = new ViewDispatch($request, $this, $route, is_array($option['view']) ? $option['view'] : $this->vars);
669
        } elseif (!empty($option['redirect'])) {
670
            // 路由到重定向地址
671
            $result = new RedirectDispatch($request, $this, $route, $this->vars, $option['status'] ?? 301);
672
        } elseif (false !== strpos($route, '\\')) {
673
            // 路由到类的方法
674
            $result = $this->dispatchMethod($request, $route);
675
        } else {
676
            // 路由到控制器/操作
677
            $result = $this->dispatchController($request, $route);
678
        }
679
680
        return $result;
681
    }
682
683
    /**
684
     * 解析URL地址为 模块/控制器/操作
685
     * @access protected
686
     * @param  Request $request Request对象
687
     * @param  string  $route 路由地址
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
688
     * @return CallbackDispatch
689
     */
690
    protected function dispatchMethod(Request $request, string $route): CallbackDispatch
691
    {
692
        $path = $this->parseUrlPath($route);
693
694
        $route  = str_replace('/', '@', implode('/', $path));
695
        $method = strpos($route, '@') ? explode('@', $route) : $route;
696
697
        return new CallbackDispatch($request, $this, $method, $this->vars);
698
    }
699
700
    /**
701
     * 解析URL地址为 模块/控制器/操作
702
     * @access protected
703
     * @param  Request $request Request对象
704
     * @param  string  $route 路由地址
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
705
     * @return ControllerDispatch
706
     */
707
    protected function dispatchController(Request $request, string $route): ControllerDispatch
708
    {
709
        $path = $this->parseUrlPath($route);
710
711
        $action     = array_pop($path);
712
        $controller = !empty($path) ? array_pop($path) : null;
713
714
        // 路由到模块/控制器/操作
715
        return new ControllerDispatch($request, $this, [$controller, $action], $this->vars);
716
    }
717
718
    /**
719
     * 路由检查
720
     * @access protected
721
     * @param  array   $option 路由参数
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter name; 1 found
Loading history...
722
     * @param  Request $request Request对象
723
     * @return bool
724
     */
725
    protected function checkOption(array $option, Request $request): bool
726
    {
727
        // 请求类型检测
728
        if (!empty($option['method'])) {
729
            if (is_string($option['method']) && false === stripos($option['method'], $request->method())) {
730
                return false;
731
            }
732
        }
733
734
        // AJAX PJAX 请求检查
735
        foreach (['ajax', 'pjax', 'json'] as $item) {
736
            if (isset($option[$item])) {
737
                $call = 'is' . $item;
738
                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...
739
                    return false;
740
                }
741
            }
742
        }
743
744
        // 伪静态后缀检测
745
        if ($request->url() != '/' && ((isset($option['ext']) && false === stripos('|' . $option['ext'] . '|', '|' . $request->ext() . '|'))
746
            || (isset($option['deny_ext']) && false !== stripos('|' . $option['deny_ext'] . '|', '|' . $request->ext() . '|')))) {
0 ignored issues
show
Coding Style introduced by
Closing parenthesis of a multi-line IF statement must be on a new line
Loading history...
747
            return false;
748
        }
749
750
        // 域名检查
751
        if ((isset($option['domain']) && !in_array($option['domain'], [$request->host(true), $request->subDomain()]))) {
752
            return false;
753
        }
754
755
        // HTTPS检查
756
        if ((isset($option['https']) && $option['https'] && !$request->isSsl())
757
            || (isset($option['https']) && !$option['https'] && $request->isSsl())) {
0 ignored issues
show
Coding Style introduced by
Closing parenthesis of a multi-line IF statement must be on a new line
Loading history...
758
            return false;
759
        }
760
761
        // 请求参数检查
762
        if (isset($option['filter'])) {
763
            foreach ($option['filter'] as $name => $value) {
764
                if ($request->param($name, '', null) != $value) {
765
                    return false;
766
                }
767
            }
768
        }
769
770
        return true;
771
    }
772
773
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $url should have a doc-comment as per coding-style.
Loading history...
774
     * 解析URL地址中的参数Request对象
775
     * @access protected
776
     * @param  string $rule 路由规则
0 ignored issues
show
Coding Style introduced by
Doc comment for parameter $rule does not match actual variable name $url
Loading history...
777
     * @param  array  $var 变量
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter name; 1 found
Loading history...
778
     * @return void
779
     */
780
    protected function parseUrlParams(string $url, array &$var = []): void
781
    {
782
        if ($url) {
783
            preg_replace_callback('/(\w+)\|([^\|]+)/', function ($match) use (&$var) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
784
                $var[$match[1]] = strip_tags($match[2]);
785
            }, $url);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
786
        }
787
    }
788
789
    /**
790
     * 解析URL的pathinfo参数
791
     * @access public
792
     * @param  string $url URL地址
793
     * @return array
794
     */
795
    public function parseUrlPath(string $url): array
796
    {
797
        // 分隔符替换 确保路由定义使用统一的分隔符
798
        $url = str_replace('|', '/', $url);
799
        $url = trim($url, '/');
800
801
        if (strpos($url, '/')) {
802
            // [控制器/操作]
803
            $path = explode('/', $url);
804
        } else {
805
            $path = [$url];
806
        }
807
808
        return $path;
809
    }
810
811
    /**
812
     * 生成路由的正则规则
813
     * @access protected
814
     * @param  string $rule 路由规则
0 ignored issues
show
Coding Style introduced by
Expected 10 spaces after parameter name; 1 found
Loading history...
815
     * @param  array  $match 匹配的变量
0 ignored issues
show
Coding Style introduced by
Expected 9 spaces after parameter name; 1 found
Loading history...
816
     * @param  array  $pattern   路由变量规则
0 ignored issues
show
Coding Style introduced by
Expected 7 spaces after parameter name; 3 found
Loading history...
817
     * @param  array  $option    路由参数
0 ignored issues
show
Coding Style introduced by
Expected 8 spaces after parameter name; 4 found
Loading history...
818
     * @param  bool   $completeMatch   路由是否完全匹配
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 3 found
Loading history...
819
     * @param  string $suffix   路由正则变量后缀
0 ignored issues
show
Coding Style introduced by
Expected 8 spaces after parameter name; 3 found
Loading history...
820
     * @return string
821
     */
822
    protected function buildRuleRegex(string $rule, array $match, array $pattern = [], array $option = [], bool $completeMatch = false, string $suffix = ''): string
823
    {
824
        foreach ($match as $name) {
825
            $replace[] = $this->buildNameRegex($name, $pattern, $suffix);
826
        }
827
828
        // 是否区分 / 地址访问
829
        if ('/' != $rule) {
830
            if (!empty($option['remove_slash'])) {
831
                $rule = rtrim($rule, '/');
832
            } elseif (substr($rule, -1) == '/') {
833
                $rule     = rtrim($rule, '/');
834
                $hasSlash = true;
835
            }
836
        }
837
838
        $regex = str_replace(array_unique($match), array_unique($replace), $rule);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $replace seems to be defined by a foreach iteration on line 824. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
839
        $regex = str_replace([')?/', ')/', ')?-', ')-', '\\\\/'], [')\/', ')\/', ')\-', ')\-', '\/'], $regex);
840
841
        if (isset($hasSlash)) {
842
            $regex .= '\/';
843
        }
844
845
        return $regex . ($completeMatch ? '$' : '');
846
    }
847
848
    /**
849
     * 生成路由变量的正则规则
850
     * @access protected
851
     * @param  string $name    路由变量
852
     * @param  array  $pattern 变量规则
853
     * @param  string $suffix  路由正则变量后缀
854
     * @return string
855
     */
856
    protected function buildNameRegex(string $name, array $pattern, string $suffix): string
857
    {
858
        $optional = '';
859
        $slash    = substr($name, 0, 1);
860
861
        if (in_array($slash, ['/', '-'])) {
862
            $prefix = '\\' . $slash;
863
            $name   = substr($name, 1);
864
            $slash  = substr($name, 0, 1);
865
        } else {
866
            $prefix = '';
867
        }
868
869
        if ('<' != $slash) {
870
            return $prefix . preg_quote($name, '/');
871
        }
872
873
        if (strpos($name, '?')) {
874
            $name     = substr($name, 1, -2);
875
            $optional = '?';
876
        } elseif (strpos($name, '>')) {
877
            $name = substr($name, 1, -1);
878
        }
879
880
        if (isset($pattern[$name])) {
881
            $nameRule = $pattern[$name];
882
            if (0 === strpos($nameRule, '/') && '/' == substr($nameRule, -1)) {
883
                $nameRule = substr($nameRule, 1, -1);
884
            }
885
        } else {
886
            $nameRule = $this->router->config('default_route_pattern');
887
        }
888
889
        return '(' . $prefix . '(?<' . $name . $suffix . '>' . $nameRule . '))' . $optional;
890
    }
891
892
    /**
893
     * 设置路由参数
894
     * @access public
895
     * @param  string $method 方法名
896
     * @param  array  $args   调用参数
897
     * @return $this
898
     */
899
    public function __call($method, $args)
900
    {
901
        if (count($args) > 1) {
902
            $args[0] = $args;
903
        }
904
        array_unshift($args, $method);
905
906
        return call_user_func_array([$this, 'setOption'], $args);
907
    }
908
909
    public function __sleep()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __sleep()
Loading history...
910
    {
911
        return ['name', 'rule', 'route', 'method', 'vars', 'option', 'pattern'];
912
    }
913
914
    public function __wakeup()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __wakeup()
Loading history...
915
    {
916
        $this->router = Container::pull('route');
917
    }
918
919
    public function __debugInfo()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __debugInfo()
Loading history...
920
    {
921
        return [
922
            'name'    => $this->name,
923
            'rule'    => $this->rule,
924
            'route'   => $this->route,
925
            'method'  => $this->method,
926
            'vars'    => $this->vars,
927
            'option'  => $this->option,
928
            'pattern' => $this->pattern,
929
        ];
930
    }
931
}
932