Completed
Push — 6.0 ( 7d6f90...b04f47 )
by yun
02:12
created

RuleGroup::check()   C

Complexity

Conditions 12
Paths 89

Size

Total Lines 55
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 12.5898

Importance

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