Completed
Push — master ( 9cbf62...87164b )
by Dominik
01:58
created

Uri::getPathForUriWithoutAuthority()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.2
c 0
b 0
f 0
cc 4
eloc 6
nc 6
nop 1
1
<?php
2
3
namespace Saxulum\HttpMessage;
4
5
use Psr\Http\Message\UriInterface;
6
7
final class Uri implements UriInterface
8
{
9
    /**
10
     * @var string|null
11
     */
12
    private $scheme;
13
14
    /**
15
     * @var string|null
16
     */
17
    private $host;
18
19
    /**
20
     * @var int|null
21
     */
22
    private $port;
23
24
    const PORT_HTTP = 80;
25
    const PORT_HTTPS = 443;
26
27
    /**
28
     * @var string|null
29
     */
30
    private $user;
31
32
    /**
33
     * @var string|null
34
     */
35
    private $password;
36
37
    /**
38
     * @var string|null
39
     */
40
    private $path;
41
42
    /**
43
     * @var string|null
44
     */
45
    private $query;
46
47
    /**
48
     * @var string|null
49
     */
50
    private $fragment;
51
52
    /**
53
     * @var UriInterface|null
54
     */
55
    private $previous;
56
57
    /**
58
     * @param string|null       $scheme
59
     * @param string|null       $host
60
     * @param int|null          $port
61
     * @param string|null       $user
62
     * @param string|null       $password
63
     * @param string|null       $path
64
     * @param string|null       $query
65
     * @param string|null       $fragment
66
     * @param UriInterface|null $previous
67
     */
68
    public function __construct(
69
        string $scheme = null,
70
        string $host = null,
71
        int $port = null,
72
        string $user = null,
73
        string $password = null,
74
        string $path = null,
75
        string $query = null,
76
        string $fragment = null,
77
        UriInterface $previous = null
78
    ) {
79
        $this->scheme = $scheme;
80
        $this->host = $host;
81
        $this->port = $port;
82
        $this->user = $user;
83
        $this->password = $password;
84
        $this->path = $path;
85
        $this->query = $query;
86
        $this->fragment = $fragment;
87
        $this->previous = $previous;
88
    }
89
90
    /**
91
     * @param string            $uri
92
     * @param UriInterface|null $previous
93
     *
94
     * @return UriInterface
95
     *
96
     * @throws \InvalidArgumentException
97
     */
98
    public static function create(string $uri, UriInterface $previous = null)
99
    {
100
        $uriParts = parse_url($uri);
101
        if (false === $uriParts) {
102
            throw new \InvalidArgumentException(sprintf('Invalid uri format for parse_url: %s', $uri));
103
        }
104
105
        return new self(
106
            $uriParts['scheme'] ?? null,
107
            $uriParts['host'] ?? null,
108
            $uriParts['port'] ?? null,
109
            $uriParts['user'] ?? null,
110
            $uriParts['pass'] ?? null,
111
            $uriParts['path'] ?? null,
112
            $uriParts['query'] ?? null,
113
            $uriParts['fragment'] ?? null,
114
            $previous
115
        );
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121
    public function getScheme(): string
122
    {
123
        if (null === $this->scheme) {
124
            return '';
125
        }
126
127
        return strtolower($this->scheme);
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function getAuthority(): string
134
    {
135
        if ('' === $host = $this->getHost()) {
136
            return '';
137
        }
138
139
        $authority = '';
140
141
        if ('' !== $userInfo = $this->getUserInfo()) {
142
            $authority .= $userInfo.'@';
143
        }
144
145
        $authority .= $host;
146
147
        if (null !== $port = $this->getPort()) {
148
            $authority .= ':'.$port;
149
        }
150
151
        return $authority;
152
    }
153
154
    /**
155
     * {@inheritdoc}
156
     */
157
    public function getUserInfo(): string
158
    {
159
        if (null === $this->user) {
160
            return '';
161
        }
162
163
        if (null === $this->password) {
164
            return $this->user;
165
        }
166
167
        return $this->user.':'.$this->password;
168
    }
169
170
    /**
171
     * {@inheritdoc}
172
     */
173
    public function getHost(): string
174
    {
175
        if (null === $this->host) {
176
            return '';
177
        }
178
179
        return strtolower($this->host);
180
    }
181
182
    /**
183
     * {@inheritdoc}
184
     */
185
    public function getPort()
186
    {
187
        if (null === $this->port) {
188
            return null;
189
        }
190
191
        if ($this->port === $this->getPortForScheme($this->getScheme())) {
192
            return null;
193
        }
194
195
        return $this->port;
196
    }
197
198
    /**
199
     * {@inheritdoc}
200
     */
201
    public function getPath(): string
202
    {
203
        if (null === $this->path) {
204
            return '';
205
        }
206
207
        return $this->path;
208
    }
209
210
    /**
211
     * {@inheritdoc}
212
     */
213
    public function getQuery(): string
214
    {
215
        if (null === $this->query) {
216
            return '';
217
        }
218
219
        return $this->query;
220
    }
221
222
    /**
223
     * {@inheritdoc}
224
     */
225
    public function getFragment(): string
226
    {
227
        if (null === $this->fragment) {
228
            return '';
229
        }
230
231
        return $this->fragment;
232
    }
233
234
    /**
235
     * @return UriInterface|null
236
     */
237
    public function getPrevious()
238
    {
239
        return $this->previous;
240
    }
241
242
    /**
243
     * {@inheritdoc}
244
     */
245 View Code Duplication
    public function withScheme($scheme): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
246
    {
247
        return new self(
248
            $scheme ?? null,
249
            $this->host,
250
            $this->port,
251
            $this->user,
252
            $this->password,
253
            $this->path,
254
            $this->query,
255
            $this->fragment,
256
            $this
257
        );
258
    }
259
260
    /**
261
     * {@inheritdoc}
262
     */
263
    public function withUserInfo($user, $password = null): self
264
    {
265
        return new self(
266
            $this->scheme,
267
            $this->host,
268
            $this->port,
269
            $user ?? null,
270
            $password ?? null,
271
            $this->path,
272
            $this->query,
273
            $this->fragment,
274
            $this
275
        );
276
    }
277
278
    /**
279
     * {@inheritdoc}
280
     */
281 View Code Duplication
    public function withHost($host): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
282
    {
283
        return new self(
284
            $this->scheme,
285
            $host ?? null,
286
            $this->port,
287
            $this->user,
288
            $this->password,
289
            $this->path,
290
            $this->query,
291
            $this->fragment,
292
            $this
293
        );
294
    }
295
296
    /**
297
     * {@inheritdoc}
298
     */
299 View Code Duplication
    public function withPort($port): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
300
    {
301
        return new self(
302
            $this->scheme,
303
            $this->host,
304
            $port ?? null,
305
            $this->user,
306
            $this->password,
307
            $this->path,
308
            $this->query,
309
            $this->fragment,
310
            $this
311
        );
312
    }
313
314
    /**
315
     * {@inheritdoc}
316
     */
317 View Code Duplication
    public function withPath($path): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
318
    {
319
        return new self(
320
            $this->scheme,
321
            $this->host,
322
            $this->port,
323
            $this->user,
324
            $this->password,
325
            $path ?? null,
326
            $this->query,
327
            $this->fragment,
328
            $this
329
        );
330
    }
331
332
    /**
333
     * {@inheritdoc}
334
     */
335 View Code Duplication
    public function withQuery($query): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
336
    {
337
        return new self(
338
            $this->scheme,
339
            $this->host,
340
            $this->port,
341
            $this->user,
342
            $this->password,
343
            $this->path,
344
            $query ?? null,
345
            $this->fragment,
346
            $this
347
        );
348
    }
349
350
    /**
351
     * {@inheritdoc}
352
     */
353 View Code Duplication
    public function withFragment($fragment): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
354
    {
355
        return new self(
356
            $this->scheme,
357
            $this->host,
358
            $this->port,
359
            $this->user,
360
            $this->password,
361
            $this->path,
362
            $this->query,
363
            $fragment ?? null,
364
            $this
365
        );
366
    }
367
368
    /**
369
     * {@inheritdoc}
370
     */
371
    public function __toString(): string
372
    {
373
        $uri = '';
374
375
        if ('' !== $scheme = $this->getScheme()) {
376
            $uri .= $scheme.':';
377
        }
378
379
        if ('' !== $authority = $this->getAuthority()) {
380
            $uri .= '//'.$authority;
381
        }
382
383
        if ('' !== $path = $this->getPath()) {
384
            $uri .= $this->getPathForUri($authority, $path);
385
        }
386
387
        if ('' !== $query = $this->getQuery()) {
388
            $uri .= '?'.$query;
389
        }
390
391
        if ('' !== $fragment = $this->getFragment()) {
392
            $uri .= '#'.$fragment;
393
        }
394
395
        return $uri;
396
    }
397
398
    /**
399
     * @param string $scheme
400
     *
401
     * @return int|null
402
     */
403
    private function getPortForScheme(string $scheme)
404
    {
405
        $constantName = 'PORT_'.strtoupper($scheme);
406
        $reflection = new \ReflectionObject($this);
407
        if ($reflection->hasConstant($constantName)) {
408
            return $reflection->getConstant($constantName);
409
        }
410
411
        return null;
412
    }
413
414
    /**
415
     * @param string $authority
416
     * @param string $path
417
     *
418
     * @return string
419
     */
420
    private function getPathForUri(string $authority, string $path): string
421
    {
422
        if ('' !== $authority) {
423
            return $this->getPathForUriWithAuthority($path);
424
        }
425
426
        return $this->getPathForUriWithoutAuthority($path);
427
    }
428
429
    /**
430
     * @param string $path
431
     *
432
     * @return string
433
     */
434
    private function getPathForUriWithAuthority(string $path)
435
    {
436
        return '/' === $path[0] ? $path : '/'.$path;
437
    }
438
439
    /**
440
     * @param string $path
441
     *
442
     * @return string
443
     */
444
    private function getPathForUriWithoutAuthority(string $path)
445
    {
446
        $pathLength = strlen($path);
447
        for ($i = 0; $i < $pathLength; ++$i) {
448
            if ('/' !== $path[$i]) {
449
                break;
450
            }
451
        }
452
453
        return 0 === $i ? $path : '/'.substr($path, $i);
454
    }
455
}
456