Passed
Push — 17902-fix-url-normalizer ( 3cc7cc )
by Alexander
10:36
created

UrlNormalizer   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 131
Duplicated Lines 0 %

Test Coverage

Coverage 96.55%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 32
c 1
b 0
f 0
dl 0
loc 131
ccs 28
cts 29
cp 0.9655
rs 10
wmc 16

4 Methods

Rating   Name   Duplication   Size   Complexity  
A normalizeRoute() 0 13 6
A normalizeTrailingSlash() 0 9 5
A normalizePathInfo() 0 21 4
A collapseSlashes() 0 3 1
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\BaseObject;
12
use yii\base\InvalidConfigException;
13
14
/**
15
 * UrlNormalizer normalizes URLs for [[UrlManager]] and [[UrlRule]].
16
 *
17
 * @author Robert Korulczyk <[email protected]>
18
 * @author Cronfy <[email protected]>
19
 * @since 2.0.10
20
 */
21
class UrlNormalizer extends BaseObject
22
{
23
    /**
24
     * Represents permanent redirection during route normalization.
25
     * @see https://en.wikipedia.org/wiki/HTTP_301
26
     */
27
    const ACTION_REDIRECT_PERMANENT = 301;
28
    /**
29
     * Represents temporary redirection during route normalization.
30
     * @see https://en.wikipedia.org/wiki/HTTP_302
31
     */
32
    const ACTION_REDIRECT_TEMPORARY = 302;
33
    /**
34
     * Represents showing 404 error page during route normalization.
35
     * @see https://en.wikipedia.org/wiki/HTTP_404
36
     */
37
    const ACTION_NOT_FOUND = 404;
38
39
    /**
40
     * @var bool whether slashes should be collapsed, for example `site///index` will be
41
     * converted into `site/index`
42
     */
43
    public $collapseSlashes = true;
44
    /**
45
     * @var bool whether trailing slash should be normalized according to the suffix settings
46
     * of the rule
47
     */
48
    public $normalizeTrailingSlash = true;
49
    /**
50
     * @var int|callable|null action to perform during route normalization.
51
     * Available options are:
52
     * - `null` - no special action will be performed
53
     * - `301` - the request should be redirected to the normalized URL using
54
     *   permanent redirection
55
     * - `302` - the request should be redirected to the normalized URL using
56
     *   temporary redirection
57
     * - `404` - [[NotFoundHttpException]] will be thrown
58
     * - `callable` - custom user callback, for example:
59
     *
60
     *   ```php
61
     *   function ($route, $normalizer) {
62
     *       // use custom action for redirections
63
     *       $route[1]['oldRoute'] = $route[0];
64
     *       $route[0] = 'site/redirect';
65
     *       return $route;
66
     *   }
67
     *   ```
68
     */
69
    public $action = self::ACTION_REDIRECT_PERMANENT;
70
71
72
    /**
73
     * Performs normalization action for the specified $route.
74
     * @param array $route route for normalization
75
     * @return array normalized route
76
     * @throws InvalidConfigException if invalid normalization action is used.
77
     * @throws UrlNormalizerRedirectException if normalization requires redirection.
78
     * @throws NotFoundHttpException if normalization suggests action matching route does not exist.
79
     */
80 5
    public function normalizeRoute($route)
81
    {
82 5
        if ($this->action === null) {
83 4
            return $route;
84 3
        } elseif ($this->action === static::ACTION_REDIRECT_PERMANENT || $this->action === static::ACTION_REDIRECT_TEMPORARY) {
85 3
            throw new UrlNormalizerRedirectException([$route[0]] + $route[1], $this->action);
86 2
        } elseif ($this->action === static::ACTION_NOT_FOUND) {
87 2
            throw new NotFoundHttpException(Yii::t('yii', 'Page not found.'));
88 2
        } elseif (is_callable($this->action)) {
89 2
            return call_user_func($this->action, $route, $this);
0 ignored issues
show
Bug introduced by
It seems like $this->action can also be of type integer; however, parameter $function of call_user_func() does only seem to accept callable, 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

89
            return call_user_func(/** @scrutinizer ignore-type */ $this->action, $route, $this);
Loading history...
90
        }
91
92
        throw new InvalidConfigException('Invalid normalizer action.');
93
    }
94
95
    /**
96
     * Normalizes specified pathInfo.
97
     * @param string $pathInfo pathInfo for normalization
98
     * @param string $suffix current rule suffix
99
     * @param bool $normalized if specified, this variable will be set to `true` if $pathInfo
100
     * was changed during normalization
101
     * @return string normalized pathInfo
102
     */
103 5
    public function normalizePathInfo($pathInfo, $suffix, &$normalized = false)
104
    {
105 5
        if (empty($pathInfo)) {
106 2
            return $pathInfo;
107
        }
108
109 5
        $sourcePathInfo = $pathInfo;
110 5
        if ($this->collapseSlashes) {
111 5
            $pathInfo = $this->collapseSlashes($pathInfo);
112
        }
113
114 5
        if ($this->normalizeTrailingSlash === true) {
115 5
            $pathInfo = $this->normalizeTrailingSlash($pathInfo, $suffix);
116
        }
117
118
        // Ensure there is no incorrect characters in pathInfo such as newlines
119 5
        $pathInfo = preg_replace('/\s+/', ' ', $pathInfo);
120
121 5
        $normalized = $sourcePathInfo !== $pathInfo;
122
123 5
        return $pathInfo;
124
    }
125
126
    /**
127
     * Collapse consecutive slashes in $pathInfo, for example converts `site///index` into `site/index`.
128
     * @param string $pathInfo raw path info.
129
     * @return string normalized path info.
130
     */
131 5
    protected function collapseSlashes($pathInfo)
132
    {
133 5
        return ltrim(preg_replace('#/{2,}#', '/', $pathInfo), '/');
134
    }
135
136
    /**
137
     * Adds or removes trailing slashes from $pathInfo depending on whether the $suffix has a
138
     * trailing slash or not.
139
     * @param string $pathInfo raw path info.
140
     * @param string $suffix
141
     * @return string normalized path info.
142
     */
143 5
    protected function normalizeTrailingSlash($pathInfo, $suffix)
144
    {
145 5
        if (substr($suffix, -1) === '/' && substr($pathInfo, -1) !== '/') {
146 2
            $pathInfo .= '/';
147 5
        } elseif (substr($suffix, -1) !== '/' && substr($pathInfo, -1) === '/') {
148 3
            $pathInfo = rtrim($pathInfo, '/');
149
        }
150
151 5
        return $pathInfo;
152
    }
153
}
154