GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( c73de3...97c43c )
by Robert
13:22
created

UrlRule::trimSlashes()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
ccs 4
cts 4
cp 1
cc 2
eloc 4
nc 2
nop 1
crap 2
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\web;
9
10
use Yii;
11
use yii\base\Object;
12
use yii\base\InvalidConfigException;
13
14
/**
15
 * UrlRule represents a rule used by [[UrlManager]] for parsing and generating URLs.
16
 *
17
 * To define your own URL parsing and creation logic you can extend from this class
18
 * and add it to [[UrlManager::rules]] like this:
19
 *
20
 * ```php
21
 * 'rules' => [
22
 *     ['class' => 'MyUrlRule', 'pattern' => '...', 'route' => 'site/index', ...],
23
 *     // ...
24
 * ]
25
 * ```
26
 *
27
 * @author Qiang Xue <[email protected]>
28
 * @since 2.0
29
 */
30
class UrlRule extends Object implements UrlRuleInterface
31
{
32
    /**
33
     * Set [[mode]] with this value to mark that this rule is for URL parsing only
34
     */
35
    const PARSING_ONLY = 1;
36
    /**
37
     * Set [[mode]] with this value to mark that this rule is for URL creation only
38
     */
39
    const CREATION_ONLY = 2;
40
41
    /**
42
     * @var string the name of this rule. If not set, it will use [[pattern]] as the name.
43
     */
44
    public $name;
45
    /**
46
     * On the rule initialization, the [[pattern]] matching parameters names will be replaced with [[placeholders]].
47
     * @var string the pattern used to parse and create the path info part of a URL.
48
     * @see host
49
     * @see placeholders
50
     */
51
    public $pattern;
52
    /**
53
     * @var string the pattern used to parse and create the host info part of a URL (e.g. `http://example.com`).
54
     * @see pattern
55
     */
56
    public $host;
57
    /**
58
     * @var string the route to the controller action
59
     */
60
    public $route;
61
    /**
62
     * @var array the default GET parameters (name => value) that this rule provides.
63
     * When this rule is used to parse the incoming request, the values declared in this property
64
     * will be injected into $_GET.
65
     */
66
    public $defaults = [];
67
    /**
68
     * @var string the URL suffix used for this rule.
69
     * For example, ".html" can be used so that the URL looks like pointing to a static HTML page.
70
     * If not set, the value of [[UrlManager::suffix]] will be used.
71
     */
72
    public $suffix;
73
    /**
74
     * @var string|array the HTTP verb (e.g. GET, POST, DELETE) that this rule should match.
75
     * Use array to represent multiple verbs that this rule may match.
76
     * If this property is not set, the rule can match any verb.
77
     * Note that this property is only used when parsing a request. It is ignored for URL creation.
78
     */
79
    public $verb;
80
    /**
81
     * @var int a value indicating if this rule should be used for both request parsing and URL creation,
82
     * parsing only, or creation only.
83
     * If not set or 0, it means the rule is both request parsing and URL creation.
84
     * If it is [[PARSING_ONLY]], the rule is for request parsing only.
85
     * If it is [[CREATION_ONLY]], the rule is for URL creation only.
86
     */
87
    public $mode;
88
    /**
89
     * @var bool a value indicating if parameters should be url encoded.
90
     */
91
    public $encodeParams = true;
92
    /**
93
     * @var UrlNormalizer|array|false|null the configuration for [[UrlNormalizer]] used by this rule.
94
     * If `null`, [[UrlManager::normalizer]] will be used, if `false`, normalization will be skipped
95
     * for this rule.
96
     * @since 2.0.10
97
     */
98
    public $normalizer;
99
100
    /**
101
     * @var array list of placeholders for matching parameters names. Used in [[parseRequest()]], [[createUrl()]].
102
     * On the rule initialization, the [[pattern]] parameters names will be replaced with placeholders.
103
     * This array contains relations between the original parameters names and their placeholders.
104
     * The array keys are the placeholders and the values are the original names.
105
     *
106
     * @see parseRequest()
107
     * @see createUrl()
108
     * @since 2.0.7
109
     */
110
    protected $placeholders = [];
111
112
    /**
113
     * @var string the template for generating a new URL. This is derived from [[pattern]] and is used in generating URL.
114
     */
115
    private $_template;
116
    /**
117
     * @var string the regex for matching the route part. This is used in generating URL.
118
     */
119
    private $_routeRule;
120
    /**
121
     * @var array list of regex for matching parameters. This is used in generating URL.
122
     */
123
    private $_paramRules = [];
124
    /**
125
     * @var array list of parameters used in the route.
126
     */
127
    private $_routeParams = [];
128
129
130
    /**
131
     * @return string
132
     * @since 2.0.11
133
     */
134 12
    public function __toString()
135
    {
136 12
        $str = '';
137 12
        if ($this->verb !== null) {
138 3
            $str .= implode(',', $this->verb) . ' ';
139 3
        }
140 12
        if ($this->host !== null && strrpos($this->name, $this->host) === false) {
141 1
            $str .= $this->host . '/';
142 1
        }
143 12
        $str .= $this->name;
144
145 12
        if ($str === '') {
146 1
            return '/';
147
        }
148 12
        return $str;
149
    }
150
151
    /**
152
     * Initializes this rule.
153
     */
154 87
    public function init()
155
    {
156 87
        if ($this->pattern === null) {
157
            throw new InvalidConfigException('UrlRule::pattern must be set.');
158
        }
159 87
        if ($this->route === null) {
160
            throw new InvalidConfigException('UrlRule::route must be set.');
161
        }
162 87
        if (is_array($this->normalizer)) {
163 1
            $normalizerConfig = array_merge(['class' => UrlNormalizer::className()], $this->normalizer);
164 1
            $this->normalizer = Yii::createObject($normalizerConfig);
165 1
        }
166 87
        if ($this->normalizer !== null && $this->normalizer !== false && !$this->normalizer instanceof UrlNormalizer) {
167
            throw new InvalidConfigException('Invalid config for UrlRule::normalizer.');
168
        }
169 87
        if ($this->verb !== null) {
170 11
            if (is_array($this->verb)) {
171 11
                foreach ($this->verb as $i => $verb) {
172 11
                    $this->verb[$i] = strtoupper($verb);
173 11
                }
174 11
            } else {
175
                $this->verb = [strtoupper($this->verb)];
176
            }
177 11
        }
178 87
        if ($this->name === null) {
179 87
            $this->name = $this->pattern;
180 87
        }
181
182 87
        $this->pattern = $this->trimSlashes($this->pattern);
183 87
        $this->route = trim($this->route, '/');
184
185 87
        if ($this->host !== null) {
186 17
            $this->host = rtrim($this->host, '/');
187 17
            $this->pattern = rtrim($this->host . '/' . $this->pattern, '/');
188 87
        } elseif ($this->pattern === '') {
189 21
            $this->_template = '';
190 21
            $this->pattern = '#^$#u';
191
192 21
            return;
193 75
        } elseif (($pos = strpos($this->pattern, '://')) !== false) {
194 9
            if (($pos2 = strpos($this->pattern, '/', $pos + 3)) !== false) {
195 9
                $this->host = substr($this->pattern, 0, $pos2);
196 9
            } else {
197
                $this->host = $this->pattern;
198
            }
199 75
        } elseif (strpos($this->pattern, '//') === 0) {
200 8
            if (($pos2 = strpos($this->pattern, '/', $pos + 2)) !== false) {
201 8
                $this->host = substr($this->pattern, 0, $pos2);
202 8
            } else {
203 4
                $this->host = $this->pattern;
204
            }
205 8
        } else {
206 67
            $this->pattern = '/' . $this->pattern . '/';
207
        }
208
209 79
        if (strpos($this->route, '<') !== false && preg_match_all('/<([\w._-]+)>/', $this->route, $matches)) {
210 13
            foreach ($matches[1] as $name) {
211 13
                $this->_routeParams[$name] = "<$name>";
212 13
            }
213 13
        }
214
215
        $tr = [
216 79
            '.' => '\\.',
217 79
            '*' => '\\*',
218 79
            '$' => '\\$',
219 79
            '[' => '\\[',
220 79
            ']' => '\\]',
221 79
            '(' => '\\(',
222 79
            ')' => '\\)',
223 79
        ];
224
225 79
        $tr2 = [];
226 79
        if (preg_match_all('/<([\w._-]+):?([^>]+)?>/', $this->pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
227 77
            foreach ($matches as $match) {
228 77
                $name = $match[1][0];
229 77
                $pattern = isset($match[2][0]) ? $match[2][0] : '[^\/]+';
230 77
                $placeholder = 'a' . hash('crc32b', $name); // placeholder must begin with a letter
231 77
                $this->placeholders[$placeholder] = $name;
232 77
                if (array_key_exists($name, $this->defaults)) {
233 5
                    $length = strlen($match[0][0]);
234 5
                    $offset = $match[0][1];
235 5
                    if ($offset > 1 && $this->pattern[$offset - 1] === '/' && (!isset($this->pattern[$offset + $length]) || $this->pattern[$offset + $length] === '/')) {
236 5
                        $tr["/<$name>"] = "(/(?P<$placeholder>$pattern))?";
237 5
                    } else {
238 4
                        $tr["<$name>"] = "(?P<$placeholder>$pattern)?";
239
                    }
240 5
                } else {
241 77
                    $tr["<$name>"] = "(?P<$placeholder>$pattern)";
242
                }
243 77
                if (isset($this->_routeParams[$name])) {
244 13
                    $tr2["<$name>"] = "(?P<$placeholder>$pattern)";
245 13
                } else {
246 77
                    $this->_paramRules[$name] = $pattern === '[^\/]+' ? '' : "#^$pattern$#u";
247
                }
248 77
            }
249 77
        }
250
251 79
        $this->_template = preg_replace('/<([\w._-]+):?([^>]+)?>/', '<$1>', $this->pattern);
252 79
        $this->pattern = '#^' . trim(strtr($this->_template, $tr), '/') . '$#u';
253
254
        // if host starts with relative scheme, then insert pattern to match any
255 79
        if (strpos($this->host, '//') === 0) {
256 8
            $this->pattern = substr_replace($this->pattern, '[\w]+://', 2, 0);
257 8
        }
258
259 79
        if (!empty($this->_routeParams)) {
260 13
            $this->_routeRule = '#^' . strtr($this->route, $tr2) . '$#u';
261 13
        }
262 79
    }
263
264
    /**
265
     * @param UrlManager $manager the URL manager
266
     * @return UrlNormalizer|null
267
     * @since 2.0.10
268
     */
269 15
    protected function getNormalizer($manager)
270
    {
271 15
        if ($this->normalizer === null) {
272 14
            return $manager->normalizer;
273
        } else {
274 1
            return $this->normalizer;
275
        }
276
    }
277
278
    /**
279
     * @param UrlManager $manager the URL manager
280
     * @return bool
281
     * @since 2.0.10
282
     */
283 15
    protected function hasNormalizer($manager)
284
    {
285 15
        return $this->getNormalizer($manager) instanceof UrlNormalizer;
286
    }
287
288
    /**
289
     * Parses the given request and returns the corresponding route and parameters.
290
     * @param UrlManager $manager the URL manager
291
     * @param Request $request the request component
292
     * @return array|bool the parsing result. The route and the parameters are returned as an array.
293
     * If `false`, it means this rule cannot be used to parse this path info.
294
     */
295 15
    public function parseRequest($manager, $request)
296
    {
297 15
        if ($this->mode === self::CREATION_ONLY) {
298 3
            return false;
299
        }
300
301 15
        if (!empty($this->verb) && !in_array($request->getMethod(), $this->verb, true)) {
302 2
            return false;
303
        }
304
305 15
        $suffix = (string)($this->suffix === null ? $manager->suffix : $this->suffix);
306 15
        $pathInfo = $request->getPathInfo();
307 15
        $normalized = false;
308 15
        if ($this->hasNormalizer($manager)) {
309 4
            $pathInfo = $this->getNormalizer($manager)->normalizePathInfo($pathInfo, $suffix, $normalized);
310 4
        }
311 15
        if ($suffix !== '' && $pathInfo !== '') {
312 9
            $n = strlen($suffix);
313 9
            if (substr_compare($pathInfo, $suffix, -$n, $n) === 0) {
314 9
                $pathInfo = substr($pathInfo, 0, -$n);
315 9
                if ($pathInfo === '') {
316
                    // suffix alone is not allowed
317 3
                    return false;
318
                }
319 9
            } else {
320 8
                return false;
321
            }
322 9
        }
323
324 15
        if ($this->host !== null) {
325 3
            $pathInfo = strtolower($request->getHostInfo()) . ($pathInfo === '' ? '' : '/' . $pathInfo);
326 3
        }
327
328 15
        if (!preg_match($this->pattern, $pathInfo, $matches)) {
329 14
            return false;
330
        }
331 15
        $matches = $this->substitutePlaceholderNames($matches);
332
333 15
        foreach ($this->defaults as $name => $value) {
334 3
            if (!isset($matches[$name]) || $matches[$name] === '') {
335 3
                $matches[$name] = $value;
336 3
            }
337 15
        }
338 15
        $params = $this->defaults;
339 15
        $tr = [];
340 15
        foreach ($matches as $name => $value) {
341 15
            if (isset($this->_routeParams[$name])) {
342 3
                $tr[$this->_routeParams[$name]] = $value;
343 3
                unset($params[$name]);
344 15
            } elseif (isset($this->_paramRules[$name])) {
345 14
                $params[$name] = $value;
346 14
            }
347 15
        }
348 15
        if ($this->_routeRule !== null) {
349 3
            $route = strtr($this->route, $tr);
350 3
        } else {
351 15
            $route = $this->route;
352
        }
353
354 15
        Yii::trace("Request parsed with URL rule: {$this->name}", __METHOD__);
355
356 15
        if ($normalized) {
357
            // pathInfo was changed by normalizer - we need also normalize route
358 4
            return $this->getNormalizer($manager)->normalizeRoute([$route, $params]);
359
        } else {
360 13
            return [$route, $params];
361
        }
362
    }
363
364
    /**
365
     * Creates a URL according to the given route and parameters.
366
     * @param UrlManager $manager the URL manager
367
     * @param string $route the route. It should not have slashes at the beginning or the end.
368
     * @param array $params the parameters
369
     * @return string|bool the created URL, or `false` if this rule cannot be used for creating this URL.
370
     */
371 71
    public function createUrl($manager, $route, $params)
372
    {
373 71
        if ($this->mode === self::PARSING_ONLY) {
374 9
            return false;
375
        }
376
377 71
        $tr = [];
378
379
        // match the route part first
380 71
        if ($route !== $this->route) {
381 43
            if ($this->_routeRule !== null && preg_match($this->_routeRule, $route, $matches)) {
382 10
                $matches = $this->substitutePlaceholderNames($matches);
383 10
                foreach ($this->_routeParams as $name => $token) {
384 10
                    if (isset($this->defaults[$name]) && strcmp($this->defaults[$name], $matches[$name]) === 0) {
385 1
                        $tr[$token] = '';
386 1
                    } else {
387 10
                        $tr[$token] = $matches[$name];
388
                    }
389 10
                }
390 10
            } else {
391 43
                return false;
392
            }
393 10
        }
394
395
        // match default params
396
        // if a default param is not in the route pattern, its value must also be matched
397 70
        foreach ($this->defaults as $name => $value) {
398 13
            if (isset($this->_routeParams[$name])) {
399 1
                continue;
400
            }
401 13
            if (!isset($params[$name])) {
402
                // allow omit empty optional params
403
                // @see https://github.com/yiisoft/yii2/issues/10970
404 9
                if (in_array($name, $this->placeholders) && strcmp($value, '') === 0) {
405 1
                    $params[$name] = '';
406 1
                } else {
407 9
                    return false;
408
                }
409 1
            }
410 13
            if (strcmp($params[$name], $value) === 0) { // strcmp will do string conversion automatically
411 13
                unset($params[$name]);
412 13
                if (isset($this->_paramRules[$name])) {
413 1
                    $tr["<$name>"] = '';
414 1
                }
415 13
            } elseif (!isset($this->_paramRules[$name])) {
416 13
                return false;
417
            }
418 70
        }
419
420
        // match params in the pattern
421 70
        foreach ($this->_paramRules as $name => $rule) {
422 61
            if (isset($params[$name]) && !is_array($params[$name]) && ($rule === '' || preg_match($rule, $params[$name]))) {
423 60
                $tr["<$name>"] = $this->encodeParams ? urlencode($params[$name]) : $params[$name];
424 60
                unset($params[$name]);
425 61
            } elseif (!isset($this->defaults[$name]) || isset($params[$name])) {
426 49
                return false;
427
            }
428 70
        }
429
430 70
        $url = $this->trimSlashes(strtr($this->_template, $tr));
431 70
        if ($this->host !== null) {
432 13
            $pos = strpos($url, '/', 8);
433 13
            if ($pos !== false) {
434 13
                $url = substr($url, 0, $pos) . preg_replace('#/+#', '/', substr($url, $pos));
435 13
            }
436 70
        } elseif (strpos($url, '//') !== false) {
437 1
            $url = preg_replace('#/+#', '/', $url);
438 1
        }
439
440 70
        if ($url !== '') {
441 62
            $url .= ($this->suffix === null ? $manager->suffix : $this->suffix);
442 62
        }
443
444 70
        if (!empty($params) && ($query = http_build_query($params)) !== '') {
445 51
            $url .= '?' . $query;
446 51
        }
447
448 70
        return $url;
449
    }
450
451
    /**
452
     * Returns list of regex for matching parameter.
453
     * @return array parameter keys and regexp rules.
454
     *
455
     * @since 2.0.6
456
     */
457
    protected function getParamRules()
458
    {
459
        return $this->_paramRules;
460
    }
461
462
    /**
463
     * Iterates over [[placeholders]] and checks whether each placeholder exists as a key in $matches array.
464
     * When found - replaces this placeholder key with a appropriate name of matching parameter.
465
     * Used in [[parseRequest()]], [[createUrl()]].
466
     *
467
     * @param array $matches result of `preg_match()` call
468
     * @return array input array with replaced placeholder keys
469
     * @see placeholders
470
     * @since 2.0.7
471
     */
472 25
    protected function substitutePlaceholderNames(array $matches)
473
    {
474 25
        foreach ($this->placeholders as $placeholder => $name) {
475 24
            if (isset($matches[$placeholder])) {
476 24
                $matches[$name] = $matches[$placeholder];
477 24
                unset($matches[$placeholder]);
478 24
            }
479 25
        }
480 25
        return $matches;
481
    }
482
483
    /**
484
     * Trim slashes in passed string. If string begins with '//', two slashes are left as is
485
     * in the beginning of a string.
486
     *
487
     * @param string $string
488
     * @return string
489
     */
490 87
    private function trimSlashes($string) {
491 87
        if (strpos($string, '//') === 0) {
492 8
            return '//' . trim($string, '/');
493
        }
494 87
        return trim($string, '/');
495
    }
496
}
497