Passed
Push — 5.1 ( 871d47...d4cd58 )
by liu
08:23
created

Url::getRuleUrl()   C

Complexity

Conditions 12
Paths 12

Size

Total Lines 41
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 12
eloc 25
c 3
b 0
f 0
nc 12
nop 3
dl 0
loc 41
rs 6.9666

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
// +----------------------------------------------------------------------
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2018 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
12
namespace think;
13
14
class Url
15
{
16
    /**
17
     * 配置参数
18
     * @var array
19
     */
20
    protected $config = [];
21
22
    /**
23
     * ROOT地址
24
     * @var string
25
     */
26
    protected $root;
27
28
    /**
29
     * 绑定检查
30
     * @var bool
31
     */
32
    protected $bindCheck;
33
34
    /**
35
     * 应用对象
36
     * @var App
37
     */
38
    protected $app;
39
40
    public function __construct(App $app, array $config = [])
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
41
    {
42
        $this->app    = $app;
43
        $this->config = $config;
44
45
        if (is_file($app->getRuntimePath() . 'route.php')) {
46
            // 读取路由映射文件
47
            $app['route']->setName(include $app->getRuntimePath() . 'route.php');
48
        }
49
    }
50
51
    /**
52
     * 初始化
53
     * @access public
54
     * @param  array $config
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
55
     * @return void
56
     */
57
    public function init(array $config = [])
58
    {
59
        $this->config = array_merge($this->config, array_change_key_case($config));
60
    }
61
62
    public static function __make(App $app, Config $config)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
63
    {
64
        return new static($app, $config->pull('app'));
65
    }
66
67
    /**
68
     * URL生成 支持路由反射
69
     * @access public
70
     * @param  string            $url 路由地址
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
71
     * @param  string|array      $vars 参数(支持数组和字符串)a=val&b=val2... ['a'=>'val1', 'b'=>'val2']
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 1 found
Loading history...
72
     * @param  string|bool       $suffix 伪静态后缀,默认为true表示获取配置值
73
     * @param  boolean|string    $domain 是否显示域名 或者直接传入域名
74
     * @return string
75
     */
76
    public function build($url = '', $vars = '', $suffix = true, $domain = false)
77
    {
78
        // 解析URL
79
        if (0 === strpos($url, '[') && $pos = strpos($url, ']')) {
80
            // [name] 表示使用路由命名标识生成URL
81
            $name = substr($url, 1, $pos - 1);
82
            $url  = 'name' . substr($url, $pos + 1);
83
        }
84
85
        if (false === strpos($url, '://') && 0 !== strpos($url, '/')) {
86
            $info = parse_url($url);
87
            $url  = !empty($info['path']) ? $info['path'] : '';
88
89
            if (isset($info['fragment'])) {
90
                // 解析锚点
91
                $anchor = $info['fragment'];
92
93
                if (false !== strpos($anchor, '?')) {
94
                    // 解析参数
95
                    list($anchor, $info['query']) = explode('?', $anchor, 2);
96
                }
97
98
                if (false !== strpos($anchor, '@')) {
99
                    // 解析域名
100
                    list($anchor, $domain) = explode('@', $anchor, 2);
101
                }
102
            } elseif (strpos($url, '@') && false === strpos($url, '\\')) {
103
                // 解析域名
104
                list($url, $domain) = explode('@', $url, 2);
105
            }
106
        }
107
108
        // 解析参数
109
        if (is_string($vars)) {
110
            // aaa=1&bbb=2 转换成数组
111
            parse_str($vars, $vars);
0 ignored issues
show
Bug introduced by
$vars of type string is incompatible with the type array|null expected by parameter $arr of parse_str(). ( Ignorable by Annotation )

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

111
            parse_str($vars, /** @scrutinizer ignore-type */ $vars);
Loading history...
112
        }
113
114
        if ($url) {
115
            $checkName   = isset($name) ? $name : $url . (isset($info['query']) ? '?' . $info['query'] : '');
116
            $checkDomain = $domain && is_string($domain) ? $domain : null;
117
118
            $rule = $this->app['route']->getName($checkName, $checkDomain);
119
120
            if (is_null($rule) && isset($info['query'])) {
121
                $rule = $this->app['route']->getName($url);
122
                // 解析地址里面参数 合并到vars
123
                parse_str($info['query'], $params);
124
                $vars = array_merge($params, $vars);
125
                unset($info['query']);
126
            }
127
        }
128
129
        if (!empty($rule) && $match = $this->getRuleUrl($rule, $vars, $domain)) {
130
            // 匹配路由命名标识
131
            $url = $match[0];
132
133
            if ($domain) {
134
                $domain = $match[1];
135
            }
136
137
            if (!is_null($match[2])) {
138
                $suffix = $match[2];
139
            }
140
        } elseif (!empty($rule) && isset($name)) {
141
            throw new \InvalidArgumentException('route name not exists:' . $name);
142
        } else {
143
            // 检查别名路由
144
            $alias      = $this->app['route']->getAlias();
145
            $matchAlias = false;
146
147
            if ($alias) {
148
                // 别名路由解析
149
                foreach ($alias as $key => $item) {
150
                    $val = $item->getRoute();
151
152
                    if (0 === strpos($url, $val)) {
153
                        $url        = $key . substr($url, strlen($val));
154
                        $matchAlias = true;
155
                        break;
156
                    }
157
                }
158
            }
159
160
            if (!$matchAlias) {
161
                // 路由标识不存在 直接解析
162
                $url = $this->parseUrl($url);
163
            }
164
165
            // 检测URL绑定
166
            if (!$this->bindCheck) {
167
                $bind = $this->app['route']->getBind($domain && is_string($domain) ? $domain : null);
168
169
                if ($bind && 0 === strpos($url, $bind)) {
170
                    $url = substr($url, strlen($bind) + 1);
171
                } else {
172
                    $binds = $this->app['route']->getBind(true);
173
174
                    foreach ($binds as $key => $val) {
175
                        if (is_string($val) && 0 === strpos($url, $val) && substr_count($val, '/') > 1) {
176
                            $url    = substr($url, strlen($val) + 1);
177
                            $domain = $key;
178
                            break;
179
                        }
180
                    }
181
                }
182
            }
183
184
            if (isset($info['query'])) {
185
                // 解析地址里面参数 合并到vars
186
                parse_str($info['query'], $params);
187
                $vars = array_merge($params, $vars);
188
            }
189
        }
190
191
        // 还原URL分隔符
192
        $depr = $this->config['pathinfo_depr'];
193
        $url  = str_replace('/', $depr, $url);
194
195
        // URL后缀
196
        if ('/' == substr($url, -1) || '' == $url) {
197
            $suffix = '';
198
        } else {
199
            $suffix = $this->parseSuffix($suffix);
200
        }
201
202
        // 锚点
203
        $anchor = !empty($anchor) ? '#' . $anchor : '';
204
205
        // 参数组装
206
        if (!empty($vars)) {
207
            // 添加参数
208
            if ($this->config['url_common_param']) {
209
                $vars = http_build_query($vars);
210
                $url .= $suffix . '?' . $vars . $anchor;
211
            } else {
212
                $paramType = $this->config['url_param_type'];
213
214
                foreach ($vars as $var => $val) {
215
                    if ('' !== trim($val)) {
216
                        if ($paramType) {
217
                            $url .= $depr . urlencode($val);
218
                        } else {
219
                            $url .= $depr . $var . $depr . urlencode($val);
220
                        }
221
                    }
222
                }
223
224
                $url .= $suffix . $anchor;
225
            }
226
        } else {
227
            $url .= $suffix . $anchor;
228
        }
229
230
        // 检测域名
231
        $domain = $this->parseDomain($url, $domain);
232
233
        // URL组装
234
        $url = $domain . rtrim($this->root ?: $this->app['request']->root(), '/') . '/' . ltrim($url, '/');
235
236
        $this->bindCheck = false;
237
238
        return $url;
239
    }
240
241
    // 直接解析URL地址
242
    protected function parseUrl($url)
0 ignored issues
show
Coding Style introduced by
You must use "/**" style comments for a function comment
Loading history...
243
    {
244
        $request = $this->app['request'];
245
246
        if (0 === strpos($url, '/')) {
247
            // 直接作为路由地址解析
248
            $url = substr($url, 1);
249
        } elseif (false !== strpos($url, '\\')) {
250
            // 解析到类
251
            $url = ltrim(str_replace('\\', '/', $url), '/');
252
        } elseif (0 === strpos($url, '@')) {
253
            // 解析到控制器
254
            $url = substr($url, 1);
255
        } else {
256
            // 解析到 模块/控制器/操作
257
            $module     = $request->module();
258
            $module     = $module ? $module . '/' : '';
259
            $controller = $request->controller();
260
261
            if ('' == $url) {
262
                $action = $request->action();
263
            } else {
264
                $path       = explode('/', $url);
265
                $action     = array_pop($path);
266
                $controller = empty($path) ? $controller : array_pop($path);
267
                $module     = empty($path) ? $module : array_pop($path) . '/';
268
            }
269
270
            if ($this->config['url_convert']) {
271
                $action     = strtolower($action);
272
                $controller = Loader::parseName($controller);
273
            }
274
275
            $url = $module . $controller . '/' . $action;
276
        }
277
278
        return $url;
279
    }
280
281
    // 检测域名
282
    protected function parseDomain(&$url, $domain)
0 ignored issues
show
Coding Style introduced by
You must use "/**" style comments for a function comment
Loading history...
283
    {
284
        if (!$domain) {
285
            return '';
286
        }
287
288
        $rootDomain = $this->app['request']->rootDomain();
289
        if (true === $domain) {
290
            // 自动判断域名
291
            $domain = $this->config['app_host'] ?: $this->app['request']->host();
292
293
            $domains = $this->app['route']->getDomains();
294
295
            if ($domains) {
296
                $route_domain = array_keys($domains);
297
                foreach ($route_domain as $domain_prefix) {
298
                    if (0 === strpos($domain_prefix, '*.') && strpos($domain, ltrim($domain_prefix, '*.')) !== false) {
299
                        foreach ($domains as $key => $rule) {
300
                            $rule = is_array($rule) ? $rule[0] : $rule;
301
                            if (is_string($rule) && false === strpos($key, '*') && 0 === strpos($url, $rule)) {
302
                                $url    = ltrim($url, $rule);
303
                                $domain = $key;
304
305
                                // 生成对应子域名
306
                                if (!empty($rootDomain)) {
307
                                    $domain .= $rootDomain;
308
                                }
309
                                break;
310
                            } elseif (false !== strpos($key, '*')) {
311
                                if (!empty($rootDomain)) {
312
                                    $domain .= $rootDomain;
313
                                }
314
315
                                break;
316
                            }
317
                        }
318
                    }
319
                }
320
            }
321
        } elseif (0 !== strpos($domain, $rootDomain) && false === strpos($domain, '.')) {
322
            $domain .= '.' . $rootDomain;
323
        }
324
325
        if (false !== strpos($domain, '://')) {
326
            $scheme = '';
327
        } else {
328
            $scheme = $this->app['request']->isSsl() || $this->config['is_https'] ? 'https://' : 'http://';
329
330
        }
331
332
        return $scheme . $domain;
333
    }
334
335
    // 解析URL后缀
336
    protected function parseSuffix($suffix)
0 ignored issues
show
Coding Style introduced by
You must use "/**" style comments for a function comment
Loading history...
337
    {
338
        if ($suffix) {
339
            $suffix = true === $suffix ? $this->config['url_html_suffix'] : $suffix;
340
341
            if ($pos = strpos($suffix, '|')) {
342
                $suffix = substr($suffix, 0, $pos);
343
            }
344
        }
345
346
        return (empty($suffix) || 0 === strpos($suffix, '.')) ? $suffix : '.' . $suffix;
347
    }
348
349
    // 匹配路由地址
350
    public function getRuleUrl($rule, &$vars = [], $allowDomain = '')
0 ignored issues
show
Coding Style introduced by
You must use "/**" style comments for a function comment
Loading history...
351
    {
352
        $port = $this->app['request']->port();
353
        foreach ($rule as $item) {
354
            list($url, $pattern, $domain, $suffix, $method) = $item;
355
356
            if (is_string($allowDomain) && $domain != $allowDomain) {
357
                continue;
358
            }
359
360
            if ($port && !in_array($port, [80, 443])) {
361
                $domain .= ':' . $port;
362
            }
363
364
            if (empty($pattern)) {
365
                return [rtrim($url, '?/-'), $domain, $suffix];
366
            }
367
368
            $type = $this->config['url_common_param'];
369
370
            foreach ($pattern as $key => $val) {
371
                if (isset($vars[$key])) {
372
                    $url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key, '<' . $key . '>'], $type ? $vars[$key] : urlencode($vars[$key]), $url);
373
                    unset($vars[$key]);
374
                    $url    = str_replace(['/?', '-?'], ['/', '-'], $url);
375
                    $result = [rtrim($url, '?/-'), $domain, $suffix];
376
                } elseif (2 == $val) {
377
                    $url    = str_replace(['/[:' . $key . ']', '[:' . $key . ']', '<' . $key . '?>'], '', $url);
378
                    $url    = str_replace(['/?', '-?'], ['/', '-'], $url);
379
                    $result = [rtrim($url, '?/-'), $domain, $suffix];
380
                } else {
381
                    break;
382
                }
383
            }
384
385
            if (isset($result)) {
386
                return $result;
387
            }
388
        }
389
390
        return false;
391
    }
392
393
    // 指定当前生成URL地址的root
394
    public function root($root)
0 ignored issues
show
Coding Style introduced by
You must use "/**" style comments for a function comment
Loading history...
395
    {
396
        $this->root = $root;
397
        $this->app['request']->setRoot($root);
398
    }
399
400
    public function __debugInfo()
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
401
    {
402
        $data = get_object_vars($this);
403
        unset($data['app']);
404
405
        return $data;
406
    }
407
}
408