Passed
Push — 5.1 ( 1919c6...c43485 )
by liu
09:50
created

Rule::checkCrossDomain()   B

Complexity

Conditions 7
Paths 13

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 16
nc 13
nop 1
dl 0
loc 27
rs 8.8333
c 0
b 0
f 0
1
<?php
2
// +----------------------------------------------------------------------
1 ignored issue
show
Coding Style introduced by
You must use "/**" style comments for a file comment
Loading history...
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2018 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
12
namespace think\route;
13
14
use think\Container;
15
use think\Request;
16
use think\Response;
17
use think\route\dispatch\Callback as CallbackDispatch;
18
use think\route\dispatch\Controller as ControllerDispatch;
19
use think\route\dispatch\Module as ModuleDispatch;
20
use think\route\dispatch\Redirect as RedirectDispatch;
21
use think\route\dispatch\Response as ResponseDispatch;
22
use think\route\dispatch\View as ViewDispatch;
23
24
abstract class Rule
1 ignored issue
show
Coding Style introduced by
Missing class doc comment
Loading history...
25
{
26
    /**
27
     * 路由标识
28
     * @var string
29
     */
30
    protected $name;
31
32
    /**
33
     * 路由对象
34
     * @var Route
0 ignored issues
show
Bug introduced by
The type think\route\Route was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

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

658
                if ($httpOrigin && strpos(/** @scrutinizer ignore-type */ config('cookie.domain'), $httpOrigin)) {
Loading history...
659
                    $header['Access-Control-Allow-Origin'] = $httpOrigin;
660
                } else {
661
                    $header['Access-Control-Allow-Origin'] = '*';
662
                }
663
            }
664
665
            $this->option['header'] = $header;
666
667
            if ($request->method(true) == 'OPTIONS') {
668
                return new ResponseDispatch($request, $this, Response::create()->code(204)->header($header));
669
            }
670
        }
671
    }
672
673
    /**
674
     * 设置路由规则全局有效
675
     * @access public
676
     * @return $this
677
     */
678
    public function crossDomainRule()
679
    {
680
        if ($this instanceof RuleGroup) {
681
            $method = '*';
682
        } else {
683
            $method = $this->method;
684
        }
685
686
        $this->router->setCrossDomainRule($this, $method);
687
688
        return $this;
689
    }
690
691
    /**
692
     * 合并分组参数
693
     * @access public
694
     * @return array
695
     */
696
    public function mergeGroupOptions()
697
    {
698
        if (!$this->lockOption) {
699
            $parentOption = $this->parent->getOption();
700
            // 合并分组参数
701
            foreach ($this->mergeOptions as $item) {
702
                if (isset($parentOption[$item]) && isset($this->option[$item])) {
703
                    $this->option[$item] = array_merge($parentOption[$item], $this->option[$item]);
704
                }
705
            }
706
707
            $this->option     = array_merge($parentOption, $this->option);
708
            $this->lockOption = true;
709
        }
710
711
        return $this->option;
712
    }
713
714
    /**
715
     * 解析匹配到的规则路由
716
     * @access public
717
     * @param  Request   $request 请求对象
718
     * @param  string    $rule 路由规则
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
719
     * @param  string    $route 路由地址
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
720
     * @param  string    $url URL地址
0 ignored issues
show
Coding Style introduced by
Expected 5 spaces after parameter name; 1 found
Loading history...
721
     * @param  array     $option 路由参数
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter name; 1 found
Loading history...
722
     * @param  array     $matches 匹配的变量
723
     * @return Dispatch
724
     */
725
    public function parseRule($request, $rule, $route, $url, $option = [], $matches = [])
726
    {
727
        if (is_string($route) && isset($option['prefix'])) {
728
            // 路由地址前缀
729
            $route = $option['prefix'] . $route;
730
        }
731
732
        // 替换路由地址中的变量
733
        if (is_string($route) && !empty($matches)) {
734
            $search = $replace = [];
735
736
            foreach ($matches as $key => $value) {
737
                $search[]  = '<' . $key . '>';
738
                $replace[] = $value;
739
740
                $search[]  = ':' . $key;
741
                $replace[] = $value;
742
            }
743
744
            $route = str_replace($search, $replace, $route);
745
        }
746
747
        // 解析额外参数
748
        $count = substr_count($rule, '/');
749
        $url   = array_slice(explode('|', $url), $count + 1);
750
        $this->parseUrlParams($request, implode('|', $url), $matches);
751
752
        $this->vars    = $matches;
753
        $this->option  = $option;
754
        $this->doAfter = true;
755
756
        // 发起路由调度
757
        return $this->dispatch($request, $route, $option);
758
    }
