Uri::setPort()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php declare(strict_types=1);
2
3
/**
4
 * It's free open-source software released under the MIT License.
5
 *
6
 * @author Anatoly Nekhay <[email protected]>
7
 * @copyright Copyright (c) 2018, Anatoly Nekhay
8
 * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE
9
 * @link https://github.com/sunrise-php/http-message
10
 */
11
12
namespace Sunrise\Http\Message;
13
14
use Psr\Http\Message\UriInterface;
15
use Sunrise\Http\Message\Exception\InvalidArgumentException;
16
use Sunrise\Http\Message\Uri\Component\Fragment;
17
use Sunrise\Http\Message\Uri\Component\Host;
18
use Sunrise\Http\Message\Uri\Component\Path;
19
use Sunrise\Http\Message\Uri\Component\Port;
20
use Sunrise\Http\Message\Uri\Component\Query;
21
use Sunrise\Http\Message\Uri\Component\Scheme;
22
use Sunrise\Http\Message\Uri\Component\UserInfo;
23
24
use function is_string;
25
use function ltrim;
26
use function parse_url;
27
use function strncmp;
28
29
/**
30
 * @psalm-suppress ClassMustBeFinal
31
 */
32
class Uri implements UriInterface
33
{
34
    private string $scheme = '';
35
    private string $userInfo = '';
36
    private string $host = '';
37
    private ?int $port = null;
38
    private string $path = '';
39
    private string $query = '';
40
    private string $fragment = '';
41
42
    /**
43
     * @throws InvalidArgumentException
44
     */
45 621
    public function __construct(string $uri = '')
46
    {
47 621
        if ($uri === '') {
48 100
            return;
49
        }
50
51 525
        $this->parseUri($uri);
52
    }
53
54
    /**
55
     * @param mixed $uri
56
     *
57
     * @throws InvalidArgumentException
58
     */
59 482
    public static function create($uri): UriInterface
60
    {
61 482
        if ($uri instanceof UriInterface) {
62 98
            return $uri;
63
        }
64
65 424
        if (!is_string($uri)) {
66 1
            throw new InvalidArgumentException('URI should be a string');
67
        }
68
69 423
        return new self($uri);
70
    }
71
72
    /**
73
     * @inheritDoc
74
     */
75 19
    public function withScheme($scheme): UriInterface
76
    {
77 19
        $clone = clone $this;
78 19
        $clone->setScheme($scheme);
79
80 5
        return $clone;
81
    }
82
83
    /**
84
     * @inheritDoc
85
     *
86
     * @throws InvalidArgumentException
87
     */
88 25
    public function withUserInfo($user, $password = null): UriInterface
89
    {
90 25
        $clone = clone $this;
91 25
        $clone->setUserInfo($user, $password);
92
93 8
        return $clone;
94
    }
95
96
    /**
97
     * @inheritDoc
98
     */
99 15
    public function withHost($host): UriInterface
100
    {
101 15
        $clone = clone $this;
102 15
        $clone->setHost($host);
103
104 6
        return $clone;
105
    }
106
107
    /**
108
     * @inheritDoc
109
     */
110 15
    public function withPort($port): UriInterface
111
    {
112 15
        $clone = clone $this;
113 15
        $clone->setPort($port);
114
115 4
        return $clone;
116
    }
117
118
    /**
119
     * @inheritDoc
120
     */
121 14
    public function withPath($path): UriInterface
122
    {
123 14
        $clone = clone $this;
124 14
        $clone->setPath($path);
125
126 5
        return $clone;
127
    }
128
129
    /**
130
     * @inheritDoc
131
     */
132 13
    public function withQuery($query): UriInterface
133
    {
134 13
        $clone = clone $this;
135 13
        $clone->setQuery($query);
136
137 4
        return $clone;
138
    }
139
140
    /**
141
     * @inheritDoc
142
     *
143
     * @throws InvalidArgumentException
144
     */
145 13
    public function withFragment($fragment): UriInterface
146
    {
147 13
        $clone = clone $this;
148 13
        $clone->setFragment($fragment);
149
150 4
        return $clone;
151
    }
152
153
    /**
154
     * @inheritDoc
155
     */
156 9
    public function getScheme(): string
157
    {
158 9
        return $this->scheme;
159
    }
160
161
    /**
162
     * @inheritDoc
163
     */
164 8
    public function getUserInfo(): string
165
    {
166 8
        return $this->userInfo;
167
    }
168
169
    /**
170
     * @inheritDoc
171
     */
172 481
    public function getHost(): string
173
    {
174 481
        return $this->host;
175
    }
176
177
    /**
178
     * @inheritDoc
179
     */
180 77
    public function getPort(): ?int
181
    {
182
        // The 80 is the default port number for the HTTP protocol.
183 77
        if ($this->port === 80 && $this->scheme === 'http') {
184 3
            return null;
185
        }
186
187
        // The 443 is the default port number for the HTTPS protocol.
188 77
        if ($this->port === 443 && $this->scheme === 'https') {
189 2
            return null;
190
        }
191
192 77
        return $this->port;
193
    }
194
195
    /**
196
     * @inheritDoc
197
     */
198 51
    public function getPath(): string
199
    {
200
        // CVE-2015-3257
201 51
        if (strncmp($this->path, '//', 2) === 0) {
202 8
            return '/' . ltrim($this->path, '/');
203
        }
204
205 43
        return $this->path;
206
    }
207
208
    /**
209
     * @inheritDoc
210
     */
211 38
    public function getQuery(): string
212
    {
213 38
        return $this->query;
214
    }
215
216
    /**
217
     * @inheritDoc
218
     */
219 8
    public function getFragment(): string
220
    {
221 8
        return $this->fragment;
222
    }
223
224
    /**
225
     * @inheritDoc
226
     */
227 36
    public function getAuthority(): string
228
    {
229
        // The host is the basic subcomponent.
230 36
        if ($this->host === '') {
231 14
            return '';
232
        }
233
234 27
        $authority = $this->host;
235 27
        if ($this->userInfo !== '') {
236 7
            $authority = $this->userInfo . '@' . $authority;
237
        }
238
239 27
        $port = $this->getPort();
240 27
        if ($port !== null) {
241 9
            $authority = $authority . ':' . (string) $port;
242
        }
243
244 27
        return $authority;
245
    }
246
247
    /**
248
     * @inheritDoc
249
     */
250 34
    public function __toString(): string
251
    {
252 34
        $uri = '';
253
254 34
        $scheme = $this->scheme;
255 34
        if ($scheme !== '') {
256 23
            $uri .= $scheme . ':';
257
        }
258
259 34
        $authority = $this->getAuthority();
260 34
        if ($authority !== '') {
261 25
            $uri .= '//' . $authority;
262
        }
263
264 34
        $path = $this->path;
265 34
        if ($path !== '') {
266
            // https://github.com/sunrise-php/uri/issues/31
267
            // https://datatracker.ietf.org/doc/html/rfc3986#section-3.3
268
            //
269
            // If a URI contains an authority component,
270
            // then the path component must either be empty
271
            // or begin with a slash ("/") character.
272 32
            if ($authority !== '' && strncmp($path, '/', 1) !== 0) {
273 1
                $path = '/' . $path;
274
            }
275
276
            // https://github.com/sunrise-php/uri/issues/31
277
            // https://datatracker.ietf.org/doc/html/rfc3986#section-3.3
278
            //
279
            // If a URI does not contain an authority component,
280
            // then the path cannot begin with two slash characters ("//").
281 32
            if ($authority === '' && strncmp($path, '//', 2) === 0) {
282 1
                $path = '/' . ltrim($path, '/');
283
            }
284
285 32
            $uri .= $path;
286
        }
287
288 34
        $query = $this->query;
289 34
        if ($query !== '') {
290 6
            $uri .= '?' . $query;
291
        }
292
293 34
        $fragment = $this->fragment;
294 34
        if ($fragment !== '') {
295 4
            $uri .= '#' . $fragment;
296
        }
297
298 34
        return $uri;
299
    }
300
301
    /**
302
     * @param mixed $scheme
303
     *
304
     * @throws InvalidArgumentException
305
     */
306 113
    final protected function setScheme($scheme): void
307
    {
308 113
        $this->scheme = (new Scheme($scheme))->getValue();
309
    }
310
311
    /**
312
     * @param mixed $user
313
     * @param mixed $password
314
     *
315
     * @throws InvalidArgumentException
316
     */
317 62
    final protected function setUserInfo($user, $password): void
318
    {
319 62
        $this->userInfo = (new UserInfo($user, $password))->getValue();
320
    }
321
322
    /**
323
     * @param mixed $host
324
     *
325
     * @throws InvalidArgumentException
326
     */
327 124
    final protected function setHost($host): void
328
    {
329 124
        $this->host = (new Host($host))->getValue();
330
    }
331
332
    /**
333
     * @param mixed $port
334
     *
335
     * @throws InvalidArgumentException
336
     */
337 55
    final protected function setPort($port): void
338
    {
339 55
        $this->port = (new Port($port))->getValue();
340
    }
341
342
    /**
343
     * @param mixed $path
344
     *
345
     * @throws InvalidArgumentException
346
     */
347 528
    final protected function setPath($path): void
348
    {
349 528
        $this->path = (new Path($path))->getValue();
350
    }
351
352
    /**
353
     * @param mixed $query
354
     *
355
     * @throws InvalidArgumentException
356
     */
357 56
    final protected function setQuery($query): void
358
    {
359 56
        $this->query = (new Query($query))->getValue();
360
    }
361
362
    /**
363
     * @param mixed $fragment
364
     *
365
     * @throws InvalidArgumentException
366
     */
367 49
    final protected function setFragment($fragment): void
368
    {
369 49
        $this->fragment = (new Fragment($fragment))->getValue();
370
    }
371
372
    /**
373
     * @throws InvalidArgumentException
374
     */
375 525
    private function parseUri(string $uri): void
376
    {
377 525
        $components = parse_url($uri);
378 525
        if ($components === false) {
379 6
            throw new InvalidArgumentException('Invalid URI');
380
        }
381
382 519
        if (isset($components['scheme'])) {
383 99
            $this->setScheme($components['scheme']);
384
        }
385 519
        if (isset($components['user'])) {
386 42
            $this->setUserInfo($components['user'], $components['pass'] ?? null);
387
        }
388 519
        if (isset($components['host'])) {
389 114
            $this->setHost($components['host']);
390
        }
391 519
        if (isset($components['port'])) {
392 46
            $this->setPort($components['port']);
393
        }
394 519
        if (isset($components['path'])) {
395 517
            $this->setPath($components['path']);
396
        }
397 519
        if (isset($components['query'])) {
398 46
            $this->setQuery($components['query']);
399
        }
400 519
        if (isset($components['fragment'])) {
401 39
            $this->setFragment($components['fragment']);
402
        }
403
    }
404
}
405