Completed
Push — 6.0 ( d30585...7d6f90 )
by yun
02:09
created

RuleGroup::check()   C

Complexity

Conditions 13
Paths 90

Size

Total Lines 57
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 14.0735

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 28
c 1
b 0
f 0
nc 90
nop 3
dl 0
loc 57
ccs 22
cts 27
cp 0.8148
crap 14.0735
rs 6.6166

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;
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 21
    public function check(Request $request, string $url, bool $completeMatch = false)
136
    {
137
        // 检查分组有效性
138 21
        if (!$this->checkOption($this->option, $request) || !$this->checkUrl($url)) {
139 3
            return false;
140
        }
141
142
        // 解析分组路由
143 21
        if ($this instanceof Resource) {
144 3
            $this->buildResourceRule();
145 21
        } elseif ($this->rule instanceof Response) {
146
            return new ResponseDispatch($request, $this, $this->rule);
147
        } else {
148 21
            $this->parseGroupRule($this->rule);
149
        }
150
151
        // 获取当前路由规则
152 21
        $method = strtolower($request->method());
153 21
        $rules  = $this->getRules($method);
154
155 21
        if ($this->parent) {
156
            // 合并分组参数
157 6
            $this->mergeGroupOptions();
158
            // 合并分组变量规则
159 6
            $this->pattern = array_merge($this->parent->getPattern(), $this->pattern);
160
        }
161
162 21
        if (isset($this->option['complete_match'])) {
163 3
            $completeMatch = $this->option['complete_match'];
164
        }
165
166 21
        if (!empty($this->option['merge_rule_regex'])) {
167
            // 合并路由正则规则进行路由匹配检查
168
            $result = $this->checkMergeRuleRegex($request, $rules, $url, $completeMatch);
169
170
            if (false !== $result) {
171
                return $result;
172
            }
173
        }
174
175
        // 检查分组路由
176 21
        foreach ($rules as $key => $item) {
177 18
            $result = $item[1]->check($request, $url, $completeMatch);
178
179 18
            if (false !== $result) {
180 18
                return $result;
181
            }
182
        }
183
184 9
        if ($this->miss && in_array($this->miss->getMethod(), ['*', $method])) {
185
            // 未匹配所有路由的路由规则处理
186
            $result = $this->parseRule($request, '', $this->miss->getRoute(), $url, $this->miss->mergeGroupOptions());
187
        } else {
188 9
            $result = false;
189
        }
190
191 9
        return $result;
192
    }
193
194
    /**
195
     * 分组URL匹配检查
196
     * @access protected
197
     * @param  string $url URL
198
     * @return bool
199
     */
200 21
    protected function checkUrl(string $url): bool
201
    {
202 21
        if ($this->fullName) {
203 3
            $pos = strpos($this->fullName, '<');
204
205 3
            if (false !== $pos) {
206
                $str = substr($this->fullName, 0, $pos);
207
            } else {
208 3
                $str = $this->fullName;
209
            }
210
211 3
            if ($str && 0 !== stripos(str_replace('|', '/', $url), $str)) {
212 3
                return false;
213
            }
214
        }
215
216 21
        return true;
217
    }
218
219
    /**
220
     * 设置路由分组别名
221
     * @access public
222
     * @param  string $alias 路由分组别名
223
     * @return $this
224
     */
225
    public function alias(string $alias)
226
    {
227
        $this->alias = $alias;
228
        $this->router->getRuleName()->setGroup($alias, $this);
229
230
        return $this;
231
    }
232
233
    /**
234
     * 延迟解析分组的路由规则
235
     * @access public
236
     * @param  bool $lazy 路由是否延迟解析
237
     * @return $this
238
     */
239 6
    public function lazy(bool $lazy = true)
240
    {
241 6
        if (!$lazy) {
242 6
            $this->parseGroupRule($this->rule);
243 6
            $this->rule = null;
244
        }
245
246 6
        return $this;
247
    }
248
249
    /**
250
     * 解析分组和域名的路由规则及绑定
251
     * @access public
252
     * @param  mixed $rule 路由规则
253
     * @return void
254
     */
