Completed
Push — master ( be4b54...4b1069 )
by ignace nyamagana
04:15
created

AbstractUri::getPort()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 2
eloc 2
nc 2
nop 0
crap 2
1
<?php
2
/**
3
 * League.Uri (http://uri.thephpleague.com)
4
 *
5
 * @package   League.uri
6
 * @author    Ignace Nyamagana Butera <[email protected]>
7
 * @copyright 2013-2015 Ignace Nyamagana Butera
8
 * @license   https://github.com/thephpleague/uri/blob/master/LICENSE (MIT License)
9
 * @version   4.2.0
10
 * @link      https://github.com/thephpleague/uri/
11
 */
12
namespace League\Uri\Schemes\Generic;
13
14
use InvalidArgumentException;
15
use League\Uri\Interfaces\Fragment;
16
use League\Uri\Interfaces\Host;
17
use League\Uri\Interfaces\Path;
18
use League\Uri\Interfaces\Port;
19
use League\Uri\Interfaces\Query;
20
use League\Uri\Interfaces\Scheme;
21
use League\Uri\Interfaces\UserInfo;
22
use League\Uri\Types\ImmutablePropertyTrait;
23
24
/**
25
 * common URI Object properties and methods
26
 *
27
 * @package League.uri
28
 * @author  Ignace Nyamagana Butera <[email protected]>
29
 * @since   4.0.0
30
 */
