Completed
Push — master ( c6a3a2...0735e1 )
by Daniel
13:20
created

Uri::getScheme()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 1
Metric Value
c 4
b 0
f 1
dl 0
loc 8
rs 9.4285
cc 2
eloc 4
nc 2
nop 0
1
<?php
2
/**
3
 * This file is part of the Uri package.
4
 *
5
 * @author Daniel Schröder <[email protected]>
6
 */
7
8
namespace GravityMedia\Uri;
9
10
use Psr\Http\Message\UriInterface;
11
12
/**
13
 * URI class.
14
 *
15
 * @package GravityMedia\Uri
16
 */
17
class Uri implements UriInterface
18
{
19
    /**
20
     * Unreserved characters used in paths, query strings, and fragments.
21
     *
22
     * @const string
23
     */
24
    const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~';
25
26
    /**
27
     * Sub-delimiters used in query strings and fragments.
28
     *
29
     * @const string
30
     */
31
    const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
32
33
    /**
34
     * Pattern for path filtering.
35
     *
36
     * @const string
37
     */
38
    const PATTERN_PATH_FILTER = '/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS
39
    . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/';
40
41
    /**
42
     * Pattern for query or fragment filtering.
43
     *
44
     * @const string
45
     */
46
    const PATTERN_QUERY_OR_FRAGMENT_FILTER = '/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS
47
    . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/';
48
49
    /**
50
     * The schemes.
51
     *
52
     * @var array
53
     */
54
    protected static $schemes = [
55
        'http' => 80,
56
        'https' => 443,
57
    ];
58
59
    /**
60
     * The scheme.
61
     *
62
     * @var null|string
63
     */
64
    protected $scheme;
65
66
    /**
67
     * The user info.
68
     *
69
     * @var null|string
70
     */
71
    protected $userInfo;
72
73
    /**
74
     * The host.
75
     *
76
     * @var null|string
77
     */
78
    protected $host;
79
80
    /**
81
     * The port.
82
     *
83
     * @var null|int
84
     */
85
    protected $port;
86
87
    /**
88
     * The path.
89
     *
90
     * @var null|string
91
     */
92
    protected $path;
93
94
    /**
95
     * The query.
96
     *
97
     * @var null|string
98
     */
99
    protected $query;
100
101
    /**
102
     * The fragment.
103
     *
104
     * @var null|string
105
     */
106
    protected $fragment;
107
108
    /**
109
     * Create URI object from array.
110
     *
111
     * @param array $array
112
     *
113
     * @return Uri
114
     * @throws \InvalidArgumentException
115
     */
116
    public static function fromArray(array $array = [])
117
    {
118
        $uri = new static();
119
120
        if (isset($array['scheme'])) {
121
            $uri = $uri->withScheme($array['scheme']);
122
        }
123
124
        if (isset($array['user'])) {
125
            $password = null;
126
            if (isset($array['pass'])) {
127
                $password = $array['pass'];
128
            }
129
130
            $uri = $uri->withUserInfo($array['user'], $password);
131
        }
132
133
        if (isset($array['host'])) {
134
            $uri = $uri->withHost($array['host']);
135
        }
136
137
        if (isset($array['port'])) {
138
            $uri = $uri->withPort($array['port']);
139
        }
140
141
        if (isset($array['path'])) {
142
            $uri = $uri->withPath($array['path']);
143
        }
144
145
        if (isset($array['query'])) {
146
            $uri = $uri->withQuery($array['query']);
147
        }
148
149
        if (isset($array['fragment'])) {
150
            $uri = $uri->withFragment($array['fragment']);
151
        }
152
153
        return $uri;
154
    }
155
156
    /**
157
     * Create URI object from string.
158
     *
159
     * @param string $string
160
     *
161
     * @return Uri
162
     * @throws \InvalidArgumentException
163
     */
164
    public static function fromString($string)
165
    {
166
        $array = parse_url($string);
167
        if (false === $array) {
168
            throw new \InvalidArgumentException('The string argument appears to be malformed');
169
        }
170
171
        return static::fromArray($array);
172
    }
173
174
    /**
175
     * Register scheme.
176
     *
177
     * @param string     $scheme
178
     * @param int|string $port
179
     *
180
     * @throws \InvalidArgumentException
181
     */
182
    public static function registerScheme($scheme, $port)
183
    {
184
        if (!is_string($scheme)) {
185
            throw new \InvalidArgumentException('Invalid scheme argument');
186
        }
187
188
        if (!is_numeric($port)) {
189
            throw new \InvalidArgumentException('Invalid port argument');
190
        }
191
192
        static::$schemes[strtolower($scheme)] = (int)$port;
193
    }
194
195
    /**
196
     * {@inheritdoc}
197
     */
198
    public function getScheme()
199
    {
200
        if (null === $this->scheme) {
201
            return '';
202
        }
203
204
        return strtolower($this->scheme);
205
    }
206
207
    /**
208
     * {@inheritdoc}
209
     */
210
    public function getAuthority()
211
    {
212
        $authority = $this->getHost();
213
        if (0 === strlen($authority)) {
214
            return '';
215
        }
216
217
        $userInfo = $this->getUserInfo();
218
        if (strlen($userInfo) > 0) {
219
            $authority = $userInfo . '@' . $authority;
220
        }
221
222
        $port = $this->getPort();
223
        if (is_int($port)) {
224
            $authority .= ':' . $port;
225
        }
226
227
        return $authority;
228
    }
229
230
    /**
231
     * {@inheritdoc}
232
     */
233
    public function getUserInfo()
234
    {
235
        if (null === $this->userInfo) {
236
            return '';
237
        }
238
239
        return $this->userInfo;
240
    }
241
242
    /**
243
     * {@inheritdoc}
244
     */
245
    public function getHost()
246
    {
247
        if (null === $this->host) {
248
            return '';
249
        }
250
251
        return strtolower($this->host);
252
    }
253
254
    /**
255
     * {@inheritdoc}
256
     */
257
    public function getPort()
258
    {
259
        $scheme = $this->getScheme();
260
        if (0 === strlen($scheme) && null === $this->port) {
261
            return null;
262
        }
263
264
        if (!isset(static::$schemes[$scheme]) || static::$schemes[$scheme] === $this->port) {
265
            return null;
266
        }
267
268
        return $this->port;
269
    }
270
271
    /**
272
     * {@inheritdoc}
273
     */
274
    public function getPath()
275
    {
276
        if (null === $this->path) {
277
            return '';
278
        }
279
280
        return $this->filterPathValue($this->path);
281
    }
282
283
    /**
284
     * {@inheritdoc}
285
     */
286
    public function getQuery()
287
    {
288
        if (null === $this->query) {
289
            return '';
290
        }
291
292
        return $this->filterQueryOrFragmentValue($this->query);
293
    }
294
295
    /**
296
     * {@inheritdoc}
297
     */
298
    public function getFragment()
299
    {
300
        if (null === $this->fragment) {
301
            return '';
302
        }
303
304
        return $this->filterQueryOrFragmentValue($this->fragment);
305
    }
306
307
    /**
308
     * Filter path value.
309
     *
310
     * @param string $value
311
     *
312
     * @return string
313
     */
314
    protected function filterPathValue($value)
315
    {
316
        return preg_replace_callback(static::PATTERN_PATH_FILTER, [$this, 'urlEncodeFirstMatch'], $value);
317
    }
318
319
    /**
320
     * Filter query or fragment value.
321
     *
322
     * @param string $value
323
     *
324
     * @return string
325
     */
326
    protected function filterQueryOrFragmentValue($value)
327
    {
328
        return preg_replace_callback(static::PATTERN_QUERY_OR_FRAGMENT_FILTER, [$this, 'urlEncodeFirstMatch'], $value);
329
    }
330
331
    /**
332
     * URL encode a the first match returned by a regex.
333
     *
334
     * @param array $matches
335
     *
336
     * @return string
337
     */
338
    protected function urlEncodeFirstMatch(array $matches)
339
    {
340
        return rawurlencode($matches[0]);
341
    }
342
343
    /**
344
     * {@inheritdoc}
345
     */
346
    public function withScheme($scheme)
347
    {
348
        if (!is_string($scheme)) {
349
            throw new \InvalidArgumentException('Invalid scheme argument');
350
        }
351
352
        $uri = clone $this;
353
        $uri->scheme = $scheme;
354
355
        return $uri;
356
    }
357
358
    /**
359
     * {@inheritdoc}
360
     */
361
    public function withUserInfo($user, $password = null)
362
    {
363
        if (!is_string($user)) {
364
            throw new \InvalidArgumentException('Invalid user argument');
365
        }
366
367
        if (null !== $password && !is_string($password)) {
368
            throw new \InvalidArgumentException('Invalid password argument');
369
        }
370
371
        $userInfo = $user;
372
        if (null !== $password) {
373
            $userInfo .= ':' . $password;
374
        }
375
376
        $uri = clone $this;
377
        $uri->userInfo = $userInfo;
378
379
        return $uri;
380
    }
381
382
    /**
383
     * {@inheritdoc}
384
     */
385 View Code Duplication
    public function withHost($host)
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...
386
    {
387
        if (!is_string($host)) {
388
            throw new \InvalidArgumentException('Invalid host argument');
389
        }
390
391
        $uri = clone $this;
392
        $uri->host = $host;
393
394
        return $uri;
395
    }
396
397
    /**
398
     * {@inheritdoc}
399
     */
400
    public function withPort($port)
401
    {
402
        if (null !== $port) {
403
            if (!is_numeric($port)) {
404
                throw new \InvalidArgumentException('Invalid port argument');
405
            }
406
407
            $port = (int)$port;
408
        }
409
410
        $uri = clone $this;
411
        $uri->port = $port;
412
413
        return $uri;
414
    }
415
416
    /**
417
     * {@inheritdoc}
418
     */
419 View Code Duplication
    public function withPath($path)
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...
420
    {
421
        if (!is_string($path)) {
422
            throw new \InvalidArgumentException('Invalid path argument');
423
        }
424
425
        $uri = clone $this;
426
        $uri->path = $path;
427
428
        return $uri;
429
    }
430
431
    /**
432
     * {@inheritdoc}
433
     */
434
    public function withQuery($query)
435
    {
436
        if (!is_string($query)) {
437
            throw new \InvalidArgumentException('Invalid query argument');
438
        }
439
440
        $uri = clone $this;
441
        $uri->query = $query;
442
443
        return $uri;
444
    }
445
446
    /**
447
     * {@inheritdoc}
448
     */
449 View Code Duplication
    public function withFragment($fragment)
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...
450
    {
451
        if (!is_string($fragment)) {
452
            throw new \InvalidArgumentException('Invalid fragment argument');
453
        }
454
455
        $uri = clone $this;
456
        $uri->fragment = $fragment;
457
458
        return $uri;
459
    }
460
461
    /**
462
     * {@inheritdoc}
463
     */
464
    public function __toString()
465
    {
466
        return $this->toString();
467
    }
468
469
    /**
470
     * Convert URI into a string representation.
471
     *
472
     * @return string
473
     */
474
    public function toString()
475
    {
476
        $uri = $this->getScheme();
477
        if (strlen($uri) > 0) {
478
            $uri .= ':';
479
        }
480
481
        $authority = $this->getAuthority();
482
        if (strlen($authority) > 0) {
483
            $uri .= '//' . $authority;
484
        }
485
486
        $path = $this->getPath();
487
        if (strlen($path) > 0) {
488
            $uri .= '/' . ltrim($this->path, '/');
489
        }
490
491
        $query = $this->getQuery();
492
        if (strlen($query) > 0) {
493
            $uri .= '?' . $query;
494
        }
495
496
        $fragment = $this->getFragment();
497
        if (strlen($fragment) > 0) {
498
            $uri .= '#' . $fragment;
499
        }
500
501
        return $uri;
502
    }
503
}
504