255 21
    public function parseGroupRule($rule): void
256
    {
257 21
        $origin = $this->router->getGroup();
258 21
        $this->router->setGroup($this);
259
260 21
        if ($rule instanceof \Closure) {
261 6
            Container::getInstance()->invokeFunction($rule);
262 21
        } elseif (is_string($rule) && $rule) {
263
            $this->router->bind($rule, $this->domain);
264
        }
265
266 21
        $this->router->setGroup($origin);
267 21
    }
268
269
    /**
270
     * 检测分组路由
271
     * @access public
272
     * @param  Request $request       请求对象
273
     * @param  array   $rules         路由规则
274
     * @param  string  $url           访问地址
275
     * @param  bool    $completeMatch 路由是否完全匹配
276
     * @return Dispatch|false
277
     */
278
    protected function checkMergeRuleRegex(Request $request, array &$rules, string $url, bool $completeMatch)
279
    {
280
        $depr  = $this->router->config('pathinfo_depr');
281
        $url   = $depr . str_replace('|', $depr, $url);
282
        $regex = [];
283
        $items = [];
284
285
        foreach ($rules as $key => $val) {
286
            $item = $val[1];
287
            if ($item instanceof RuleItem) {
288
                $rule = $depr . str_replace('/', $depr, $item->getRule());
289
                if ($depr == $rule && $depr != $url) {
290
                    unset($rules[$key]);
291
                    continue;
292
                }
293
294
                $complete = $item->getOption('complete_match', $completeMatch);
295
296
                if (false === strpos($rule, '<')) {
297
                    if (0 === strcasecmp($rule, $url) || (!$complete && 0 === strncasecmp($rule, $url, strlen($rule)))) {
298
                        return $item->checkRule($request, $url, []);
299
                    }
300
301
                    unset($rules[$key]);
302
                    continue;
303
                }
304
305
                $slash = preg_quote('/-' . $depr, '/');
306
307
                if ($matchRule = preg_split('/[' . $slash . ']<\w+\??>/', $rule, 2)) {
308
                    if ($matchRule[0] && 0 !== strncasecmp($rule, $url, strlen($matchRule[0]))) {
309
                        unset($rules[$key]);
310
                        continue;
311
                    }
312
                }
313
314
                if (preg_match_all('/[' . $slash . ']?<?\w+\??>?/', $rule, $matches)) {
315
                    unset($rules[$key]);
316
                    $pattern = array_merge($this->getPattern(), $item->getPattern());
317
                    $option  = array_merge($this->getOption(), $item->getOption());
318
319
                    $regex[$key] = $this->buildRuleRegex($rule, $matches[0], $pattern, $option, $complete, '_THINK_' . $key);
320
                    $items[$key] = $item;
321
                }
322
            }
323
        }
324
325
        if (empty($regex)) {
326
            return false;
327
        }
328
329
        try {
330
            $result = preg_match('/^(?:' . implode('|', $regex) . ')/u', $url, $match);
331
        } catch (\Exception $e) {
332
            throw new Exception('route pattern error');
333
        }
334
335
        if ($result) {
336
            $var = [];
337
            foreach ($match as $key => $val) {
338
                if (is_string($key) && '' !== $val) {
339
                    [$name, $pos] = explode('_THINK_', $key);
340
341
                    $var[$name] = $val;
342
                }
343
            }
344
345
            if (!isset($pos)) {
346
                foreach ($regex as $key => $item) {
347
                    if (0 === strpos(str_replace(['\/', '\-', '\\' . $depr], ['/', '-', $depr], $item), $match[0])) {
348
                        $pos = $key;
349
                        break;
350
                    }
351
                }
352
            }
353
354
            $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...
355
            $array = $this->router->getRule($rule);
356
357
            foreach ($array as $item) {
358
                if (in_array($item->getMethod(), ['*', strtolower($request->method())])) {
359
                    $result = $item->checkRule($request, $url, $var);
360
361
                    if (false !== $result) {
362
                        return $result;
363
                    }
364
                }
365
            }
366
        }
367
368
        return false;
369
    }
