Passed
Push — 8.0 ( cd7e8a...0aac54 )
by liu
11:57 queued 09:21
created

RuleItem::checkMatch()   D

Complexity

Conditions 32
Paths 80

Size

Total Lines 77
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 282

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 32
eloc 41
c 2
b 0
f 0
nc 80
nop 4
dl 0
loc 77
ccs 15
cts 40
cp 0.375
crap 282
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 9
    public function getSuffix(): ?string
86
    {
87 9
        if (isset($this->option['ext'])) {
88
            $suffix = $this->option['ext'];
89 9
        } elseif ($this->parent->getOption('ext')) {
90
            $suffix = $this->parent->getOption('ext');
91
        }
92
93 9
        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, ':') || str_contains($rule, '{')) {
118
            $this->rule = preg_replace(['/\[\:(\w+)\]/', '/\:(\w+)/', '/\{(\w+)\}/', '/\{(\w+)\?\}/'], ['<\1?>', '<\1>', '<\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 9
            $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 24
    public function checkRule(Request $request, string $url, ?array $match = null, bool $completeMatch = false)
164
    {
165
        // 检查参数有效性
166 24
        if (!$this->checkOption($this->option, $request)) {
167
            return false;
168
        }
169
170
        // 合并分组参数
171 24
        $option  = $this->getOption();
172 24
        $pattern = $this->getPattern();
173 24
        $url     = $this->urlSuffixCheck($request, $url, $option);
174
175 24
        if (is_null($match)) {
176 24
            $match = $this->checkMatch($url, $option, $pattern, $completeMatch);
177
        }
178
179 24
        if (false !== $match) {
180 24
            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 24
    public function check(Request $request, string $url, bool $completeMatch = false)
195
    {
196 24
        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 24
    protected function urlSuffixCheck(Request $request, string $url, array $option = []): string
208
    {
209
        // 是否区分 / 地址访问
210 24
        if (!empty($option['remove_slash']) && '/' != $this->rule) {
211
            $this->rule = rtrim($this->rule, '/');
212
            $url        = rtrim($url, '|');
213
        }
214
215 24
        if (isset($option['ext'])) {
216
            // 路由ext参数 优先于系统配置的URL伪静态后缀参数
217
            $url = preg_replace('/\.(' . $request->ext() . ')$/i', '', $url);
218
        }
219
220 24
        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 24
    private function checkMatch(string $url, array $option, array $pattern, bool $completeMatch)
233
    {
234 24
        if (isset($option['complete_match'])) {
235 3
            $completeMatch = $option['complete_match'];
236
        }
237
238 24
        $depr = $this->config('pathinfo_depr');
239 24
        if (isset($option['case_sensitive'])) {
240
            $case = $option['case_sensitive'];
241
        } else {
242 24
            $case = $this->config('url_case_sensitive');
243
        }
244
245
        // 检查完整规则定义
246 24
        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 24
        $var  = [];
251 24
        $url  = $depr . str_replace('|', $depr, $url);
252 24
        $rule = $depr . str_replace('/', $depr, $this->rule);
253
254 24
        if ($depr == $rule && $depr != $url) {
255
            return false;
256
        }
257
258 24
        if (!str_contains($rule, '<')) {
259
            // 静态路由
260 24
            if ($case && (0 === strcmp($rule, $url) || (!$completeMatch && 0 === strncmp($rule . $depr, $url . $depr, strlen($rule . $depr))))) {
261
                return $var;
262 24
            } elseif (!$case && (0 === strcasecmp($rule, $url) || (!$completeMatch && 0 === strncasecmp($rule . $depr, $url . $depr, strlen($rule . $depr))))) {
263 24
                return $var;
264
            }
265
            return false;
266
        }
267
268
        $slash = preg_quote('/-' . $depr, '/');
269
270
        if ($matchRule = preg_split('/[' . $slash . ']?<\w+\??>/', $rule, 2)) {
271
            if ($matchRule[0] && 0 !== strncasecmp($rule, $url, strlen($matchRule[0]))) {
272
                return false;
273
            }
274
        }
275
276
        if (preg_match_all('/[' . $slash . ']?<?\w+\??>?/', $rule, $matches)) {
277
            $regex = $this->buildRuleRegex($rule, $matches[0], $pattern, $option, $completeMatch);
278
279
            try {
280
                if (!preg_match('~^' . $regex . '~u' . ($case ? '' : 'i'), $url, $match)) {
281
                    return false;
282
                }
283
            } catch (\Exception $e) {
284
                throw new Exception('route pattern error');
285
            }
286
287
            foreach ($match as $key => $val) {
288
                if (is_string($key)) {
289
                    if (isset($option['var_rule'][$key]) && !Validate::checkRule($val, $option['var_rule'][$key])) {
290
                        // 检查变量
291
                        return false;
292
                    }
293
                    $var[$key] = $val;
294
                }
295
            }
296
        }
297
298
        if (!empty($option['default'])) {
299
            // 可选路由变量设置默认值
300
            foreach ($option['default'] as $name => $default) {
301
                if (!isset($var[$name])) {
302
                    $var[$name] = $default;
303
                }
304
            }
305
        }
306
307
        // 成功匹配后返回URL中的动态变量数组
308
        return $var;
309
    }
310
311
    /**
312
     * 设置路由所属分组(用于注解路由)
313
     * @access public
314
     * @param  string $name 分组名称或者标识
315
     * @return $this
316
     */
317
    public function group(string $name)
318
    {
319
        $group = $this->router->getRuleName()->getGroup($name);
320
321
        if ($group) {
322
            $this->parent = $group;
323
            $this->setRule($this->rule);
324
        }
325
326
        return $this;
327
    }
328
}
329