759
760
    /**
761
     * 检查路由前置行为
762
     * @access protected
763
     * @param  mixed   $before 前置行为
764
     * @return mixed
765
     */
766
    protected function checkBefore($before)
767
    {
768
        $hook = Container::get('hook');
769
770
        foreach ((array) $before as $behavior) {
771
            $result = $hook->exec($behavior);
772
773
            if (false === $result) {
774
                return false;
775
            }
776
        }
777
    }
778
779
    /**
780
     * 发起路由调度
781
     * @access protected
782
     * @param  Request   $request Request对象
783
     * @param  mixed     $route  路由地址
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 2 found
Loading history...
784
     * @param  array     $option 路由参数
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter name; 1 found
Loading history...
785
     * @return Dispatch
786
     */
787
    protected function dispatch($request, $route, $option)
788
    {
789
        if ($route instanceof \Closure) {
790
            // 执行闭包
791
            $result = new CallbackDispatch($request, $this, $route);
792
        } elseif ($route instanceof Response) {
793
            $result = new ResponseDispatch($request, $this, $route);
794
        } elseif (isset($option['view']) && false !== $option['view']) {
795
            $result = new ViewDispatch($request, $this, $route, is_array($option['view']) ? $option['view'] : []);
796
        } elseif (!empty($option['redirect']) || 0 === strpos($route, '/') || strpos($route, '://')) {
797
            // 路由到重定向地址
798
            $result = new RedirectDispatch($request, $this, $route, [], isset($option['status']) ? $option['status'] : 301);
799
        } elseif (false !== strpos($route, '\\')) {
800
            // 路由到方法
801
            $result = $this->dispatchMethod($request, $route);
802
        } elseif (0 === strpos($route, '@')) {
803
            // 路由到控制器
804
            $result = $this->dispatchController($request, substr($route, 1));
805
        } else {
806
            // 路由到模块/控制器/操作
807
            $result = $this->dispatchModule($request, $route);
808
        }
809
810
        return $result;
811
    }
812
813
    /**
814
     * 解析URL地址为 模块/控制器/操作
815
     * @access protected
816
     * @param  Request   $request Request对象
817
     * @param  string    $route 路由地址
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
818
     * @return CallbackDispatch
819
     */
820
    protected function dispatchMethod($request, $route)
821
    {
822
        list($path, $var) = $this->parseUrlPath($route);
823
824
        $route  = str_replace('/', '@', implode('/', $path));
825
        $method = strpos($route, '@') ? explode('@', $route) : $route;
826
827
        return new CallbackDispatch($request, $this, $method, $var);
828
    }
829
830
    /**
831
     * 解析URL地址为 模块/控制器/操作
832
     * @access protected
833
     * @param  Request   $request Request对象
834
     * @param  string    $route 路由地址
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
835
     * @return ControllerDispatch
836
     */
837
    protected function dispatchController($request, $route)
838
    {
839
        list($route, $var) = $this->parseUrlPath($route);
840
841
        $result = new ControllerDispatch($request, $this, implode('/', $route), $var);
842
843
        $request->setAction(array_pop($route));
844
        $request->setController($route ? array_pop($route) : $this->getConfig('default_controller'));
845
        $request->setModule($route ? array_pop($route) : $this->getConfig('default_module'));
846
847
        return $result;
848
    }
849
850
    /**
851
     * 解析URL地址为 模块/控制器/操作
852
     * @access protected
853
     * @param  Request   $request Request对象
854
     * @param  string    $route 路由地址
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
855
     * @return ModuleDispatch
856
     */
857
    protected function dispatchModule($request, $route)
858
    {
859
        list($path, $var) = $this->parseUrlPath($route);
860
861
        $action     = array_pop($path);
862
        $controller = !empty($path) ? array_pop($path) : null;
863
        $module     = $this->getConfig('app_multi_module') && !empty($path) ? array_pop($path) : null;
864
        $method     = $request->method();
865
866
        if ($this->getConfig('use_action_prefix') && $this->router->getMethodPrefix($method)) {
867
            $prefix = $this->router->getMethodPrefix($method);
868
            // 操作方法前缀支持
869
            $action = 0 !== strpos($action, $prefix) ? $prefix . $action : $action;
870
        }
871
872
        // 设置当前请求的路由变量
873
        $request->setRouteVars($var);
874
875
        // 路由到模块/控制器/操作
876
        return new ModuleDispatch($request, $this, [$module, $controller, $action], ['convert' => false]);
877
    }
878
879
    /**
880
     * 路由检查
881
     * @access protected
882
     * @param  array     $option 路由参数
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter name; 1 found
Loading history...
883
     * @param  Request   $request Request对象
884
     * @return bool
885
     */
