Completed
Push — 6.0 ( 8ae504...076435 )
by liu
02:15
created

RuleGroup::check()   C

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
        $origin = $this->router->getGroup();
250 33
        $this->router->setGroup($this);
251
252 33
        if ($rule instanceof \Closure) {
253 9
            Container::getInstance()->invokeFunction($rule);
254 33
        } elseif (is_string($rule) && $rule) {
255
            $this->router->bind($rule, $this->domain);
256
        }
257
258 33
        $this->router->setGroup($origin);
259 33
    }
260
261
    /**
262
     * 检测分组路由
263
     * @access public
264
     * @param  Request $request       请求对象
265
     * @param  array   $rules         路由规则
266
     * @param  string  $url           访问地址
267
     * @param  bool    $completeMatch 路由是否完全匹配
268
     * @return Dispatch|false
269
     */
270
    protected function checkMergeRuleRegex(Request $request, array &$rules, string $url, bool $completeMatch)
271
    {
272
        $depr  = $this->router->config('pathinfo_depr');
273
        $url   = $depr . str_replace('|', $depr, $url);
274
        $regex = [];
275
        $items = [];
276
277
        foreach ($rules as $key => $val) {
278
            $item = $val[1];
279
            if ($item instanceof RuleItem) {
280
                $rule = $depr . str_replace('/', $depr, $item->getRule());
281
                if ($depr == $rule && $depr != $url) {
282
                    unset($rules[$key]);
283
                    continue;
284
                }
285
286
                $complete = $item->getOption('complete_match', $completeMatch);
287
288
                if (false === strpos($rule, '<')) {
289
                    if (0 === strcasecmp($rule, $url) || (!$complete && 0 === strncasecmp($rule, $url, strlen($rule)))) {
290
                        return $item->checkRule($request, $url, []);
291
                    }
292
293
                    unset($rules[$key]);
294
                    continue;
295
                }
296
297
                $slash = preg_quote('/-' . $depr, '/');
298
299
                if ($matchRule = preg_split('/[' . $slash . ']<\w+\??>/', $rule, 2)) {
300
                    if ($matchRule[0] && 0 !== strncasecmp($rule, $url, strlen($matchRule[0]))) {
301
                        unset($rules[$key]);
302
                        continue;
303
                    }
304
                }
305
306
                if (preg_match_all('/[' . $slash . ']?<?\w+\??>?/', $rule, $matches)) {
307
                    unset($rules[$key]);
308
                    $pattern = array_merge($this->getPattern(), $item->getPattern());
309
                    $option  = array_merge($this->getOption(), $item->getOption());
310
311
                    $regex[$key] = $this->buildRuleRegex($rule, $matches[0], $pattern, $option, $complete, '_THINK_' . $key);
312
                    $items[$key] = $item;
313
                }
314
            }
315
        }
316
317
        if (empty($regex)) {
318
            return false;
319
        }
320
321
        try {
322
            $result = preg_match('~^(?:' . implode('|', $regex) . ')~u', $url, $match);
323
        } catch (\Exception $e) {
324
            throw new Exception('route pattern error');
325
        }
326
327
        if ($result) {
328
            $var = [];
329
            foreach ($match as $key => $val) {
330
                if (is_string($key) && '' !== $val) {
331
                    [$name, $pos] = explode('_THINK_', $key);
332
333
                    $var[$name] = $val;
334
                }
335
            }
336
337
            if (!isset($pos)) {
338
                foreach ($regex as $key => $item) {
339
                    if (0 === strpos(str_replace(['\/', '\-', '\\' . $depr], ['/', '-', $depr], $item), $match[0])) {
340
                        $pos = $key;
341
                        break;
342
                    }
343
                }
344
            }
345
346
            $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...
347
            $array = $this->router->getRule($rule);
348
349
            foreach ($array as $item) {
350
                if (in_array($item->getMethod(), ['*', strtolower($request->method())])) {
351
                    $result = $item->checkRule($request, $url, $var);
352
353
                    if (false !== $result) {
354
                        return $result;
355
                    }
356
                }
357
            }
358
        }
359
360
        return false;
361
    }
362
363
    /**
364
     * 获取分组的MISS路由
365
     * @access public
366
     * @return RuleItem|null
367
     */
