Passed
Pull Request — 5.1 (#1748)
by guanguans
09:27
created

Url::__debugInfo()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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