Completed
Push — master ( 35abb3...8654c8 )
by Derek
02:09
created

Uri::getUserInfo()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
namespace Subreality\Dilmun\Anshar\Http;
3
4
use Psr\Http\Message\UriInterface;
5
use Subreality\Dilmun\Anshar\Http\UriParts\Fragment;
6
use Subreality\Dilmun\Anshar\Http\UriParts\Host;
7
use Subreality\Dilmun\Anshar\Http\UriParts\Port;
8
use Subreality\Dilmun\Anshar\Http\UriParts\Query;
9
use Subreality\Dilmun\Anshar\Http\UriParts\Scheme;
10
use Subreality\Dilmun\Anshar\Http\UriParts\UserInfo;
11
use Subreality\Dilmun\Anshar\Utils\ArrayHelper;
12
use Subreality\Dilmun\Anshar\Utils\StringHelper;
13
14
/**
15
 * Class Uri
16
 * @package Subreality\Dilmun\Anshar\Http
17
 */
18
class Uri implements UriInterface
19
{
20
    use SchemePortsTrait;
21
22
    protected $uri_parts = array(
23
        "scheme"    => "",
24
        "hier_part" => "",
25
        "authority" => "",
26
        "user_info" => "",
27
        "host"      => "",
28
        "port"      => null,
29
        "path"      => "",
30
        "query"     => "",
31
        "fragment"  => "",
32
    );
33
34
    /** @var  Scheme */
35
    protected $scheme;
36
    /** @var  UserInfo */
37
    protected $user_info;
38
    /** @var  Host */
39
    protected $host;
40
    /** @var  Port */
41
    protected $port;
42
    /** @var  Query */
43
    protected $query;
44
    /** @var  Fragment */
45
    protected $fragment;
46
47
    protected $sub_delims = array(
48
        "!",
49
        "$",
50
        "&",
51
        "'",
52
        "(",
53
        ")",
54
        "*",
55
        "+",
56
        ",",
57
        ";",
58
        "=",
59
    );
60
61
    protected $pchar_unencoded = array(
62
        ":",
63
        "@",
64
    );
65
66
    /**
67
     * Uri constructor.  Accepts a string representing a URI and parses the string into the URI's component parts.
68
     *
69
     * @throws \InvalidArgumentException    Throws an \InvalidArgumentException when its parameter is not a string
70
     * @param string $uri
71
     */
72 101
    public function __construct($uri)
73
    {
74 101
        if (!is_string($uri)) {
75 6
            throw new \InvalidArgumentException("New Uri objects must be constructed with a string URI");
76
        }
77
78 95
        $this->explodeUri($uri);
79 95
    }
80
81
    /**
82
     * Retrieve the parsed components of the URI string.
83
     *
84
     * If the class was provided an invalid or empty URI string, URI components will be empty strings, except port,
85
     * which will be null
86
     *
87
     * @return mixed[]
88
     */
89 55
    public function getParsedUri()
90
    {
91 55
        return $this->uri_parts;
92
    }
93
94
    /**
95
     * Retrieve the scheme component of the URI.
96
     *
97
     * If no scheme is present, this method MUST return an empty string.
98
     *
99
     * The value returned MUST be normalized to lowercase, per RFC 3986
100
     * Section 3.1.
101
     *
102
     * The trailing ":" character is not part of the scheme and MUST NOT be
103
     * added.
104
     *
105
     * @see https://tools.ietf.org/html/rfc3986#section-3.1
106
     * @return string The URI scheme.
107
     */
108 3
    public function getScheme()
109
    {
110 3
        return (string) $this->scheme;
111
    }
112
113
    /**
114
     * Retrieve the authority component of the URI.
115
     *
116
     * If no authority information is present, this method MUST return an empty
117
     * string.
118
     *
119
     * The authority syntax of the URI is:
120
     *
121
     * <pre>
122
     * [user-info@]host[:port]
123
     * </pre>
124
     *
125
     * If the port component is not set or is the standard port for the current
126
     * scheme, it SHOULD NOT be included.
127
     *
128
     * @see https://tools.ietf.org/html/rfc3986#section-3.2
129
     * @return string The URI authority, in "[user-info@]host[:port]" format.
130
     */
131 4
    public function getAuthority()
132
    {
133 4
        $authority = $this->host->toUriString();
134
135 4
        $authority = $this->user_info->toUriString() . $authority;
136
137 4
        $authority = $authority . $this->port->toUriString($this->scheme);
138
139 4
        return $authority;
140 4
    }
141
142
    /**
143
     * Retrieve the user information component of the URI.
144
     *
145
     * If no user information is present, this method MUST return an empty
146
     * string.
147
     *
148
     * If a user is present in the URI, this will return that value;
149
     * additionally, if the password is also present, it will be appended to the
150
     * user value, with a colon (":") separating the values.
151
     *
152
     * The trailing "@" character is not part of the user information and MUST
153
     * NOT be added.
154
     *
155
     * @return string The URI user information, in "username[:password]" format.
156
     */
157 2
    public function getUserInfo()
158
    {
159 2
        return (string) $this->user_info;
160
    }
161
162
    /**
163
     * Retrieve the host component of the URI.
164
     *
165
     * If no host is present, this method MUST return an empty string.
166
     *
167
     * The value returned MUST be normalized to lowercase, per RFC 3986
168
     * Section 3.2.2.
169
     *
170
     * @see http://tools.ietf.org/html/rfc3986#section-3.2.2
171
     * @return string The URI host.
172
     */
173 3
    public function getHost()
174
    {
175 3
        return (string) $this->host;
176
    }
177
178
    /**
179
     * Retrieve the port component of the URI.
180
     *
181
     * If a port is present, and it is non-standard for the current scheme,
182
     * this method MUST return it as an integer. If the port is the standard port
183
     * used with the current scheme, this method SHOULD return null.
184
     *
185
     * If no port is present, and no scheme is present, this method MUST return
186
     * a null value.
187
     *
188
     * If no port is present, but a scheme is present, this method MAY return
189
     * the standard port for that scheme, but SHOULD return null.
190
     *
191
     * @return null|int The URI port.
192
     */
193 4
    public function getPort()
194
    {
195 4
        $normalized_port = $this->port->normalizePortAgainstScheme($this->scheme);
196
197 4
        return $normalized_port;
198
    }
199
200
    /**
201
     * Retrieve the path component of the URI.
202
     *
203
     * The path can either be empty or absolute (starting with a slash) or
204
     * rootless (not starting with a slash). Implementations MUST support all
205
     * three syntaxes.
206
     *
207
     * Normally, the empty path "" and absolute path "/" are considered equal as
208
     * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically
209
     * do this normalization because in contexts with a trimmed base path, e.g.
210
     * the front controller, this difference becomes significant. It's the task
211
     * of the user to handle both "" and "/".
212
     *
213
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
214
     * any characters. To determine what characters to encode, please refer to
215
     * RFC 3986, Sections 2 and 3.3.
216
     *
217
     * As an example, if the value should include a slash ("/") not intended as
218
     * delimiter between path segments, that value MUST be passed in encoded
219
     * form (e.g., "%2F") to the instance.
220
     *
221
     * @see https://tools.ietf.org/html/rfc3986#section-2
222
     * @see https://tools.ietf.org/html/rfc3986#section-3.3
223
     * @return string The URI path.
224
     */
225 8
    public function getPath()
226
    {
227 8
        $path_unencoded = array("/");
228 8
        $allowed        = implode($this->pchar_unencoded) . implode($this->sub_delims) . implode($path_unencoded);
229
230 8
        if ($this->containsUnallowedUriCharacters($this->uri_parts["path"], $allowed)) {
231 4
            $encoded_string = $this->encodeComponent($this->uri_parts["path"], $path_unencoded);
232
233 4
            return $encoded_string;
234
        } else {
235 4
            return $this->uri_parts["path"];
236
        }
237
    }
238
239
    /**
240
     * Retrieve the query string of the URI.
241
     *
242
     * If no query string is present, this method MUST return an empty string.
243
     *
244
     * The leading "?" character is not part of the query and MUST NOT be
245
     * added.
246
     *
247
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
248
     * any characters. To determine what characters to encode, please refer to
249
     * RFC 3986, Sections 2 and 3.4.
250
     *
251
     * As an example, if a value in a key/value pair of the query string should
252
     * include an ampersand ("&") not intended as a delimiter between values,
253
     * that value MUST be passed in encoded form (e.g., "%26") to the instance.
254
     *
255
     * @see https://tools.ietf.org/html/rfc3986#section-2
256
     * @see https://tools.ietf.org/html/rfc3986#section-3.4
257
     * @return string The URI query string.
258
     */
259 4
    public function getQuery()
260
    {
261 4
        return (string) $this->query;
262
    }
263
264
    /**
265
     * Retrieve the fragment component of the URI.
266
     *
267
     * If no fragment is present, this method MUST return an empty string.
268
     *
269
     * The leading "#" character is not part of the fragment and MUST NOT be
270
     * added.
271
     *
272
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
273
     * any characters. To determine what characters to encode, please refer to
274
     * RFC 3986, Sections 2 and 3.5.
275
     *
276
     * @see https://tools.ietf.org/html/rfc3986#section-2
277
     * @see https://tools.ietf.org/html/rfc3986#section-3.5
278
     * @return string The URI fragment.
279
     */
280 4
    public function getFragment()
281
    {
282 4
        return (string) $this->fragment;
283
    }
284
285
    /**
286
     * Return an instance with the specified scheme.
287
     *
288
     * This method MUST retain the state of the current instance, and return
289
     * an instance that contains the specified scheme.
290
     *
291
     * Implementations MUST support the schemes "http" and "https" case
292
     * insensitively, and MAY accommodate other schemes if required.
293
     *
294
     * An empty scheme is equivalent to removing the scheme.
295
     *
296
     * @param string $scheme The scheme to use with the new instance.
297
     * @return static A new instance with the specified scheme.
0 ignored issues
show
Documentation introduced by
Should the return type not be Uri|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
298
     * @throws \InvalidArgumentException for invalid or unsupported schemes.
299
     */
300
    public function withScheme($scheme)
301
    {
302
        // TODO: Implement withScheme() method.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
303
    }
304
305
    /**
306
     * Return an instance with the specified authority.
307
     *
308
     * This method MUST retain the state of the current instance, and return
309
     * an instance that contains the specified authority.
310
     *
311
     * Replacing the authority is equivalent to replacing or removing all authority components depending upon the
312
     * composition of the authority.
313
     *
314
     * An empty authority is equivalent to removing the authority and all authority components.
315
     *
316
     * @param string $authority The scheme to use with the new instance.
317
     * @return static A new instance with the specified authority.
318
     * @throws \InvalidArgumentException for invalid authorities.
319
     */
320 22
    public function withAuthority($authority)
321
    {
322 22
        if (!is_string($authority)) {
323 6
            throw new \InvalidArgumentException("Authority must be a string");
324 16
        } elseif (stristr($authority, "/")) {
325 1
            throw new \InvalidArgumentException("Authority must not contain a slash");
326
        }
327
328 15
        $uri_parts              = $this->uri_parts;
329 15
        $uri_parts["authority"] = $authority;
330
331 15
        $new_authority_string = $this->toString($uri_parts);
332
333 15
        $new_authority_uri = new Uri($new_authority_string);
334
335 15
        return $new_authority_uri;
336
    }
337
338
    /**
339
     * Return an instance with the specified user information.
340
     *
341
     * This method MUST retain the state of the current instance, and return
342
     * an instance that contains the specified user information.
343
     *
344
     * Password is optional, but the user information MUST include the
345
     * user; an empty string for the user is equivalent to removing user
346
     * information.
347
     *
348
     * @param string $user The user name to use for authority.
349
     * @param null|string $password The password associated with $user.
350
     * @return static A new instance with the specified user information.
0 ignored issues
show
Documentation introduced by
Should the return type not be Uri|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
351
     */
352
    public function withUserInfo($user, $password = null)
353
    {
354
        // TODO: Implement withUserInfo() method.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
355
    }
356
357
    /**
358
     * Return an instance with the specified host.
359
     *
360
     * This method MUST retain the state of the current instance, and return
361
     * an instance that contains the specified host.
362
     *
363
     * An empty host value is equivalent to removing the host.
364
     *
365
     * @param string $host The hostname to use with the new instance.
366
     * @return static A new instance with the specified host.
0 ignored issues
show
Documentation introduced by
Should the return type not be Uri|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
367
     * @throws \InvalidArgumentException for invalid hostnames.
368
     */
369
    public function withHost($host)
370
    {
371
        // TODO: Implement withHost() method.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
372
    }
373
374
    /**
375
     * Return an instance with the specified port.
376
     *
377
     * This method MUST retain the state of the current instance, and return
378
     * an instance that contains the specified port.
379
     *
380
     * Implementations MUST raise an exception for ports outside the
381
     * established TCP and UDP port ranges.
382
     *
383
     * A null value provided for the port is equivalent to removing the port
384
     * information.
385
     *
386
     * @param null|int $port The port to use with the new instance; a null value
387
     *     removes the port information.
388
     * @return static A new instance with the specified port.
0 ignored issues
show
Documentation introduced by
Should the return type not be Uri|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
389
     * @throws \InvalidArgumentException for invalid ports.
390
     */
391
    public function withPort($port)
392
    {
393
        // TODO: Implement withPort() method.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
394
    }
395
396
    /**
397
     * Return an instance with the specified path.
398
     *
399
     * This method MUST retain the state of the current instance, and return
400
     * an instance that contains the specified path.
401
     *
402
     * The path can either be empty or absolute (starting with a slash) or
403
     * rootless (not starting with a slash). Implementations MUST support all
404
     * three syntaxes.
405
     *
406
     * If the path is intended to be domain-relative rather than path relative then
407
     * it must begin with a slash ("/"). Paths not starting with a slash ("/")
408
     * are assumed to be relative to some base path known to the application or
409
     * consumer.
410
     *
411
     * Users can provide both encoded and decoded path characters.
412
     * Implementations ensure the correct encoding as outlined in getPath().
413
     *
414
     * @param string $path The path to use with the new instance.
415
     * @return static A new instance with the specified path.
416
     * @throws \InvalidArgumentException for invalid paths.
417
     */
418 18
    public function withPath($path)
419
    {
420 18
        if (!is_string($path)) {
421 6
            throw new \InvalidArgumentException("Supplied path must be a string");
422
        }
423
424 12
        $uri_parts          = $this->uri_parts;
425 12
        $uri_parts["path"]  = $path;
426 12
        $path_string_helper = new StringHelper($path);
427
428 12
        if (!empty($uri_parts["authority"]) && !empty($path) && !$path_string_helper->startsWith("/")) {
429 1
            throw new \InvalidArgumentException("Cannot create a URI with an authority given a rootless path");
430
        }
431
432 11
        $new_path_string = $this->toString($uri_parts);
433
434 11
        $new_path_uri = new Uri($new_path_string);
435
436 11
        return $new_path_uri;
437
    }
438
439
    /**
440
     * Return an instance with the specified query string.
441
     *
442
     * This method MUST retain the state of the current instance, and return
443
     * an instance that contains the specified query string.
444
     *
445
     * Users can provide both encoded and decoded query characters.
446
     * Implementations ensure the correct encoding as outlined in getQuery().
447
     *
448
     * An empty query string value is equivalent to removing the query string.
449
     *
450
     * @param string $query The query string to use with the new instance.
451
     * @return static A new instance with the specified query string.
0 ignored issues
show
Documentation introduced by
Should the return type not be Uri|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
452
     * @throws \InvalidArgumentException for invalid query strings.
453
     */
454
    public function withQuery($query)
455
    {
456
        // TODO: Implement withQuery() method.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
457
    }
458
459
    /**
460
     * Return an instance with the specified URI fragment.
461
     *
462
     * This method MUST retain the state of the current instance, and return
463
     * an instance that contains the specified URI fragment.
464
     *
465
     * Users can provide both encoded and decoded fragment characters.
466
     * Implementations ensure the correct encoding as outlined in getFragment().
467
     *
468
     * An empty fragment value is equivalent to removing the fragment.
469
     *
470
     * @param string $fragment The fragment to use with the new instance.
471
     * @return static A new instance with the specified fragment.
0 ignored issues
show
Documentation introduced by
Should the return type not be Uri|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
472
     */
473
    public function withFragment($fragment)
474
    {
475
        // TODO: Implement withFragment() method.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
476
    }
477
478
    /**
479
     * Return the string representation as a URI reference.
480
     *
481
     * Depending on which components of the URI are present, the resulting
482
     * string is either a full URI or relative reference according to RFC 3986,
483
     * Section 4.1. The method concatenates the various components of the URI,
484
     * using the appropriate delimiters:
485
     *
486
     * - If a scheme is present, it MUST be suffixed by ":".
487
     * - If an authority is present, it MUST be prefixed by "//".
488
     * - The path can be concatenated without delimiters. But there are two
489
     *   cases where the path has to be adjusted to make the URI reference
490
     *   valid as PHP does not allow to throw an exception in __toString():
491
     *     - If the path is rootless and an authority is present, the path MUST
492
     *       be prefixed by "/".
493
     *     - If the path is starting with more than one "/" and no authority is
494
     *       present, the starting slashes MUST be reduced to one.
495
     * - If a query is present, it MUST be prefixed by "?".
496
     * - If a fragment is present, it MUST be prefixed by "#".
497
     *
498
     * @see http://tools.ietf.org/html/rfc3986#section-4.1
499
     * @return string
500
     */
501 8
    public function __toString()
502
    {
503 8
        return $this->toString($this->uri_parts);
504
    }
505
506
    /**
507
     * @todo Maybe make static?
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
508
     * Converts a given array of URI parts to a string according to the specification of the __toString magic method
509
     *
510
     * @see Uri::__toString
511
     *
512
     * @param array $uri_parts  The URI parts to be combined into a string
513
     * @return string           The string combined from the array of URI parts
514
     */
515 31
    private function toString(array $uri_parts)
516
    {
517 31
        $uri_string = "";
518
519 31
        $uri_string .= $this->scheme->toUriString();
520
521 31
        $uri_string .= $this->authorityToString($uri_parts["authority"]);
522
523 31
        $uri_string .= $this->pathToString($uri_parts["path"], $uri_parts["authority"]);
524
525 31
        $uri_string .= $this->query->toUriString();
526
527 31
        $uri_string .= $this->fragment->toUriString();
528
529 31
        return $uri_string;
530
    }
531
532
    /**
533
     * Splits a string URI into its component parts, returning true if the URI string matches a valid URI's syntax
534
     * and false if the URI string does not
535
     *
536
     * @param string $uri   The URI string to be decomposed
537
     * @return bool         Returns true if the URI string matches a valid URI's syntax
538
     *                      Returns false otherwise
539
     */
540 95
    private function explodeUri($uri)
541
    {
542 95
        $reg_start     = '/^';
543 95
        $scheme_part   = '(?:(?\'scheme\'[A-Za-z0-9][^\/\?#:]+):)?';
544 95
        $hier_part     = '(?\'hier_part\'[^\?#]+)?';
545 95
        $query_part    = '(?:\?(?\'query\'[^#]*))?';
546 95
        $fragment_part = '(?:#(?\'fragment\'.*))?';
547 95
        $reg_end       = '/';
548
549 95
        $uri_syntax = $reg_start . $scheme_part . $hier_part . $query_part . $fragment_part . $reg_end;
550
551 95
        $uri_valid = preg_match($uri_syntax, $uri, $parts);
552
553 95
        $this->uri_parts = array_merge($this->uri_parts, $parts); //overwriting default values with matches
554
555 95
        $this->explodeHierParts($this->uri_parts["hier_part"]);
556
557 95
        $this->scheme    = new Scheme($this->uri_parts["scheme"]);
558 95
        $this->query     = new Query($this->uri_parts["query"]);
559 95
        $this->fragment  = new Fragment($this->uri_parts["fragment"]);
560 95
        $this->user_info = new UserInfo($this->uri_parts["user_info"]);
561 95
        $this->host      = new Host($this->uri_parts["host"]);
562 95
        $this->port      = new Port($this->uri_parts["port"]);
563
564 95
        $this->sanitizeUriPartsArray();
565
566 95
        return (bool) $uri_valid;
567
    }
568
569
    /**
570
     * Splits URI hierarchy data into authority and path data.
571
     *
572
     * @param string $hier_part     The hierarchy part of a URI to be decomposed
573
     * @return void
574
     */
575 95
    private function explodeHierParts($hier_part)
576
    {
577 95
        $authority_parts = array();
578
579 95
        $reg_start      = '/^';
580 95
        $authority_part = '(?:(?:\/\/)(?\'authority\'.[^\/]+))?';
581 95
        $path_part      = '(?\'path\'.+)?';
582 95
        $reg_end        = '/';
583
584 95
        $hier_part_syntax = $reg_start . $authority_part . $path_part . $reg_end;
585
586 95
        preg_match($hier_part_syntax, $hier_part, $hier_parts);
587
588 95
        if (isset($hier_parts["authority"])) {
589 90
            $authority_parts = $this->explodeAuthority($hier_parts["authority"]);
590 90
        }
591
592 95
        $hier_parts = array_merge($hier_parts, $authority_parts);
593
594 95
        $this->uri_parts = array_merge($this->uri_parts, $hier_parts);
595 95
    }
596
597
    /**
598
     * Splits URI authority data into user info, host, and port data, returning an array with named keys.
599
     *
600
     * For the host component, it will capture everything within brackets to support ipv6 or match all characters until
601
     * it finds a colon indicating the start of the port component.
602
     *
603
     * @param string $authority     The authority part of a URI to be decomposed
604
     * @return mixed[]              An array with named keys containing the component parts of the supplied
0 ignored issues
show
Documentation introduced by
Should the return type not be array<*,string|integer|null>?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
605
     *                              authority
606
     */
607 90 View Code Duplication
    private function explodeAuthority($authority)
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...
608
    {
609 90
        $reg_start      = '/^';
610 90
        $user_info_part = '(?:(?\'user_info\'.+)@)?';
611 90
        $host_part      = '(?\'host\'\[.+\]|.[^:]+)';
612 90
        $port_part      = '(?::(?\'port\'[0-9]+))?';
613 90
        $reg_end        = '/';
614
615 90
        $authority_syntax = $reg_start . $user_info_part . $host_part . $port_part . $reg_end;
616
617 90
        preg_match($authority_syntax, $authority, $authority_parts);
618
619 90
        if (isset($authority_parts["port"])) {
620 35
            $authority_parts["port"] = (int) $authority_parts["port"];
621 35
        }
622
623 90
        return $authority_parts;
624
    }
625
626
    /**
627
     * Sanitizes the URI component array by removing redundant key/value pairs
628
     *
629
     * @return void
630
     */
631 95
    private function sanitizeUriPartsArray()
632
    {
633 95
        $uri_part_array = new ArrayHelper($this->uri_parts);
634
635 95
        $this->uri_parts = $uri_part_array->removeNumericKeys();
636 95
    }
637
638
    /**
639
     * Percent encodes a component string except for sub-delims and unencoded pchar characters as defined by RFC 3986
640
     * in addition to any component-specific unencoded characters
641
     *
642
     * @param string $component_string          The string representing a URI component
643
     * @param string[] $component_unencoded     [OPTIONAL] Any additional unencoded characters specific to the component
644
     *
645
     * @return string                           The string with appropriate characters percent-encoded
646
     */
647 4
    private function encodeComponent($component_string, array $component_unencoded = array())
648
    {
649 4
        $uri_unencoded = array_merge($component_unencoded, $this->sub_delims, $this->pchar_unencoded);
650
651 4
        $string_helper = new StringHelper($component_string);
652
653 4
        $encoded_string = $string_helper->affectChunks("rawurlencode", ...$uri_unencoded);
654
655 4
        return $encoded_string;
656
    }
657
658
    /**
659
     * Determines whether a string contains unallowed URI characters, provided a string of allowed characters for a
660
     * given component.
661
     *
662
     * Note that a percent-encoded character (e.g. %20 for space) automatically counts as an allowed character, whereas
663
     * a percent sign not followed by two hex digits (e.g. %2X) does not count as an allowed character.
664
     *
665
     * @param string $string    The string to be checked for unallowed characters
666
     * @param string $allowed   A string containing all allowed characters for a given component
667
     *
668
     * @return bool             Returns true if the string contains unallowed characters
669
     *                          Returns false if the string contains only allowed characters (including percent-encoded
670
     *                          characters)
671
     */
672 8
    private function containsUnallowedUriCharacters($string, $allowed)
673
    {
674 8
        $allowed = preg_quote($allowed, "/");
675
676 8
        $pattern = "/^([0-9a-zA-Z\\.\\-_~{$allowed}]|%[0-9a-fA-F]{2})*\$/";
677
678 8
        $matches_allowed = preg_match($pattern, $string);
679
680 8
        return (bool) !$matches_allowed;
681
    }
682
683
    /**
684
     * Returns the appropriate authority string based upon __toString specification rules.
685
     *
686
     * @see Uri::__toString()
687
     *
688
     * @param string $authority     The authority to compile into a URI-friendly string
689
     *
690
     * @return string               The URI-friendly authority string
691
     */
692 31
    private function authorityToString($authority)
693
    {
694 31
        $authority_string = "";
695
696 31
        if (!empty($authority)) {
697 23
            $authority_string .= "//" . $authority;
698 23
        }
699
700 31
        return $authority_string;
701
    }
702
703
    /**
704
     * Returns the appropriate path string based upon __toString specification rules.
705
     *
706
     * @see Uri::__toString()
707
     *
708
     * @param string $path          The path to compile into a URI-friendly string
709
     * @param string $authority     [optional] The authority of the URI
710
     *
711
     * @return string               The URI-friendly path string
712
     */
713 31
    private function pathToString($path, $authority = "")
714
    {
715 31
        $path_string        = "";
716 31
        $path_string_helper = new StringHelper($path);
717
718 31
        if (empty($authority)) {
719 9
            $collapsed_slashes = $path_string_helper->collapseStartingRepetition("/");
720
721 9
            $path_string .= $collapsed_slashes;
722 31
        } elseif (!empty($path)) {
723 21
            if (!$path_string_helper->startsWith("/")) {
724 1
                $path_string .= "/" . $path;
725 1
            } else {
726 21
                $path_string .= $path;
727
            }
728 21
        }
729
730 31
        return $path_string;
731
    }
732
}
733