368
    public function getMissRule():  ? RuleItem
369
    {
370
        return $this->miss;
371
    }
372
373
    /**
374
     * 注册MISS路由
375
     * @access public
376
     * @param  string|Closure $route  路由地址
377
     * @param  string         $method 请求类型
378
     * @return RuleItem
379
     */
380
    public function miss($route, string $method = '*') : RuleItem
381
    {
382
        // 创建路由规则实例
383
        $ruleItem = new RuleItem($this->router, $this, null, '', $route, strtolower($method));
384
385
        $ruleItem->setMiss();
386
        $this->miss = $ruleItem;
387
388
        return $ruleItem;
389
    }
390
391
    /**
392
     * 添加分组下的路由规则
393
     * @access public
394
     * @param  string $rule   路由规则
395
     * @param  mixed  $route  路由地址
396
     * @param  string $method 请求类型
397
     * @return RuleItem
398
     */
399 30
    public function addRule(string $rule, $route = null, string $method = '*'): RuleItem
400
    {
401
        // 读取路由标识
402 30
        if (is_string($route)) {
403 12
            $name = $route;
404
        } else {
405 21
            $name = null;
406
        }
407
408 30
        $method = strtolower($method);
409
410 30
        if ('' === $rule || '/' === $rule) {
411 6
            $rule .= '$';
412
        }
413
414
        // 创建路由规则实例
415 30
        $ruleItem = new RuleItem($this->router, $this, $name, $rule, $route, $method);
416
417 30
        $this->addRuleItem($ruleItem, $method);
418
419 30
        return $ruleItem;
420
    }
421
422
    /**
423
     * 注册分组下的路由规则
424
     * @access public
425
     * @param  Rule   $rule   路由规则
426
     * @param  string $method 请求类型
427
     * @return $this
428
     */
429 30
    public function addRuleItem(Rule $rule, string $method = '*')
430
    {
431 30
        if (strpos($method, '|')) {
432
            $rule->method($method);
433
            $method = '*';
434
        }
435
436 30
        $this->rules[] = [$method, $rule];
437
438 30
        if ($rule instanceof RuleItem && 'options' != $method) {
439 30
            $this->rules[] = ['options', $rule->setAutoOptions()];
440
        }
441
442 30
        return $this;
443
    }
444
445
    /**
446
     * 设置分组的路由前缀
447
     * @access public
448
     * @param  string $prefix 路由前缀
449
     * @return $this
450
     */
451
    public function prefix(string $prefix)
452
    {
453
        if ($this->parent && $this->parent->getOption('prefix')) {
454
            $prefix = $this->parent->getOption('prefix') . $prefix;
455
        }
456
457
        return $this->setOption('prefix', $prefix);
458
    }
459
460
    /**
461
     * 合并分组的路由规则正则
462
     * @access public
463
     * @param  bool $merge 是否合并
464
     * @return $this
465
     */
466 9
    public function mergeRuleRegex(bool $merge = true)
467
    {
468 9
        return $this->setOption('merge_rule_regex', $merge);
469
    }
470
471
    /**
472
     * 设置分组的Dispatch调度
473
     * @access public
474
     * @param  string $dispatch 调度类
475
     * @return $this
476
     */
477
    public function dispatcher(string $dispatch)
478
    {
479
        return $this->setOption('dispatcher', $dispatch);
480
    }
481
482
    /**
483
     * 获取完整分组Name
484
     * @access public
485
     * @return string
486
     */
487 30
    public function getFullName(): string
488
    {
489 30
        return $this->fullName ?: '';
490
    }
491
492
    /**
493
     * 获取分组的路由规则
494
     * @access public
495
     * @param  string $method 请求类型
496
     * @return array
497
     */
498 33
    public function getRules(string $method = ''): array
499
    {
500 33
        if ('' === $method) {
501
            return $this->rules;
502
        }
503
504
        return array_filter($this->rules, function ($item) use ($method) {
505 30
            return $method == $item[0] || '*' == $item[0];
506 33
        });
507
    }
508
509
    /**
510
     * 清空分组下的路由规则
511
     * @access public
512
     * @return void
513
     */
514
    public function clear(): void
515
    {
516
        $this->rules = [];
517
    }
518
}
519