Passed
Pull Request — 6.0 (#1716)
by
unknown
02:27
created

Url::parseController()   C

Complexity

Conditions 13
Paths 32

Size

Total Lines 43
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 25
nc 32
nop 2
dl 0
loc 43
rs 6.6166
c 0
b 0
f 0

How to fix   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
// +----------------------------------------------------------------------
1 ignored issue
show
Coding Style introduced by
You must use "/**" style comments for a file comment
Loading history...
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\dispatch;
14
15
use think\App;
16
use think\Container;
17
use think\exception\HttpException;
18
use think\Request;
19
use think\route\Rule;
20
21
class Url extends Controller
1 ignored issue
show
Coding Style introduced by
Missing class doc comment
Loading history...
22
{
23
24
    public function __construct(Request $request, Rule $rule, $dispatch, array $param = [], int $code = null)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
25
    {
26
        $this->request  = $request;
27
        $this->rule     = $rule;
28
        $this->app      = Container::pull('app');
29
        // 解析默认的URL规则
30
        $dispatch = $this->parseUrl($dispatch);
31
32
        parent::__construct($request, $rule, $dispatch, $param, $code);
33
    }
34
35
    /**
36
     * 解析URL地址
37
     * @access protected
38
     * @param  string $url URL
39
     * @return array
40
     */
41
    protected function parseUrl(string $url): array
42
    {
43
        $depr = $this->rule->config('pathinfo_depr');
44
        $bind = $this->rule->getRouter()->getDomainBind();
45
46
        if ($bind && preg_match('/^[a-z]/is', $bind)) {
47
            $bind = str_replace('/', $depr, $bind);
48
            // 如果有模块/控制器绑定
49
            $url = $bind . ('.' != substr($bind, -1) ? $depr : '') . ltrim($url, $depr);
50
        }
51
52
        list($path, $var) = $this->rule->parseUrlPath($url);
53
        if (empty($path)) {
54
            return [null, null];
55
        }
56
57
        // 解析控制器
58
        list($controller, $path) = $this->parseController($path, $this->app->config->get('app.auto_multi_controller', false));
0 ignored issues
show
Bug introduced by
It seems like $this->app->config->get(...lti_controller', false) can also be of type array and array; however, parameter $multi of think\route\dispatch\Url::parseController() does only seem to accept boolean, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

58
        list($controller, $path) = $this->parseController($path, /** @scrutinizer ignore-type */ $this->app->config->get('app.auto_multi_controller', false));
Loading history...
59
60
        // 解析操作
61
        $action = !empty($path) ? array_shift($path) : null;
62
63
        // 解析额外参数
64
        if ($path) {
65
            preg_replace_callback('/(\w+)\|([^\|]+)/', function ($match) use (&$var) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
66
                $var[$match[1]] = strip_tags($match[2]);
67
            }, implode('|', $path));
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
68
        }
69
70
        $panDomain = $this->request->panDomain();
71
        if ($panDomain && $key = array_search('*', $var)) {
72
            // 泛域名赋值
73
            $var[$key] = $panDomain;
74
        }
75
76
        // 设置当前请求的参数
77
        $this->request->setRoute($var);
78
79
        // 封装路由
80
        $route = [$controller, $action];
81
82
        if ($this->hasDefinedRoute($route)) {
83
            throw new HttpException(404, 'invalid request:' . str_replace('|', $depr, $url));
84
        }
85
86
        return $route;
87
    }
88
89
    /**
90
     * 解析访问控制器,支持多级控制器
91
     * @access public
92
     * @param  array $paths 地址
93
     * @param  bool $multi 是否多级控制器
94
     * @return array
95
     * @throws HttpException
96
     */
97
    private function parseController(array $paths, bool $multi = false) : array
0 ignored issues
show
Coding Style introduced by
Private method name "Url::parseController" must be prefixed with an underscore
Loading history...
98
    {
99
        $tempPaths   = \array_merge([], $paths);
100
        $controller  = !empty($tempPaths) ? array_shift($tempPaths) : null;
101
        if ($controller && !\preg_match('/^[A-Za-z][\w|\.]*$/', $controller)) {
102
            throw new HttpException(404, 'controller not exists:' . $controller);
103
        }
104
105
        if ($multi) {
106
            // 处理多级控制器
107
            $controllers = [];
108
            \array_push($controllers, $controller);
109
            // 控制器是否存在
110
            $exists = $this->exists($controller);
0 ignored issues
show
Bug introduced by
It seems like $controller can also be of type null; however, parameter $name of think\route\dispatch\Controller::exists() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

110
            $exists = $this->exists(/** @scrutinizer ignore-type */ $controller);
Loading history...
111
            do {
112
                if (!$exists) {
113
                    // 控制器不存在,取下一级路径
114
                    $nextPath = !empty($tempPaths) ? \array_shift($tempPaths) : null;
115
                    if ($nextPath && \preg_match('/^[A-Za-z][\w|\.]*$/', $nextPath)) {
116
                        \array_push($controllers, $nextPath);
117
                        $controller = join('.', $controllers);
118
                    } else {
119
                        // 下一级路径命名不正确, 压回数组, 退出循环
120
                        if (!empty($nextPath)) {
121
                            \array_unshift($tempPaths, $nextPath);
122
                        }
123
                        break;
124
                    }
125
                }
126
                // 控制器是否存在
127
                $exists = $this->exists($controller);
128
            } while (!$exists);
129
130
            // 多级控制器不存在,还原为单级控制器
131
            if (!$exists) {
132
                while (count($controllers) > 1) {
133
                    \array_unshift($tempPaths, \array_pop($controllers));
134
                }
135
                $controller = join('.', $controllers);
136
            }
137
        }
138
139
        return [$controller, $tempPaths];
140
    }
141
142
    /**
143
     * 检查URL是否已经定义过路由
144
     * @access protected
145
     * @param  array $route 路由信息
146
     * @return bool
147
     */
148
    protected function hasDefinedRoute(array $route): bool
149
    {
150
        list($controller, $action) = $route;
151
152
        // 检查地址是否被定义过路由
153
        $name = strtolower(App::parseName($controller, 1) . '/' . $action);
154
155
        $host   = $this->request->host(true);
156
        $method = $this->request->method();
157
158
        if ($this->rule->getRouter()->getName($name, $host, $method)) {
159
            return true;
160
        }
161
162
        return false;
163
    }
164
165
}
166