31
abstract class AbstractUri
32
{
33
    use ImmutablePropertyTrait;
34
35
    use UriBuilderTrait;
36
37
    /**
38
     * Host Component
39
     *
40
     * @var Host
41
     */
42
    protected $host;
43
44
    /**
45
     * Scheme Component
46
     *
47
     * @var Scheme
48
     */
49
    protected $scheme;
50
51
    /**
52
     * User Information Part
53
     *
54
     * @var UserInfo
55
     */
56
    protected $userInfo;
57
58
    /**
59
     * Port Component
60
     *
61
     * @var Port
62
     */
63
    protected $port;
64
65
    /**
66
     * Path Component
67
     *
68
     * @var Path
69
     */
70
    protected $path;
71
72
    /**
73
     * Query Component
74
     *
75
     * @var Query
76
     */
77
    protected $query;
78
79
    /**
80
     * Fragment Component
81
     *
82
     * @var Fragment
83
     */
84
    protected $fragment;
85
86
    /**
87
     * Supported Schemes
88
     *
89
     * @var array
90
     */
91
    protected static $supportedSchemes = [];
92
93
    /**
94
     * Retrieve the scheme component of the URI.
95
     *
96
     * If no scheme is present, this method MUST return an empty string.
97
     *
98
     * The value returned MUST be normalized to lowercase, per RFC 3986
99
     * Section 3.1.
100
     *
101
     * The trailing ":" character is not part of the scheme and MUST NOT be
102
     * added.
103
     *
104
     * @see https://tools.ietf.org/html/rfc3986#section-3.1
105
     *
106
     * @return string The URI scheme.
107
     */
108 599
    public function getScheme()
109
    {
110 599
        return $this->scheme->__toString();
111
    }
112
113
    /**
114
     * Return an instance with the specified scheme.
115
     *
116
     * This method MUST retain the state of the current instance, and return
117
     * an instance that contains the specified scheme.
118
     *
119
     * An empty scheme is equivalent to removing the scheme.
120
     *
121
     * @param string $scheme The scheme to use with the new instance.
122
     *
123
     * @throws InvalidArgumentException for invalid schemes.
124
     * @throws InvalidArgumentException for unsupported schemes.
125
     * @throws InvalidArgumentException for transformations that would result in a object in invalid state.
126
     *
127
     * @return static A new instance with the specified scheme.
128
     */
129 51
    public function withScheme($scheme)
130
    {
131 51
        return $this->withProperty('scheme', $this->filterPropertyValue($scheme));
132
    }
133
134
    /**
135
     * Retrieve the user information component of the URI.
136
     *
137
     * If no user information is present, this method MUST return an empty
138
     * string.
139
     *
140
     * If a user is present in the URI, this will return that value;
141
     * additionally, if the password is also present, it will be appended to the
142
     * user value, with a colon (":") separating the values.
143
     *
144
     * The trailing "@" character is not part of the user information and MUST
145
     * NOT be added.
146
     *
147
     * @return string The URI user information, in "username[:password]" format.
148
     */
149 6
    public function getUserInfo()
150
    {
151 6
        return $this->userInfo->__toString();
152
    }
153
154
    /**
155
     * Return an instance with the specified user information.
156
     *
157
     * This method MUST retain the state of the current instance, and return
158
     * an instance that contains the specified user information.
159
     *
160
     * Password is optional, but the user information MUST include the
161
     * user; an empty string for the user is equivalent to removing user
162
     * information.
163
     *
164
     * @param string      $user     The user name to use for authority.
165
     * @param null|string $password The password associated with $user.
166
     *
167
     * @throws InvalidArgumentException for transformations that would result in a object in invalid state.
168
     *
169
     * @return static A new instance with the specified user information.
170
     */
171 9
    public function withUserInfo($user, $password = null)
172
    {
173 9
        if (null === $password) {
174 6
            $password = '';
175 4
        }
176
177 9
        $userInfo = $this->userInfo->withUser($this->filterPropertyValue($user))->withPass($password);
178 9
        if ($this->userInfo->getUser() == $userInfo->getUser()
179 9
            && $this->userInfo->getPass() == $userInfo->getPass()
180 6
        ) {
181 3
            return $this;
182
        }
183
184 6
        $clone = clone $this;
185 6
        $clone->userInfo = $userInfo;
186
187 6
        return $clone;
188
    }
189
190
    /**
191
     * Retrieve the host component of the URI.
192
     *
193
     * If no host is present, this method MUST return an empty string.
194
     *
195
     * The value returned MUST be normalized to lowercase, per RFC 3986
196
     * Section 3.2.2.
197
     *
198
     * @see http://tools.ietf.org/html/rfc3986#section-3.2.2
199
     *
200
     * @return string The URI host.
201
     */
202 548
    public function getHost()
203
    {
204 548
        return $this->host->__toString();
205
    }
206
207
    /**
208
     * Return an instance with the specified host.
209
     *
210
     * This method MUST retain the state of the current instance, and return
211
     * an instance that contains the specified host.
212
     *
213
     * An empty host value is equivalent to removing the host.
214
     *
215
     * @param string $host The hostname to use with the new instance.
216
     *
217
     * @throws InvalidArgumentException for invalid hostnames.
218
     * @throws InvalidArgumentException for transformations that would result in a object in invalid state.
219
     *
220
     * @return static A new instance with the specified host.
221
     */
222 81
    public function withHost($host)
223
    {
224 81
        return $this->withProperty('host', $this->filterPropertyValue($host));
225
    }
226
227
    /**
228
     * Retrieve the port component of the URI.
229
     *
230
     * If a port is present, and it is non-standard for the current scheme,
231
     * this method MUST return it as an integer. If the port is the standard port
232
     * used with the current scheme, this method SHOULD return null.
233
     *
234
     * If no port is present, and no scheme is present, this method MUST return
235
     * a null value.
236
     *
237
     * If no port is present, but a scheme is present, this method MAY return
238
     * the standard port for that scheme, but SHOULD return null.
239
     *
240
     * @return null|int The URI port.
241
     */
242 15
    public function getPort()
243
    {
244 15
        return $this->hasStandardPort() ? null : $this->port->toInt();
245
    }
246
247
    /**
248
     * Return an instance with the specified port.
249
     *
250
     * This method MUST retain the state of the current instance, and return
251
     * an instance that contains the specified port.
252
     *
253
     * Implementations MUST raise an exception for ports outside the
254
     * established TCP and UDP port ranges.
255
     *
256
     * A null value provided for the port is equivalent to removing the port
257
     * information.
258
     *
259
     * @param null|int $port The port to use with the new instance; a null value
260
     *                       removes the port information.
261
     *
262
     * @throws InvalidArgumentException for invalid ports.
263
     * @throws InvalidArgumentException for transformations that would result in a object in invalid state.
264
     *
265
     * @return static A new instance with the specified port.
266
     */
267 9
    public function withPort($port)
268
    {
269 9
        return $this->withProperty('port', $port);
270
    }
271
272
    /**
273
     * Retrieve the path component of the URI.
274
     *
275
     * The path can either be empty or absolute (starting with a slash) or
276
     * rootless (not starting with a slash). Implementations MUST support all
277
     * three syntaxes.
278
     *
279
     * Normally, the empty path "" and absolute path "/" are considered equal as
280
     * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically
281
     * do this normalization because in contexts with a trimmed base path, e.g.
282
     * the front controller, this difference becomes significant. It's the task
283
     * of the user to handle both "" and "/".
284
     *
285
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
286
     * any characters. To determine what characters to encode, please refer to
287
     * RFC 3986, Sections 2 and 3.3.
288
     *
289
     * As an example, if the value should include a slash ("/") not intended as
290
     * delimiter between path segments, that value MUST be passed in encoded
291
     * form (e.g., "%2F") to the instance.
292
     *
293
     * @see https://tools.ietf.org/html/rfc3986#section-2
294
     * @see https://tools.ietf.org/html/rfc3986#section-3.3
295
     *
296
     * @return string The URI path.
297
     */
298 261
    public function getPath()
299
    {
300 261
        return $this->path->__toString();
301
    }
302
303
    /**
304
     * Return an instance with the specified path.
305
     *
306
     * This method MUST retain the state of the current instance, and return
307
     * an instance that contains the specified path.
308
     *
309
     * The path can either be empty or absolute (starting with a slash) or
310
     * rootless (not starting with a slash). Implementations MUST support all
311
     * three syntaxes.
312
     *
313
     * If the path is intended to be domain-relative rather than path relative then
314
     * it must begin with a slash ("/"). Paths not starting with a slash ("/")
315
     * are assumed to be relative to some base path known to the application or
316
     * consumer.
317
     *
318
     * Users can provide both encoded and decoded path characters.
319
     * Implementations ensure the correct encoding as outlined in getPath().
320
     *
321
     * @param string $path The path to use with the new instance.
322
     *
323
     * @throws InvalidArgumentException for invalid paths.
324
     * @throws InvalidArgumentException for transformations that would result in a object in invalid state.
325
     *
326
     * @return static A new instance with the specified path.
327
     */
328 249
    public function withPath($path)
329
    {
330 249
        return $this->withProperty('path', $path);
331
    }
332
333
    /**
334
     * Retrieve the query string of the URI.
335
     *
336
     * If no query string is present, this method MUST return an empty string.
337
     *
338
     * The leading "?" character is not part of the query and MUST NOT be
339
     * added.
340
     *
341
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
342
     * any characters. To determine what characters to encode, please refer to
343
     * RFC 3986, Sections 2 and 3.4.
344
     *
345
     * As an example, if a value in a key/value pair of the query string should
346
     * include an ampersand ("&") not intended as a delimiter between values,
347
     * that value MUST be passed in encoded form (e.g., "%26") to the instance.
348
     *
349
     * @see https://tools.ietf.org/html/rfc3986#section-2
350
     * @see https://tools.ietf.org/html/rfc3986#section-3.4
351
     *
352
     * @return string The URI query string.
353
     */
354 198
    public function getQuery()
355
    {
356 198
        return $this->query->__toString();
357
    }
358
359
    /**
360
     * Return an instance with the specified query string.
361
     *
362
     * This method MUST retain the state of the current instance, and return
363
     * an instance that contains the specified query string.
364
     *
365
     * Users can provide both encoded and decoded query characters.
366
     * Implementations ensure the correct encoding as outlined in getQuery().
367
     *
368
     * An empty query string value is equivalent to removing the query string.
369
     *
370
     * @param string $query The query string to use with the new instance.
371
     *
372
     * @throws InvalidArgumentException for invalid query strings.
373
     * @throws InvalidArgumentException for transformations that would result in a object in invalid state.
374
     *
375
     * @return static A new instance with the specified query string.
376
     */
377 180
    public function withQuery($query)
378
    {
379 180
        return $this->withProperty('query', $this->filterPropertyValue($query));
380
    }
381
382
    /**
383
     * Retrieve the fragment component of the URI.
384
     *
385
     * If no fragment is present, this method MUST return an empty string.
386
     *
387
     * The leading "#" character is not part of the fragment and MUST NOT be
388
     * added.
389
     *
390
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
391
     * any characters. To determine what characters to encode, please refer to
392
     * RFC 3986, Sections 2 and 3.5.
393
     *
394
     * @see https://tools.ietf.org/html/rfc3986#section-2
395
     * @see https://tools.ietf.org/html/rfc3986#section-3.5
396
     *
397
     * @return string The URI fragment.
398
     */
399 147
    public function getFragment()
400
    {
401 147
        return $this->fragment->__toString();
402
    }
403
404
    /**
405
     * Return an instance with the specified URI fragment.
406
     *
407
     * This method MUST retain the state of the current instance, and return
408
     * an instance that contains the specified URI fragment.
409
     *
410
     * Users can provide both encoded and decoded fragment characters.
411
     * Implementations ensure the correct encoding as outlined in getFragment().
412
     *
413
     * An empty fragment value is equivalent to removing the fragment.
414
     *
415
     * @param string $fragment The fragment to use with the new instance.
416
     *
417
     * @throws InvalidArgumentException for transformations that would result in a object in invalid state.
418
     *
419
     * @return static A new instance with the specified fragment.
420
     */
421 126
    public function withFragment($fragment)
422
    {
423 126
        return $this->withProperty('fragment', $this->filterPropertyValue($fragment));
424
    }
425
426
    /**
427
     * Return the string representation as a URI reference.
428
     *
429
     * Depending on which components of the URI are present, the resulting
430
     * string is either a full URI or relative reference according to RFC 3986,
431
     * Section 4.1. The method concatenates the various components of the URI,
432
     * using the appropriate delimiters:
433
     *
434
     * - If a scheme is present, it MUST be suffixed by ":".
435
     * - If an authority is present, it MUST be prefixed by "//".
436
     * - The path can be concatenated without delimiters. But there are two
437
     *   cases where the path has to be adjusted to make the URI reference
438
     *   valid as PHP does not allow to throw an exception in __toString():
439
     *     - If the path is rootless and an authority is present, the path MUST
440
     *       be prefixed by "/".
441
     *     - If the path is starting with more than one "/" and no authority is
442
     *       present, the starting slashes MUST be reduced to one.
443
     * - If a query is present, it MUST be prefixed by "?".
444
     * - If a fragment is present, it MUST be prefixed by "#".
445
     *
446
     * @see http://tools.ietf.org/html/rfc3986#section-4.1
447
     *
448
     * @return string
449
     */
450 317
    public function __toString()
451
    {
452 317
        return $this->scheme->getUriComponent().$this->getSchemeSpecificPart();
453
    }
454
455
    /**
456
     * @inheritdoc
457
     */
458 2
    public function __debugInfo()
459
    {
460 2
        return ['uri' => $this->__toString()];
461
    }
462
463
    /**
464
     * Retrieve the scheme specific part of the URI.
465
     *
466
     * If no specific part information is present, this method MUST return an empty
467
     * string.
468
     *
469
     * @return string The URI authority, in "[user-info@]host[:port]" format.
470
     */
471 587
    protected function getSchemeSpecificPart()
472
    {
473 587
        $authority = $this->getAuthority();
474
475 587
        $res = array_filter([
476 587
            $this->userInfo->getContent(),
477 587
            $this->host->getContent(),
478 587
            $this->port->getContent(),
479 587
        ], function ($value) {
480 587
            return null !== $value;
481 587
        });
482
483 587
        if ($res) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $res of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
484 530
            $authority = '//'.$authority;
485 353
        }
486
487
        return $authority
488 587
            .$this->path->getUriComponent()
489 587
            .$this->query->getUriComponent()
490 587
            .$this->fragment->getUriComponent();
491
    }