370
371
    /**
372
     * 获取分组的MISS路由
373
     * @access public
374
     * @return RuleItem|null
375
     */
376
    public function getMissRule():  ? RuleItem
377
    {
378
        return $this->miss;
379
    }
380
381
    /**
382
     * 注册MISS路由
383
     * @access public
384
     * @param  string|Closure $route  路由地址
385
     * @param  string         $method 请求类型
386
     * @return RuleItem
387
     */
388
    public function miss($route, string $method = '*') : RuleItem
389
    {
390
        // 创建路由规则实例
391
        $ruleItem = new RuleItem($this->router, $this, null, '', $route, strtolower($method));
392
393
        $ruleItem->setMiss();
394
        $this->miss = $ruleItem;
395
396
        return $ruleItem;
397
    }
398
399
    /**
400
     * 添加分组下的路由规则
401
     * @access public
402
     * @param  string $rule   路由规则
403
     * @param  mixed  $route  路由地址
404
     * @param  string $method 请求类型
405
     * @return RuleItem
406
     */
407 18
    public function addRule(string $rule, $route = null, string $method = '*'): RuleItem
408
    {
409
        // 读取路由标识
410 18
        if (is_string($route)) {
411 12
            $name = $route;
412
        } else {
413 9
            $name = null;
414
        }
415
416 18
        $method = strtolower($method);
417
418 18
        if ('' === $rule || '/' === $rule) {
419 3
            $rule .= '$';
420
        }
421
422
        // 创建路由规则实例
423 18
        $ruleItem = new RuleItem($this->router, $this, $name, $rule, $route, $method);
424
425 18
        $this->addRuleItem($ruleItem, $method);
426
427 18
        return $ruleItem;
428
    }
429
430
    /**
431
     * 注册分组下的路由规则
432
     * @access public
433
     * @param  Rule   $rule   路由规则
434
     * @param  string $method 请求类型
435
     * @return $this
436
     */
437 18
    public function addRuleItem(Rule $rule, string $method = '*')
438
    {
439 18
        if (strpos($method, '|')) {
440
            $rule->method($method);
441
            $method = '*';
442
        }
443
444 18
        $this->rules[] = [$method, $rule];
445
446 18
        if ($rule instanceof RuleItem && 'options' != $method) {
447 18
            $this->rules[] = ['options', $rule->setAutoOptions()];
448
        }
449
450 18
        return $this;
451
    }
452
453
    /**
454
     * 设置分组的路由前缀
455
     * @access public
456
     * @param  string $prefix 路由前缀
457
     * @return $this
458
     */
459
    public function prefix(string $prefix)
460
    {
461
        if ($this->parent && $this->parent->getOption('prefix')) {
462
            $prefix = $this->parent->getOption('prefix') . $prefix;
463
        }
464
465
        return $this->setOption('prefix', $prefix);
466
    }
467
468
    /**
469
     * 合并分组的路由规则正则
470
     * @access public
471
     * @param  bool $merge 是否合并
472
     * @return $this
473
     */
474 6
    public function mergeRuleRegex(bool $merge = true)
475
    {
476 6
        return $this->setOption('merge_rule_regex', $merge);
477
    }
478
479
    /**
480
     * 获取完整分组Name
481
     * @access public
482
     * @return string
483
     */
484 18
    public function getFullName(): string
485
    {
486 18
        return $this->fullName ?: '';
487
    }
488
489
    /**
490
     * 获取分组的路由规则
491
     * @access public
492
     * @param  string $method 请求类型
493
     * @return array
494
     */
495 21
    public function getRules(string $method = ''): array
496
    {
497 21
        if ('' === $method) {
498
            return $this->rules;
499
        }
500
501
        return array_filter($this->rules, function ($item) use ($method) {
502 18
            return $method == $item[0] || $item[0] == '*';
503 21
        });
504
    }
505
506
    /**
507
     * 清空分组下的路由规则
508
     * @access public
509
     * @return void
510
     */
511
    public function clear(): void
512
    {
513
        $this->rules = [];
514
    }
515
}
516