Completed
Branch 6.0 (d30585)
by yun
04:17
created

Rule::__wakeup()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
773
     * @param  array  $var 变量
774
     * @return void
775
     */
776 18
    protected function parseUrlParams(string $url, array &$var = []): void
777
    {
778 18
        if ($url) {
779
            preg_replace_callback('/(\w+)\|([^\|]+)/', function ($match) use (&$var) {
780
                $var[$match[1]] = strip_tags($match[2]);
781
            }, $url);
782
        }
783 18
    }
784
785
    /**
786
     * 解析URL的pathinfo参数
787
     * @access public
788
     * @param  string $url URL地址
789
     * @return array
790
     */
791 12
    public function parseUrlPath(string $url): array
792
    {
793
        // 分隔符替换 确保路由定义使用统一的分隔符
794 12
        $url = str_replace('|', '/', $url);
795 12
        $url = trim($url, '/');
796
797 12
        if (strpos($url, '/')) {
798
            // [控制器/操作]
799 12
            $path = explode('/', $url);
800
        } else {
801
            $path = [$url];
802
        }
803
804 12
        return $path;
805
    }
806
807
    /**
808
     * 生成路由的正则规则
809
     * @access protected
810
     * @param  string $rule 路由规则
811
     * @param  array  $match 匹配的变量
812
     * @param  array  $pattern   路由变量规则
813
     * @param  array  $option    路由参数
814
     * @param  bool   $completeMatch   路由是否完全匹配
815
     * @param  string $suffix   路由正则变量后缀
816
     * @return string
817
     */
818 3
    protected function buildRuleRegex(string $rule, array $match, array $pattern = [], array $option = [], bool $completeMatch = false, string $suffix = ''): string
819
    {
820 3
        foreach ($match as $name) {
821 3
            $replace[] = $this->buildNameRegex($name, $pattern, $suffix);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$replace was never initialized. Although not strictly required by PHP, it is generally a good practice to add $replace = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
822
        }
823
824
        // 是否区分 / 地址访问
825 3
        if ('/' != $rule) {
826 3
            if (!empty($option['remove_slash'])) {
827
                $rule = rtrim($rule, '/');
828 3
            } elseif (substr($rule, -1) == '/') {
829
                $rule     = rtrim($rule, '/');
830
                $hasSlash = true;
831
            }
832
        }
833
834 3
        $regex = str_replace(array_unique($match), array_unique($replace), $rule);
0 ignored issues
show
Bug introduced by
The variable $replace does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
835 3
        $regex = str_replace([')?/', ')/', ')?-', ')-', '\\\\/'], [')\/', ')\/', ')\-', ')\-', '\/'], $regex);
836
837 3
        if (isset($hasSlash)) {
838
            $regex .= '\/';
839
        }
840
841 3
        return $regex . ($completeMatch ? '$' : '');
842
    }
843
844
    /**
845
     * 生成路由变量的正则规则
846
     * @access protected
847
     * @param  string $name    路由变量
848
     * @param  array  $pattern 变量规则
849
     * @param  string $suffix  路由正则变量后缀
850
     * @return string
851
     */
852 3
    protected function buildNameRegex(string $name, array $pattern, string $suffix): string
853
    {
854 3
        $optional = '';
855 3
        $slash    = substr($name, 0, 1);
856
857 3
        if (in_array($slash, ['/', '-'])) {
858 3
            $prefix = '\\' . $slash;
859 3
            $name   = substr($name, 1);
860 3
            $slash  = substr($name, 0, 1);
861
        } else {
862
            $prefix = '';
863
        }
864
865 3
        if ('<' != $slash) {
866 3
            return $prefix . preg_quote($name, '/');
867
        }
868
869 3
        if (strpos($name, '?')) {
870
            $name     = substr($name, 1, -2);
871
            $optional = '?';
872 3
        } elseif (strpos($name, '>')) {
873 3
            $name = substr($name, 1, -1);
874
        }
875
876 3
        if (isset($pattern[$name])) {
877
            $nameRule = $pattern[$name];
878
            if (0 === strpos($nameRule, '/') && '/' == substr($nameRule, -1)) {
879
                $nameRule = substr($nameRule, 1, -1);
880
            }
881
        } else {
882 3
            $nameRule = $this->router->config('default_route_pattern');
883
        }
884
885 3
        return '(' . $prefix . '(?<' . $name . $suffix . '>' . $nameRule . '))' . $optional;
886
    }
887
888
    /**
889
     * 设置路由参数
890
     * @access public
891
     * @param  string $method 方法名
892
     * @param  array  $args   调用参数
893
     * @return $this
894
     */
895
    public function __call($method, $args)
896
    {
897
        if (count($args) > 1) {
898
            $args[0] = $args;
899
        }
900
        array_unshift($args, $method);
901
902
        return call_user_func_array([$this, 'setOption'], $args);
903
    }
904
905
    public function __sleep()
906
    {
907
        return ['name', 'rule', 'route', 'method', 'vars', 'option', 'pattern'];
908
    }
909
910
    public function __wakeup()
911
    {
912
        $this->router = Container::pull('route');
913
    }
914
915
    public function __debugInfo()
916
    {
917
        return [
918
            'name'    => $this->name,
919
            'rule'    => $this->rule,
920
            'route'   => $this->route,
921
            'method'  => $this->method,
922
            'vars'    => $this->vars,
923
            'option'  => $this->option,
924
            'pattern' => $this->pattern,
925
        ];
926
    }
927
}
928