492
493
    /**
494
     * Retrieve the authority component of the URI.
495
     *
496
     * If no authority information is present, this method MUST return an empty
497
     * string.
498
     *
499
     * The authority syntax of the URI is:
500
     *
501
     * <pre>
502
     * [user-info@]host[:port]
503
     * </pre>
504
     *
505
     * If the port component is not set or is the standard port for the current
506
     * scheme, it SHOULD NOT be included.
507
     *
508
     * @see https://tools.ietf.org/html/rfc3986#section-3.2
509
     *
510
     * @return string The URI authority, in "[user-info@]host[:port]" format.
511
     */
512 617
    public function getAuthority()
513
    {
514 617
        $port = '';
515 617
        if (!$this->hasStandardPort()) {
516 152
            $port = $this->port->getUriComponent();
517 101
        }
518
519 617
        return $this->userInfo->getUriComponent().$this->host->getUriComponent().$port;
520
    }
521
522
    /**
523
     * Returns whether the standard port for the given scheme is used, when
524
     * the scheme is unknown or unsupported will the method return false
525
     *
526
     * @return bool
527
     */
528 617
    protected function hasStandardPort()
529
    {
530 617
        $port = $this->port->toInt();
531 617
        if (null === $port) {
532 489
            return true;
533
        }
534
535 164
        $scheme = $this->scheme->__toString();
536 164
        if ('' === $scheme) {
537 12
            return false;
538
        }
539
540 161
        return isset(static::$supportedSchemes[$scheme])
541 161
            && static::$supportedSchemes[$scheme] === $port;
542
    }
