Completed
Push — master ( ac4cc7...3c4d8e )
by Tobias
03:01
created

Uri::__construct()   B

Complexity

Conditions 10
Paths 130

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 10

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 14
cts 14
cp 1
rs 7.4166
c 0
b 0
f 0
cc 10
nc 130
nop 1
crap 10

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
declare(strict_types=1);
4
5
namespace Nyholm\Psr7;
6
7
use Psr\Http\Message\UriInterface;
8
9
/**
10
 * PSR-7 URI implementation.
11
 *
12
 * @author Michael Dowling
13
 * @author Tobias Schultze
14
 * @author Matthew Weier O'Phinney
15
 * @author Tobias Nyholm <[email protected]>
16
 * @author Martijn van der Ven <[email protected]>
17
 */
18
final class Uri implements UriInterface
19
{
20
    private const SCHEMES = ['http' => 80, 'https' => 443];
21
22
    private const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~';
23
24
    private const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
25
26
    /** @var string Uri scheme. */
27
    private $scheme = '';
28
29
    /** @var string Uri user info. */
30
    private $userInfo = '';
31
32
    /** @var string Uri host. */
33
    private $host = '';
34
35
    /** @var int|null Uri port. */
36
    private $port;
37
38
    /** @var string Uri path. */
39
    private $path = '';
40
41
    /** @var string Uri query string. */
42
    private $query = '';
43
44
    /** @var string Uri fragment. */
45
    private $fragment = '';
46
47 150
    public function __construct(string $uri = '')
48
    {
49 150
        if ('' !== $uri) {
50 130
            if (false === $parts = \parse_url($uri)) {
51 4
                throw new \InvalidArgumentException("Unable to parse URI: $uri");
52
            }
53
54
            // Apply parse_url parts to a URI.
55 126
            $this->scheme = isset($parts['scheme']) ? \strtolower($parts['scheme']) : '';
56 126
            $this->userInfo = $parts['user'] ?? '';
57 126
            $this->host = isset($parts['host']) ? \strtolower($parts['host']) : '';
58 126
            $this->port = isset($parts['port']) ? $this->filterPort($parts['port']) : null;
59 126
            $this->path = isset($parts['path']) ? $this->filterPath($parts['path']) : '';
60 126
            $this->query = isset($parts['query']) ? $this->filterQueryAndFragment($parts['query']) : '';
61 126
            $this->fragment = isset($parts['fragment']) ? $this->filterQueryAndFragment($parts['fragment']) : '';
62 126
            if (isset($parts['pass'])) {
63 5
                $this->userInfo .= ':' . $parts['pass'];
64
            }
65
        }
66 146
    }
67
68 67
    public function __toString(): string
69
    {
70 67
        return self::createUriString($this->scheme, $this->getAuthority(), $this->path, $this->query, $this->fragment);
71
    }
72
73 8
    public function getScheme(): string
74
    {
75 8
        return $this->scheme;
76
    }
77
78 73
    public function getAuthority(): string
79
    {
80 73
        if ('' === $this->host) {
81 29
            return '';
82
        }
83
84 46
        $authority = $this->host;
85 46
        if ('' !== $this->userInfo) {
86 7
            $authority = $this->userInfo . '@' . $authority;
87
        }
88
89 46
        if (null !== $this->port) {
90 7
            $authority .= ':' . $this->port;
91
        }
92
93 46
        return $authority;
94
    }
95
96 7
    public function getUserInfo(): string
97
    {
98 7
        return $this->userInfo;
99
    }
100
101 90
    public function getHost(): string
102
    {
103 90
        return $this->host;
104
    }
105
106 42
    public function getPort(): ?int
107
    {
108 42
        return $this->port;
109
    }
110
111 23
    public function getPath(): string
112
    {
113 23
        return $this->path;
114
    }
115
116 19
    public function getQuery(): string
117
    {
118 19
        return $this->query;
119
    }
120
121 17
    public function getFragment(): string
122
    {
123 17
        return $this->fragment;
124
    }
125
126 12
    public function withScheme($scheme): self
127
    {
128 12
        if (!\is_string($scheme)) {
129 5
            throw new \InvalidArgumentException('Scheme must be a string');
130
        }
131
132 7
        if ($this->scheme === $scheme = \strtolower($scheme)) {
133
            return $this;
134
        }
135
136 7
        $new = clone $this;
137 7
        $new->scheme = $scheme;
138 7
        $new->port = $new->filterPort($new->port);
139
140 7
        return $new;
141
    }
142
143 5
    public function withUserInfo($user, $password = null): self
144
    {
145 5
        $info = $user;
146 5
        if (null !== $password && '' !== $password) {
147 5
            $info .= ':' . $password;
148
        }
149
150 5
        if ($this->userInfo === $info) {
151
            return $this;
152
        }
153
154 5
        $new = clone $this;
155 5
        $new->userInfo = $info;
156
157 5
        return $new;
158
    }
159
160 7
    public function withHost($host): self
161
    {
162 7
        if (!\is_string($host)) {
163 1
            throw new \InvalidArgumentException('Host must be a string');
164
        }
165
166 6
        if ($this->host === $host = \strtolower($host)) {
167
            return $this;
168
        }
169
170 6
        $new = clone $this;
171 6
        $new->host = $host;
172
173 6
        return $new;
174
    }
175
176 9
    public function withPort($port): self
177
    {
178 9
        if ($this->port === $port = $this->filterPort($port)) {
179 1
            return $this;
180
        }
181
182 6
        $new = clone $this;
183 6
        $new->port = $port;
184
185 6
        return $new;
186
    }
187
188 9
    public function withPath($path): self
189
    {
190 9
        if ($this->path === $path = $this->filterPath($path)) {
191
            return $this;
192
        }
193
194 8
        $new = clone $this;
195 8
        $new->path = $path;
196
197 8
        return $new;
198
    }
199
200 6
    public function withQuery($query): self
201
    {
202 6
        if ($this->query === $query = $this->filterQueryAndFragment($query)) {
203
            return $this;
204
        }
205
206 5
        $new = clone $this;
207 5
        $new->query = $query;
208
209 5
        return $new;
210
    }
211
212 6
    public function withFragment($fragment): self
213
    {
214 6
        if ($this->fragment === $fragment = $this->filterQueryAndFragment($fragment)) {
215
            return $this;
216
        }
217
218 5
        $new = clone $this;
219 5
        $new->fragment = $fragment;
220
221 5
        return $new;
222
    }
223
224
    /**
225
     * Create a URI string from its various parts.
226
     */
227 67
    private static function createUriString(string $scheme, string $authority, string $path, string $query, string $fragment): string
228
    {
229 67
        $uri = '';
230 67
        if ('' !== $scheme) {
231 42
            $uri .= $scheme . ':';
232
        }
233
234 67
        if ('' !== $authority) {
235 42
            $uri .= '//' . $authority;
236
        }
237
238 67
        if ('' !== $path) {
239 54
            if ('/' !== $path[0]) {
240 7
                if ('' !== $authority) {
241
                    // If the path is rootless and an authority is present, the path MUST be prefixed by "/"
242 7
                    $path = '/' . $path;
243
                }
244 47
            } elseif (isset($path[1]) && '/' === $path[1]) {
245 1
                if ('' === $authority) {
246
                    // If the path is starting with more than one "/" and no authority is present, the
247
                    // starting slashes MUST be reduced to one.
248 1
                    $path = '/' . \ltrim($path, '/');
249
                }
250
            }
251
252 54
            $uri .= $path;
253
        }
254
255 67
        if ('' !== $query) {
256 35
            $uri .= '?' . $query;
257
        }
258
259 67
        if ('' !== $fragment) {
260 16
            $uri .= '#' . $fragment;
261
        }
262
263 67
        return $uri;
264
    }
265
266
    /**
267
     * Is a given port non-standard for the current scheme?
268
     */
269 12
    private static function isNonStandardPort(string $scheme, int $port): bool
270
    {
271 12
        return !isset(self::SCHEMES[$scheme]) || $port !== self::SCHEMES[$scheme];
272
    }
273
274 17
    private function filterPort($port): ?int
275
    {
276 17
        if (null === $port) {
277 6
            return null;
278
        }
279
280 14
        $port = (int) $port;
281 14
        if (1 > $port || 0xffff < $port) {
282 2
            throw new \InvalidArgumentException(\sprintf('Invalid port: %d. Must be between 1 and 65535', $port));
283
        }
284
285 12
        return self::isNonStandardPort($this->scheme, $port) ? $port : null;
286
    }
287
288 121
    private function filterPath($path): string
289
    {
290 121
        if (!\is_string($path)) {
291 1
            throw new \InvalidArgumentException('Path must be a string');
292
        }
293
294 120
        return \preg_replace_callback('/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/', [__CLASS__, 'rawurlencodeMatchZero'], $path);
295
    }
296
297 40
    private function filterQueryAndFragment($str): string
298
    {
299 40
        if (!\is_string($str)) {
300 2
            throw new \InvalidArgumentException('Query and fragment must be a string');
301
        }
302
303 38
        return \preg_replace_callback('/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/', [__CLASS__, 'rawurlencodeMatchZero'], $str);
304
    }
305
306 6
    private static function rawurlencodeMatchZero(array $match): string
307
    {
308 6
        return \rawurlencode($match[0]);
309
    }
310
}
311