Passed
Push — 8.0 ( d0e1cd...7f09d5 )
by liu
02:13
created

RuleItem::checkMatch()   D

Complexity

Conditions 32
Paths 80

Size

Total Lines 76
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 43.6639

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 32
eloc 41
c 1
b 0
f 0
nc 80
nop 4
dl 0
loc 76
ccs 31
cts 40
cp 0.775
crap 43.6639
rs 4.1666

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~2023 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 think\Exception;
16
use think\facade\Validate;
17
use think\Request;
18
use think\Route;
19
20
/**
21
 * 路由规则类
22
 */
23
class RuleItem extends Rule
24
{
25
    /**
26
     * 是否为MISS规则
27
     * @var bool
28
     */
29
    protected $miss = false;
30
31
    /**
32
     * 是否为额外自动注册的OPTIONS规则
33
     * @var bool
34
     */
35
    protected $autoOption = false;
36
37
    /**
38
     * 架构函数
39
     * @access public
40
     * @param  Route             $router 路由实例
41
     * @param  RuleGroup         $parent 上级对象
42
     * @param  string            $name 路由标识
43
     * @param  string            $rule 路由规则
44
     * @param  string|\Closure   $route 路由地址
45
     * @param  string            $method 请求类型
46
     */
47 27
    public function __construct(Route $router, RuleGroup $parent, ?string $name = null, string $rule = '', $route = null, string $method = '*')
48
    {
49 27
        $this->router = $router;
50 27
        $this->parent = $parent;
51 27
        $this->name   = $name;
52 27
        $this->route  = $route;
53 27
        $this->method = $method;
54
55 27
        $this->setRule($rule);
56 27
        $this->router->setRule($this->rule, $this);
57
    }
58
59
    /**
60
     * 设置当前路由规则为MISS路由
61
     * @access public
62
     * @return $this
63
     */
64 27
    public function setMiss()
65
    {
66 27
        $this->miss = true;
67 27
        return $this;
68
    }
69
70
    /**
71
     * 判断当前路由规则是否为MISS路由
72
     * @access public
73
     * @return bool
74
     */
75
    public function isMiss(): bool
76
    {
77
        return $this->miss;
78
    }
79
80
    /**
81
     * 获取当前路由的URL后缀
82
     * @access public
83
     * @return string|null
84
     */
85 12
    public function getSuffix(): ?string
86
    {
87 12
        if (isset($this->option['ext'])) {
88
            $suffix = $this->option['ext'];
89 12
        } elseif ($this->parent->getOption('ext')) {
90
            $suffix = $this->parent->getOption('ext');
91
        }
92
93 12
        return $suffix ?? null;
94
    }
95
96
    /**
97
     * 路由规则预处理
98
     * @access public
99
     * @param  string      $rule     路由规则
100
     * @return void
101
     */
102 27
    public function setRule(string $rule): void
103
    {
104 27
        if (str_ends_with($rule, '$')) {
105
            // 是否完整匹配
106 3
            $rule = substr($rule, 0, -1);
107
108 3
            $this->option['complete_match'] = true;
109
        }
110
111 27
        $rule = '/' != $rule ? ltrim($rule, '/') : '';
112
113 27
        if ($this->parent && $prefix = $this->parent->getFullName()) {
114
            $rule = $prefix . ($rule ? '/' . ltrim($rule, '/') : '');
115
        }
116
117 27
        if (str_contains($rule, ':')) {
118 3
            $this->rule = preg_replace(['/\[\:(\w+)\]/', '/\:(\w+)/'], ['<\1?>', '<\1>'], $rule);
119
        } else {
120 27
            $this->rule = $rule;
121
        }
122
123
        // 生成路由标识的快捷访问
124 27
        $this->setRuleName();
125
    }
126
127
    /**
128
     * 设置别名
129
     * @access public
130
     * @param  string     $name
131
     * @return $this
132
     */
133
    public function name(string $name)
134
    {
135
        $this->name = $name;
136
        $this->setRuleName(true);
137
138
        return $this;
139
    }
140
141
    /**
142
     * 设置路由标识 用于URL反解生成
143
     * @access protected
144
     * @param  bool $first 是否插入开头
145
     * @return void
146
     */
147 27
    protected function setRuleName(bool $first = false): void
148
    {
149 27
        if ($this->name) {
150 12
            $this->router->setName($this->name, $this, $first);
151
        }
152
    }
153
154
    /**
155
     * 检测路由
156
     * @access public
157
     * @param  Request      $request  请求对象
158
     * @param  string       $url      访问地址
159
     * @param  array        $match    匹配路由变量
160
     * @param  bool         $completeMatch   路由是否完全匹配
161
     * @return Dispatch|false
162
     */
163 27
    public function checkRule(Request $request, string $url, ?array $match = null, bool $completeMatch = false)
164
    {
165
        // 检查参数有效性
166 27
        if (!$this->checkOption($this->option, $request)) {
167
            return false;
168
        }
169
170
        // 合并分组参数
171 27
        $option  = $this->getOption();
172 27
        $pattern = $this->getPattern();
173 27
        $url     = $this->urlSuffixCheck($request, $url, $option);
174
175 27
        if (is_null($match)) {
176 27
            $match = $this->checkMatch($url, $option, $pattern, $completeMatch);
177
        }
178
179 27
        if (false !== $match) {
180 27
            return $this->parseRule($request, $this->rule, $this->route, $url, $option, $match);
181
        }
182
183
        return false;
184
    }
185
186
    /**
187
     * 检测路由(含路由匹配)
188
     * @access public
189
     * @param  Request      $request  请求对象
190
     * @param  string       $url      访问地址
191
     * @param  bool         $completeMatch   路由是否完全匹配
192
     * @return Dispatch|false
193
     */
194 27
    public function check(Request $request, string $url, bool $completeMatch = false)
195
    {
196 27
        return $this->checkRule($request, $url, null, $completeMatch);
197
    }
198
199
    /**
200
     * URL后缀及Slash检查
201
     * @access protected
202
     * @param  Request      $request  请求对象
203
     * @param  string       $url      访问地址
204
     * @param  array        $option   路由参数
205
     * @return string
206
     */
207 27
    protected function urlSuffixCheck(Request $request, string $url, array $option = []): string
208
    {
209
        // 是否区分 / 地址访问
210 27
        if (!empty($option['remove_slash']) && '/' != $this->rule) {
211
            $this->rule = rtrim($this->rule, '/');
212
            $url        = rtrim($url, '|');
213
        }
214
215 27
        if (isset($option['ext'])) {
216
            // 路由ext参数 优先于系统配置的URL伪静态后缀参数
217
            $url = preg_replace('/\.(' . $request->ext() . ')$/i', '', $url);
218
        }
219
220 27
        return $url;
221
    }
222
223
    /**
224
     * 检测URL和规则路由是否匹配
225
     * @access private
226
     * @param  string    $url URL地址
227
     * @param  array     $option    路由参数
228
     * @param  array     $pattern   变量规则
229
     * @param  bool      $completeMatch   是否完全匹配
230
     * @return array|false
231
     */
232 27
    private function checkMatch(string $url, array $option, array $pattern, bool $completeMatch)
233
    {
234 27
        if (isset($option['complete_match'])) {
235 3
            $completeMatch = $option['complete_match'];
236
        }
237
238 27
        $depr = $this->config('pathinfo_depr');
239 27
        if (isset($option['case_sensitive'])) {
240
            $case = $option['case_sensitive'];
241
        } else {
242 27
            $case = $this->config('url_case_sensitive');
243
        }
244
245
        // 检查完整规则定义
246 27
        if (isset($pattern['__url__']) && !preg_match(str_starts_with($pattern['__url__'], '/') ? $pattern['__url__'] : '/^' . $pattern['__url__'] . ($completeMatch ? '$' : '') . '/', str_replace('|', $depr, $url))) {
247
            return false;
248
        }
249
250 27
        $var  = [];
251 27
        $url  = $depr . str_replace('|', $depr, $url);
252 27
        $rule = $depr . str_replace('/', $depr, $this->rule);
253
254 27
        if ($depr == $rule && $depr != $url) {
255
            return false;
256
        }
257
258 27
        if (!str_contains($rule, '<')) {
259 24
            if ($case && (0 === strcmp($rule, $url) || (!$completeMatch && 0 === strncmp($rule . $depr, $url . $depr, strlen($rule . $depr))))) {
260
                return $var;
261 24
            } elseif (!$case && (0 === strcasecmp($rule, $url) || (!$completeMatch && 0 === strncasecmp($rule . $depr, $url . $depr, strlen($rule . $depr))))) {
262 24
                return $var;
263
            }
264
            return false;
265
        }
266
267 3
        $slash = preg_quote('/-' . $depr, '/');
268
269 3
        if ($matchRule = preg_split('/[' . $slash . ']?<\w+\??>/', $rule, 2)) {
270 3
            if ($matchRule[0] && 0 !== strncasecmp($rule, $url, strlen($matchRule[0]))) {
271
                return false;
272
            }
273
        }
274
275 3
        if (preg_match_all('/[' . $slash . ']?<?\w+\??>?/', $rule, $matches)) {
276 3
            $regex = $this->buildRuleRegex($rule, $matches[0], $pattern, $option, $completeMatch);
277
278
            try {
279 3
                if (!preg_match('~^' . $regex . '~u' . ($case ? '' : 'i'), $url, $match)) {
280 3
                    return false;
281
                }
282
            } catch (\Exception $e) {
283
                throw new Exception('route pattern error');
284
            }
285
286 3
            foreach ($match as $key => $val) {
287 3
                if (is_string($key)) {
288 3
                    if (isset($option['var_rule'][$key]) && !Validate::checkRule($val, $option['var_rule'][$key])) {
289
                        // 检查变量
290
                        return false;
291
                    }
292 3
                    $var[$key] = $val;
293
                }
294
            }
295
        }
296
297 3
        if (!empty($option['default'])) {
298
            // 可选路由变量设置默认值
299 3
            foreach ($option['default'] as $name => $default) {
300 3
                if (!isset($var[$name])) {
301 3
                    $var[$name] = $default;
302
                }
303
            }
304
        }
305
306
        // 成功匹配后返回URL中的动态变量数组
307 3
        return $var;
308
    }
309
310
    /**
311
     * 设置路由所属分组(用于注解路由)
312
     * @access public
313
     * @param  string $name 分组名称或者标识
314
     * @return $this
315
     */
316
    public function group(string $name)
317
    {
318
        $group = $this->router->getRuleName()->getGroup($name);
319
320
        if ($group) {
321
            $this->parent = $group;
322
            $this->setRule($this->rule);
323
        }
324
325
        return $this;
326
    }
327
}
328