543
544
    /**
545
     * Assert if the current URI object is valid
546
     *
547
     * @throws InvalidArgumentException for transformations that would result in a object in invalid state.
548
     */
549 605
    protected function assertValidObject()
550
    {
551 605
        if (!$this->isValid()) {
552 30
            throw new InvalidArgumentException(sprintf(
553 30
                'The URI components will produce a `%s` instance in invalid state', get_class($this)
554 20
            ));
555
        }
556 554
    }
557
558
    /**
559
     * Tell whether the current URI is valid.
560
     *
561
     * The URI object validity depends on the scheme. This method
562
     * MUST be implemented on every URI object
563
     *
564
     * @return bool
565
     */
566
    abstract protected function isValid();
567
568
    /**
569
     *  Tell whether any generic URI is valid
570
     *
571
     * @return bool
572
     */
573 596
    protected function isValidGenericUri()
574
    {
575 596
        $path = $this->path->getUriComponent();
576 596
        $authority = $this->getAuthority();
577 596
        $scheme = $this->scheme->getUriComponent();
578
579 596
        if ('' === $authority) {
580 219
            if (0 === strpos($path, '//')) {
581 3
                return false;
582
            }
583
584 219
            if ('' !== $scheme || false === ($pos = strpos($path, ':'))) {
585 219
                return true;
586
            }
587
588 3
            return !(false === strpos(substr($path, 0, $pos), '/'));
589
        }
590
591 521
        if ('' === $path) {
592 90
            return true;
593
        }
594
595 464
        return strpos($path, '/') === 0;
596
    }
597
598
    /**
599
     * Assert whether the current scheme is supported by the URI object
600
     *
601
     * @throws InvalidArgumentException If the Scheme is not supported
602
     */
603 539
    protected function assertSupportedScheme()
604
    {
605 539
        $scheme = $this->getScheme();
606 539
        if (!isset(static::$supportedSchemes[$scheme])) {
607 33
            throw new InvalidArgumentException(sprintf(
608 33
                'The submitted scheme is unsupported by `%s`', get_class($this)
609 22
            ));
610
        }
611 509
    }
612
}
613