886
    protected function checkOption($option, Request $request)
887
    {
888
        // 请求类型检测
889
        if (!empty($option['method'])) {
890
            if (is_string($option['method']) && false === stripos($option['method'], $request->method())) {
891
                return false;
892
            }
893
        }
894
895
        // AJAX PJAX 请求检查
896
        foreach (['ajax', 'pjax', 'mobile'] as $item) {
897
            if (isset($option[$item])) {
898
                $call = 'is' . $item;
899
                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...
900
                    return false;
901
                }
902
            }
903
        }
904
905
        // 伪静态后缀检测
906
        if ($request->url() != '/' && ((isset($option['ext']) && false === stripos('|' . $option['ext'] . '|', '|' . $request->ext() . '|'))
907
            || (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...
908
            return false;
909
        }
910
911
        // 域名检查
912
        if ((isset($option['domain']) && !in_array($option['domain'], [$request->host(true), $request->subDomain()]))) {
913
            return false;
914
        }
915
916
        // HTTPS检查
917
        if ((isset($option['https']) && $option['https'] && !$request->isSsl())
918
            || (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...
919
            return false;
920
        }
921
922
        // 请求参数检查
923
        if (isset($option['filter'])) {
924
            foreach ($option['filter'] as $name => $value) {
925
                if ($request->param($name, '', null) != $value) {
926
                    return false;
927
                }
928
            }
929
        }
930
        return true;
931
    }
932
933
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $url should have a doc-comment as per coding-style.
Loading history...
934
     * 解析URL地址中的参数Request对象
935
     * @access protected
936
     * @param  Request   $request
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
937
     * @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...
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
938
     * @param  array     $var 变量
0 ignored issues
show
Coding Style introduced by
Expected 5 spaces after parameter name; 1 found
Loading history...
939
     * @return void
940
     */
941
    protected function parseUrlParams($request, $url, &$var = [])
942
    {
943
        if ($url) {
944
            if ($this->getConfig('url_param_type')) {
945
                $var += explode('|', $url);
946
            } else {
947
                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...
948
                    $var[$match[1]] = strip_tags($match[2]);
949
                }, $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...
950
            }
951
        }
952
    }
953
954
    /**
955
     * 解析URL的pathinfo参数和变量
956
     * @access public
957
     * @param  string    $url URL地址
958
     * @return array
959
     */
960
    public function parseUrlPath($url)
961
    {
962
        // 分隔符替换 确保路由定义使用统一的分隔符
963
        $url = str_replace('|', '/', $url);
964
        $url = trim($url, '/');
965
        $var = [];
966
967
        if (false !== strpos($url, '?')) {
968
            // [模块/控制器/操作?]参数1=值1&参数2=值2...
969
            $info = parse_url($url);
970
            $path = explode('/', $info['path']);
971
            parse_str($info['query'], $var);
972
        } elseif (strpos($url, '/')) {
973
            // [模块/控制器/操作]
974
            $path = explode('/', $url);
975
        } elseif (false !== strpos($url, '=')) {
976
            // 参数1=值1&参数2=值2...
977
            $path = [];
978
            parse_str($url, $var);
979
        } else {
980
            $path = [$url];
981
        }
982
983
        return [$path, $var];
984
    }
985
986
    /**
987
     * 生成路由的正则规则
988
     * @access protected
989
     * @param  string    $rule 路由规则
0 ignored issues
show
Coding Style introduced by
Expected 10 spaces after parameter name; 1 found
Loading history...
990
     * @param  array     $match 匹配的变量
0 ignored issues
show
Coding Style introduced by
Expected 9 spaces after parameter name; 1 found
Loading history...
991
     * @param  array     $pattern   路由变量规则
0 ignored issues
show
Coding Style introduced by
Expected 7 spaces after parameter name; 3 found
Loading history...
992
     * @param  array     $option    路由参数
0 ignored issues
show
Coding Style introduced by
Expected 8 spaces after parameter name; 4 found
Loading history...
993
     * @param  bool      $completeMatch   路由是否完全匹配
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 3 found
Loading history...
994
     * @param  string    $suffix   路由正则变量后缀
0 ignored issues
show
Coding Style introduced by
Expected 8 spaces after parameter name; 3 found
Loading history...
995
     * @return string
996
     */
997
    protected function buildRuleRegex($rule, $match, $pattern = [], $option = [], $completeMatch = false, $suffix = '')
998
    {
999
        foreach ($match as $name) {
1000
            $replace[] = $this->buildNameRegex($name, $pattern, $suffix);
0 ignored issues
show
Bug introduced by
$pattern of type array is incompatible with the type string expected by parameter $pattern of think\route\Rule::buildNameRegex(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1000
            $replace[] = $this->buildNameRegex($name, /** @scrutinizer ignore-type */ $pattern, $suffix);
Loading history...
1001
        }
1002
1003
        // 是否区分 / 地址访问
1004
        if ('/' != $rule) {
1005
            if (!empty($option['remove_slash'])) {
1006
                $rule = rtrim($rule, '/');
1007
            } elseif (substr($rule, -1) == '/') {
1008
                $rule     = rtrim($rule, '/');
1009
                $hasSlash = true;
1010
            }
1011
        }
1012
1013
        $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 999. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
1014
        $regex = str_replace([')?/', ')/', ')?-', ')-', '\\\\/'], [')\/', ')\/', ')\-', ')\-', '\/'], $regex);
1015
1016
        if (isset($hasSlash)) {
1017
            $regex .= '\/';
1018
        }
1019
1020
        return $regex . ($completeMatch ? '$' : '');
1021
    }
1022
1023
    /**
1024
     * 生成路由变量的正则规则
1025
     * @access protected
1026
     * @param  string    $name      路由变量
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 6 found
Loading history...
1027
     * @param  string    $pattern   变量规则
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 3 found
Loading history...
1028
     * @param  string    $suffix    路由正则变量后缀
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter name; 4 found
Loading history...
1029
     * @return string
1030
     */
1031
    protected function buildNameRegex($name, $pattern, $suffix)
1032
    {
1033
        $optional = '';
1034
        $slash    = substr($name, 0, 1);
1035
1036
        if (in_array($slash, ['/', '-'])) {
1037
            $prefix = '\\' . $slash;
1038
            $name   = substr($name, 1);
1039
            $slash  = substr($name, 0, 1);
1040
        } else {
1041
            $prefix = '';
1042
        }
1043
1044
        if ('<' != $slash) {
1045
            return $prefix . preg_quote($name, '/');
1046
        }
1047
1048
        if (strpos($name, '?')) {
1049
            $name     = substr($name, 1, -2);
1050
            $optional = '?';
1051
        } elseif (strpos($name, '>')) {
1052
            $name = substr($name, 1, -1);
1053
        }
1054
1055
        if (isset($pattern[$name])) {
1056
            $nameRule = $pattern[$name];
1057
            if (0 === strpos($nameRule, '/') && '/' == substr($nameRule, -1)) {
1058
                $nameRule = substr($nameRule, 1, -1);
1059
            }
1060
        } else {
1061
            $nameRule = $this->getConfig('default_route_pattern');
1062
        }
1063
1064
        return '(' . $prefix . '(?<' . $name . $suffix . '>' . $nameRule . '))' . $optional;
1065
    }
1066
1067
    /**
1068
     * 分析路由规则中的变量
1069
     * @access protected
1070
     * @param  string    $rule 路由规则
1071
     * @return array
1072
     */
1073
    protected function parseVar($rule)
1074
    {
1075
        // 提取路由规则中的变量
1076
        $var = [];
1077
1078
        if (preg_match_all('/<\w+\??>/', $rule, $matches)) {
1079
            foreach ($matches[0] as $name) {
1080
                $optional = false;
1081
1082
                if (strpos($name, '?')) {
1083
                    $name     = substr($name, 1, -2);
1084
                    $optional = true;
1085
                } else {
1086
                    $name = substr($name, 1, -1);
1087
                }
1088
1089
                $var[$name] = $optional ? 2 : 1;
1090
            }
1091
        }
1092
1093
        return $var;
1094
    }
1095
1096
    /**
1097
     * 设置路由参数
1098
     * @access public
1099
     * @param  string    $method     方法名
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 5 found
Loading history...
1100
     * @param  array     $args       调用参数
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 7 found
Loading history...
1101
     * @return $this
1102
     */
1103
    public function __call($method, $args)
1104
    {
1105
        if (count($args) > 1) {
1106
            $args[0] = $args;
1107
        }
1108
        array_unshift($args, $method);
1109
1110
        return call_user_func_array([$this, 'option'], $args);
1111
    }
1112
1113
    public function __sleep()
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
1114
    {
1115
        return ['name', 'rule', 'route', 'method', 'vars', 'option', 'pattern', 'doAfter'];
1116
    }
1117
1118
    public function __wakeup()
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
1119
    {
1120
        $this->router = Container::get('route');
1121
    }
1122
1123
    public function __debugInfo()
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
1124
    {
1125
        $data = get_object_vars($this);
1126
        unset($data['parent'], $data['router'], $data['route']);
1127
1128
        return $data;
1129
    }
1130
}
1131