RuleGroup::check()   C
last analyzed

Complexity

Conditions 12
Paths 61

Size

Total Lines 51
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 13.152

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 12
eloc 26
c 2
b 0
f 0
nc 61
nop 3
dl 0
loc 51
ccs 20
cts 25
cp 0.8
crap 13.152
rs 6.9666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
// +----------------------------------------------------------------------
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2021 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\Exception;
18
use think\Request;
19
use think\Route;
20
21
/**
22
 * 路由分组类
23
 */
24
class RuleGroup extends Rule
25
{
26
    /**
27
     * 分组路由(包括子分组)
28
     * @var array
29
     */
30
    protected $rules = [];
31
32
    /**
33
     * 分组路由规则
34
     * @var mixed
35
     */
36
    protected $rule;
37
38
    /**
39
     * MISS路由
40
     * @var RuleItem
41
     */
42
    protected $miss;
43
44
    /**
45
     * 完整名称
46
     * @var string
47
     */
48
    protected $fullName;
49
50
    /**
51
     * 分组别名
52
     * @var string
53
     */
54
    protected $alias;
55
56
    /**
57
     * 架构函数
58
     * @access public
59
     * @param  Route     $router 路由对象
60
     * @param  RuleGroup $parent 上级对象
61
     * @param  string    $name   分组名称
62
     * @param  mixed     $rule   分组路由
63
     */
64 6
    public function __construct(Route $router, RuleGroup $parent = null, string $name = '', $rule = null)
65
    {
66 6
        $this->router = $router;
67 6
        $this->parent = $parent;
68 6
        $this->rule   = $rule;
69 6
        $this->name   = trim($name, '/');
70
71 6
        $this->setFullName();
72
73 6
        if ($this->parent) {
74 6
            $this->domain = $this->parent->getDomain();
75 6
            $this->parent->addRuleItem($this);
76
        }
77
78 6
        if ($router->isTest()) {
79
            $this->lazy(false);
80
        }
81 6
    }
82
83
    /**
84
     * 设置分组的路由规则
85
     * @access public
86
     * @return void
87
     */
88 6
    protected function setFullName(): void
89
    {
90 6
        if (false !== strpos($this->name, ':')) {
91
            $this->name = preg_replace(['/\[\:(\w+)\]/', '/\:(\w+)/'], ['<\1?>', '<\1>'], $this->name);
92
        }
93
94 6
        if ($this->parent && $this->parent->getFullName()) {
95
            $this->fullName = $this->parent->getFullName() . ($this->name ? '/' . $this->name : '');
96
        } else {
97 6
            $this->fullName = $this->name;
98
        }
99
100 6
        if ($this->name) {
101 3
            $this->router->getRuleName()->setGroup($this->name, $this);
102
        }
103 6
    }
104
105
    /**
106
     * 获取所属域名
107
     * @access public
108
     * @return string
109
     */
110 15
    public function getDomain(): string
111
    {
112 15
        return $this->domain ?: '-';
113
    }
114
115
    /**
116
     * 获取分组别名
117
     * @access public
118
     * @return string
119
     */
120
    public function getAlias(): string
121
    {
122
        return $this->alias ?: '';
123
    }
124
125
    /**
126
     * 检测分组路由
127
     * @access public
128
     * @param  Request $request       请求对象
129
     * @param  string  $url           访问地址
130
     * @param  bool    $completeMatch 路由是否完全匹配
131
     * @return Dispatch|false
132
     */
133 33
    public function check(Request $request, string $url, bool $completeMatch = false)
134
    {
135
        // 检查分组有效性
136 33
        if (!$this->checkOption($this->option, $request) || !$this->checkUrl($url)) {
137 3
            return false;
138
        }
139
140
        // 解析分组路由
141 33
        if ($this instanceof Resource) {
142 3
            $this->buildResourceRule();
143
        } else {
144 33
            $this->parseGroupRule($this->rule);
145
        }
146
147
        // 获取当前路由规则
148 33
        $method = strtolower($request->method());
149 33
        $rules  = $this->getRules($method);
150 33
        $option = $this->getOption();
151
152 33
        if (isset($option['complete_match'])) {
153 3
            $completeMatch = $option['complete_match'];
154
        }
155
156 33
        if (!empty($option['merge_rule_regex'])) {
157
            // 合并路由正则规则进行路由匹配检查
158
            $result = $this->checkMergeRuleRegex($request, $rules, $url, $completeMatch);
159
160
            if (false !== $result) {
161
                return $result;
162
            }
163
        }
164
165
        // 检查分组路由
166 33
        foreach ($rules as $key => $item) {
167 30
            $result = $item[1]->check($request, $url, $completeMatch);
168
169 30
            if (false !== $result) {
170 30
                return $result;
171
            }
172
        }
173
174 9
        if (!empty($option['dispatcher'])) {
175
            $result = $this->parseRule($request, '', $option['dispatcher'], $url, $option);
176 9
        } elseif ($this->miss && in_array($this->miss->getMethod(), ['*', $method])) {
177
            // 未匹配所有路由的路由规则处理
178
            $result = $this->parseRule($request, '', $this->miss->getRoute(), $url, $this->miss->getOption());
179
        } else {
180 9
            $result = false;
181
        }
182
183 9
        return $result;
184
    }
185
186
    /**
187
     * 分组URL匹配检查
188
     * @access protected
189
     * @param  string $url URL
190
     * @return bool
191
     */
192 33
    protected function checkUrl(string $url): bool
193
    {
194 33
        if ($this->fullName) {
195 3
            $pos = strpos($this->fullName, '<');
196
197 3
            if (false !== $pos) {
198
                $str = substr($this->fullName, 0, $pos);
199
            } else {
200 3
                $str = $this->fullName;
201
            }
202
203 3
            if ($str && 0 !== stripos(str_replace('|', '/', $url), $str)) {
204 3
                return false;
205
            }
206
        }
207
208 33
        return true;
209
    }
210
211
    /**
212
     * 设置路由分组别名
213
     * @access public
214
     * @param  string $alias 路由分组别名
215
     * @return $this
216
     */
217
    public function alias(string $alias)
218
    {
219
        $this->alias = $alias;
220
        $this->router->getRuleName()->setGroup($alias, $this);
221
222
        return $this;
223
    }
224
225
    /**
226
     * 延迟解析分组的路由规则
227
     * @access public
228
     * @param  bool $lazy 路由是否延迟解析
229
     * @return $this
230
     */
231 9
    public function lazy(bool $lazy = true)
232
    {
233 9
        if (!$lazy) {
234 9
            $this->parseGroupRule($this->rule);
235 9
            $this->rule = null;
236
        }
237
238 9
        return $this;
239
    }
240
241
    /**
242
     * 解析分组和域名的路由规则及绑定
243
     * @access public
244
     * @param  mixed $rule 路由规则
245
     * @return void
246
     */
247 33
    public function parseGroupRule($rule): void
248
    {
249 33
        if (is_string($rule) && is_subclass_of($rule, Dispatch::class)) {
250
            $this->dispatcher($rule);
251
            return;
252
        }
253
254 33
        $origin = $this->router->getGroup();
255 33
        $this->router->setGroup($this);
256
257 33
        if ($rule instanceof \Closure) {
258 9
            Container::getInstance()->invokeFunction($rule);
259 33
        } elseif (is_string($rule) && $rule) {
260
            $this->router->bind($rule, $this->domain);
261
        }
262
263 33
        $this->router->setGroup($origin);
264 33
    }
265
266
    /**
267
     * 检测分组路由
268
     * @access public
269
     * @param  Request $request       请求对象
270
     * @param  array   $rules         路由规则
271
     * @param  string  $url           访问地址
272
     * @param  bool    $completeMatch 路由是否完全匹配
273
     * @return Dispatch|false
274
     */
275
    protected function checkMergeRuleRegex(Request $request, array &$rules, string $url, bool $completeMatch)
276
    {
277
        $depr  = $this->router->config('pathinfo_depr');
278
        $url   = $depr . str_replace('|', $depr, $url);
279
        $regex = [];
280
        $items = [];
281
282
        foreach ($rules as $key => $val) {
283
            $item = $val[1];
284
            if ($item instanceof RuleItem) {
285
                $rule = $depr . str_replace('/', $depr, $item->getRule());
286
                if ($depr == $rule && $depr != $url) {
287
                    unset($rules[$key]);
288
                    continue;
289
                }
290
291
                $complete = $item->getOption('complete_match', $completeMatch);
292
293
                if (false === strpos($rule, '<')) {
294
                    if (0 === strcasecmp($rule, $url) || (!$complete && 0 === strncasecmp($rule, $url, strlen($rule)))) {
295
                        return $item->checkRule($request, $url, []);
296
                    }
297
298
                    unset($rules[$key]);
299
                    continue;
300
                }
301
302
                $slash = preg_quote('/-' . $depr, '/');
303
304
                if ($matchRule = preg_split('/[' . $slash . ']<\w+\??>/', $rule, 2)) {
305
                    if ($matchRule[0] && 0 !== strncasecmp($rule, $url, strlen($matchRule[0]))) {
306
                        unset($rules[$key]);
307
                        continue;
308
                    }
309
                }
310
311
                if (preg_match_all('/[' . $slash . ']?<?\w+\??>?/', $rule, $matches)) {
312
                    unset($rules[$key]);
313
                    $pattern = array_merge($this->getPattern(), $item->getPattern());
314
                    $option  = array_merge($this->getOption(), $item->getOption());
315
316
                    $regex[$key] = $this->buildRuleRegex($rule, $matches[0], $pattern, $option, $complete, '_THINK_' . $key);
317
                    $items[$key] = $item;
318
                }
319
            }
320
        }
321
322
        if (empty($regex)) {
323
            return false;
324
        }
325
326
        try {
327
            $result = preg_match('~^(?:' . implode('|', $regex) . ')~u', $url, $match);
328
        } catch (\Exception $e) {
329
            throw new Exception('route pattern error');
330
        }
331
332
        if ($result) {
333
            $var = [];
334
            foreach ($match as $key => $val) {
335
                if (is_string($key) && '' !== $val) {
336
                    [$name, $pos] = explode('_THINK_', $key);
337
338
                    $var[$name] = $val;
339
                }
340
            }
341
342
            if (!isset($pos)) {
343
                foreach ($regex as $key => $item) {
344
                    if (0 === strpos(str_replace(['\/', '\-', '\\' . $depr], ['/', '-', $depr], $item), $match[0])) {
345
                        $pos = $key;
346
                        break;
347
                    }
348
                }
349
            }
350
351
            $rule  = $items[$pos]->getRule();
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $pos does not seem to be defined for all execution paths leading up to this point.
Loading history...
352
            $array = $this->router->getRule($rule);
353
354
            foreach ($array as $item) {
355
                if (in_array($item->getMethod(), ['*', strtolower($request->method())])) {
356
                    $result = $item->checkRule($request, $url, $var);
357
358
                    if (false !== $result) {
359
                        return $result;
360
                    }
361
                }
362
            }
363
        }
364
365
        return false;
366
    }
367
368
    /**
369
     * 获取分组的MISS路由
370
     * @access public
371
     * @return RuleItem|null
372
     */
373
    public function getMissRule():  ? RuleItem
374
    {
375
        return $this->miss;
376
    }
377
378
    /**
379
     * 注册MISS路由
380
     * @access public
381
     * @param  string|Closure $route  路由地址
382
     * @param  string         $method 请求类型
383
     * @return RuleItem
384
     */
385
    public function miss($route, string $method = '*') : RuleItem
386
    {
387
        // 创建路由规则实例
388
        $ruleItem = new RuleItem($this->router, $this, null, '', $route, strtolower($method));
389
390
        $ruleItem->setMiss();
391
        $this->miss = $ruleItem;
392
393
        return $ruleItem;
394
    }
395
396
    /**
397
     * 添加分组下的路由规则
398
     * @access public
399
     * @param  string $rule   路由规则
400
     * @param  mixed  $route  路由地址
401
     * @param  string $method 请求类型
402
     * @return RuleItem
403
     */
404 30
    public function addRule(string $rule, $route = null, string $method = '*'): RuleItem
405
    {
406
        // 读取路由标识
407 30
        if (is_string($route)) {
408 12
            $name = $route;
409
        } else {
410 21
            $name = null;
411
        }
412
413 30
        $method = strtolower($method);
414
415 30
        if ('' === $rule || '/' === $rule) {
416 6
            $rule .= '$';
417
        }
418
419
        // 创建路由规则实例
420 30
        $ruleItem = new RuleItem($this->router, $this, $name, $rule, $route, $method);
421
422 30
        $this->addRuleItem($ruleItem, $method);
423
424 30
        return $ruleItem;
425
    }
426
427
    /**
428
     * 注册分组下的路由规则
429
     * @access public
430
     * @param  Rule   $rule   路由规则
431
     * @param  string $method 请求类型
432
     * @return $this
433
     */
434 30
    public function addRuleItem(Rule $rule, string $method = '*')
435
    {
436 30
        if (strpos($method, '|')) {
437
            $rule->method($method);
438
            $method = '*';
439
        }
440
441 30
        $this->rules[] = [$method, $rule];
442
443 30
        if ($rule instanceof RuleItem && 'options' != $method) {
444 30
            $this->rules[] = ['options', $rule->setAutoOptions()];
445
        }
446
447 30
        return $this;
448
    }
449
450
    /**
451
     * 设置分组的路由前缀
452
     * @access public
453
     * @param  string $prefix 路由前缀
454
     * @return $this
455
     */
456
    public function prefix(string $prefix)
457
    {
458
        if ($this->parent && $this->parent->getOption('prefix')) {
459
            $prefix = $this->parent->getOption('prefix') . $prefix;
460
        }
461
462
        return $this->setOption('prefix', $prefix);
463
    }
464
465
    /**
466
     * 合并分组的路由规则正则
467
     * @access public
468
     * @param  bool $merge 是否合并
469
     * @return $this
470
     */
471 9
    public function mergeRuleRegex(bool $merge = true)
472
    {
473 9
        return $this->setOption('merge_rule_regex', $merge);
474
    }
475
476
    /**
477
     * 设置分组的Dispatch调度
478
     * @access public
479
     * @param  string $dispatch 调度类
480
     * @return $this
481
     */
482
    public function dispatcher(string $dispatch)
483
    {
484
        return $this->setOption('dispatcher', $dispatch);
485
    }
486
487
    /**
488
     * 获取完整分组Name
489
     * @access public
490
     * @return string
491
     */
492 30
    public function getFullName(): string
493
    {
494 30
        return $this->fullName ?: '';
495
    }
496
497
    /**
498
     * 获取分组的路由规则
499
     * @access public
500
     * @param  string $method 请求类型
501
     * @return array
502
     */
503 33
    public function getRules(string $method = ''): array
504
    {
505 33
        if ('' === $method) {
506
            return $this->rules;
507
        }
508
509
        return array_filter($this->rules, function ($item) use ($method) {
510 30
            return $method == $item[0] || '*' == $item[0];
511 33
        });
512
    }
513
514
    /**
515
     * 清空分组下的路由规则
516
     * @access public
517
     * @return void
518
     */
519
    public function clear(): void
520
    {
521
        $this->rules = [];
522
    }
523
}
524