Completed
Push — master ( c4f3d2...79d3f7 )
by Derek
02:16
created

Uri   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 735
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 92.59%

Importance

Changes 9
Bugs 0 Features 0
Metric Value
wmc 45
c 9
b 0
f 0
lcom 1
cbo 7
dl 0
loc 735
ccs 150
cts 162
cp 0.9259
rs 8

29 Methods

Rating   Name   Duplication   Size   Complexity  
A explodeHierParts() 0 21 2
A explodeAuthority() 0 18 2
A normalizePort() 0 14 2
A sanitizeUriPartsArray() 0 6 1
A encodeComponent() 0 10 1
A containsUnallowedUriCharacters() 0 10 1
A authorityToString() 0 10 2
A pathToString() 0 19 4
A withScheme() 0 4 1
A withAuthority() 0 17 3
A withUserInfo() 0 4 1
A withHost() 0 4 1
A withPort() 0 4 1
B withPath() 0 20 5
A withQuery() 0 4 1
A withFragment() 0 4 1
A __toString() 0 4 1
A toString() 0 16 1
A __construct() 0 8 2
A getParsedUri() 0 4 1
A getScheme() 0 4 1
A getAuthority() 0 14 2
A getUserInfo() 0 4 1
A getHost() 0 4 1
A getPort() 0 6 1
A getPath() 0 13 2
A getQuery() 0 4 1
A getFragment() 0 4 1
B explodeUri() 0 26 1

How to fix   Complexity   

Complex Class

Complex classes like Uri often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Uri, and based on these observations, apply Extract Interface, too.

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\Query;
7
use Subreality\Dilmun\Anshar\Http\UriParts\Scheme;
8
use Subreality\Dilmun\Anshar\Http\UriParts\UserInfo;
9
use Subreality\Dilmun\Anshar\Utils\ArrayHelper;
10
use Subreality\Dilmun\Anshar\Utils\StringHelper;
11
12
/**
13
 * Class Uri
14
 * @package Subreality\Dilmun\Anshar\Http
15
 */
16
class Uri implements UriInterface
17
{
18
    use SchemePortsTrait;
19
20
    protected $uri_parts = array(
21
        "scheme"    => "",
22
        "hier_part" => "",
23
        "authority" => "",
24
        "user_info" => "",
25
        "host"      => "",
26
        "port"      => null,
27
        "path"      => "",
28
        "query"     => "",
29
        "fragment"  => "",
30
    );
31
32
    /** @var  Scheme */
33
    protected $scheme;
34
    /** @var  UserInfo */
35
    protected $user_info;
36
    /** @var  Query */
37
    protected $query;
38
    /** @var  Fragment */
39
    protected $fragment;
40
41
    protected $sub_delims = array(
42
        "!",
43
        "$",
44
        "&",
45
        "'",
46
        "(",
47
        ")",
48
        "*",
49
        "+",
50
        ",",
51
        ";",
52
        "=",
53
    );
54
55
    protected $pchar_unencoded = array(
56
        ":",
57
        "@",
58
    );
59
60
    /**
61
     * Uri constructor.  Accepts a string representing a URI and parses the string into the URI's component parts.
62
     *
63
     * @throws \InvalidArgumentException    Throws an \InvalidArgumentException when its parameter is not a string
64
     * @param string $uri
65
     */
66 81
    public function __construct($uri)
67
    {
68 81
        if (!is_string($uri)) {
69 6
            throw new \InvalidArgumentException("New Uri objects must be constructed with a string URI");
70
        }
71
72 75
        $this->explodeUri($uri);
73 75
    }
74
75
    /**
76
     * @todo Add a distinct test for this outside of constructor test
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...
77
     * Retrieve the parsed components of the URI string.
78
     *
79
     * If the class was provided an invalid URI string, URI components will be empty strings, except port, which will
80
     * be null
81
     *
82
     * @return mixed[]
83
     */
84 34
    public function getParsedUri()
85
    {
86 34
        return $this->uri_parts;
87
    }
88
89
    /**
90
     * Retrieve the scheme component of the URI.
91
     *
92
     * If no scheme is present, this method MUST return an empty string.
93
     *
94
     * The value returned MUST be normalized to lowercase, per RFC 3986
95
     * Section 3.1.
96
     *
97
     * The trailing ":" character is not part of the scheme and MUST NOT be
98
     * added.
99
     *
100
     * @see https://tools.ietf.org/html/rfc3986#section-3.1
101
     * @return string The URI scheme.
102
     */
103 3
    public function getScheme()
104
    {
105 3
        return (string) $this->scheme;
106
    }
107
108
    /**
109
     * Retrieve the authority component of the URI.
110
     *
111
     * If no authority information is present, this method MUST return an empty
112
     * string.
113
     *
114
     * The authority syntax of the URI is:
115
     *
116
     * <pre>
117
     * [user-info@]host[:port]
118
     * </pre>
119
     *
120
     * If the port component is not set or is the standard port for the current
121
     * scheme, it SHOULD NOT be included.
122
     *
123
     * @see https://tools.ietf.org/html/rfc3986#section-3.2
124
     * @return string The URI authority, in "[user-info@]host[:port]" format.
125
     */
126 4
    public function getAuthority()
127
    {
128 4
        $normalized_authority = $this->uri_parts["host"];
129
130 4
        $normalized_authority = $this->user_info->toUriString() . $normalized_authority;
131
132 4
        $normalized_port = $this->normalizePort();
133
134 4
        if (!is_null($normalized_port)) {
135 2
            $normalized_authority = $normalized_authority . ":" . $normalized_port;
136 2
        }
137
138 4
        return $normalized_authority;
139
    }
140
141
    /**
142
     * Retrieve the user information component of the URI.
143
     *
144
     * If no user information is present, this method MUST return an empty
145
     * string.
146
     *
147
     * If a user is present in the URI, this will return that value;
148
     * additionally, if the password is also present, it will be appended to the
149
     * user value, with a colon (":") separating the values.
150
     *
151
     * The trailing "@" character is not part of the user information and MUST
152
     * NOT be added.
153
     *
154
     * @return string The URI user information, in "username[:password]" format.
155
     */
156 2
    public function getUserInfo()
157
    {
158 2
        return (string) $this->user_info;
159
    }
160
161
    /**
162
     * Retrieve the host component of the URI.
163
     *
164
     * If no host is present, this method MUST return an empty string.
165
     *
166
     * The value returned MUST be normalized to lowercase, per RFC 3986
167
     * Section 3.2.2.
168
     *
169
     * @see http://tools.ietf.org/html/rfc3986#section-3.2.2
170
     * @return string The URI host.
171
     */
172 3
    public function getHost()
173
    {
174 3
        return strtolower($this->uri_parts["host"]);
175
    }
176
177
    /**
178
     * Retrieve the port component of the URI.
179
     *
180
     * If a port is present, and it is non-standard for the current scheme,
181
     * this method MUST return it as an integer. If the port is the standard port
182
     * used with the current scheme, this method SHOULD return null.
183
     *
184
     * If no port is present, and no scheme is present, this method MUST return
185
     * a null value.
186
     *
187
     * If no port is present, but a scheme is present, this method MAY return
188
     * the standard port for that scheme, but SHOULD return null.
189
     *
190
     * @return null|int The URI port.
191
     */
192 4
    public function getPort()
193
    {
194 4
        $normalized_port = $this->normalizePort();
195
196 4
        return $normalized_port;
197
    }
198
199
    /**
200
     * Retrieve the path component of the URI.
201
     *
202
     * The path can either be empty or absolute (starting with a slash) or
203
     * rootless (not starting with a slash). Implementations MUST support all
204
     * three syntaxes.
205
     *
206
     * Normally, the empty path "" and absolute path "/" are considered equal as
207
     * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically
208
     * do this normalization because in contexts with a trimmed base path, e.g.
209
     * the front controller, this difference becomes significant. It's the task
210
     * of the user to handle both "" and "/".
211
     *
212
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
213
     * any characters. To determine what characters to encode, please refer to
214
     * RFC 3986, Sections 2 and 3.3.
215
     *
216
     * As an example, if the value should include a slash ("/") not intended as
217
     * delimiter between path segments, that value MUST be passed in encoded
218
     * form (e.g., "%2F") to the instance.
219
     *
220
     * @see https://tools.ietf.org/html/rfc3986#section-2
221
     * @see https://tools.ietf.org/html/rfc3986#section-3.3
222
     * @return string The URI path.
223
     */
224 8
    public function getPath()
225
    {
226 8
        $path_unencoded = array("/");
227 8
        $allowed        = implode($this->pchar_unencoded) . implode($this->sub_delims) . implode($path_unencoded);
228
229 8
        if ($this->containsUnallowedUriCharacters($this->uri_parts["path"], $allowed)) {
230 4
            $encoded_string = $this->encodeComponent($this->uri_parts["path"], $path_unencoded);
231
232 4
            return $encoded_string;
233
        } else {
234 4
            return $this->uri_parts["path"];
235
        }
236
    }
237
238
    /**
239
     * Retrieve the query string of the URI.
240
     *
241
     * If no query string is present, this method MUST return an empty string.
242
     *
243
     * The leading "?" character is not part of the query and MUST NOT be
244
     * added.
245
     *
246
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
247
     * any characters. To determine what characters to encode, please refer to
248
     * RFC 3986, Sections 2 and 3.4.
249
     *
250
     * As an example, if a value in a key/value pair of the query string should
251
     * include an ampersand ("&") not intended as a delimiter between values,
252
     * that value MUST be passed in encoded form (e.g., "%26") to the instance.
253
     *
254
     * @see https://tools.ietf.org/html/rfc3986#section-2
255
     * @see https://tools.ietf.org/html/rfc3986#section-3.4
256
     * @return string The URI query string.
257
     */
258 4
    public function getQuery()
259
    {
260 4
        return (string) $this->query;
261
    }
262
263
    /**
264
     * Retrieve the fragment component of the URI.
265
     *
266
     * If no fragment is present, this method MUST return an empty string.
267
     *
268
     * The leading "#" character is not part of the fragment and MUST NOT be
269
     * added.
270
     *
271
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
272
     * any characters. To determine what characters to encode, please refer to
273
     * RFC 3986, Sections 2 and 3.5.
274
     *
275
     * @see https://tools.ietf.org/html/rfc3986#section-2
276
     * @see https://tools.ietf.org/html/rfc3986#section-3.5
277
     * @return string The URI fragment.
278
     */
279 4
    public function getFragment()
280
    {
281 4
        return (string) $this->fragment;
282
    }
283
284
    /**
285
     * Return an instance with the specified scheme.
286
     *
287
     * This method MUST retain the state of the current instance, and return
288
     * an instance that contains the specified scheme.
289
     *
290
     * Implementations MUST support the schemes "http" and "https" case
291
     * insensitively, and MAY accommodate other schemes if required.
292
     *
293
     * An empty scheme is equivalent to removing the scheme.
294
     *
295
     * @param string $scheme The scheme to use with the new instance.
296
     * @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...
297
     * @throws \InvalidArgumentException for invalid or unsupported schemes.
298
     */
299
    public function withScheme($scheme)
300
    {
301
        // 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...
302
    }
303
304
    /**
305
     * Return an instance with the specified authority.
306
     *
307
     * This method MUST retain the state of the current instance, and return
308
     * an instance that contains the specified authority.
309
     *
310
     * Replacing the authority is equivalent to replacing or removing all authority components depending upon the
311
     * composition of the authority.
312
     *
313
     * An empty authority is equivalent to removing the authority and all authority components.
314
     *
315
     * @param string $authority The scheme to use with the new instance.
316
     * @return static A new instance with the specified authority.
317
     * @throws \InvalidArgumentException for invalid authorities.
318
     */
319 22
    public function withAuthority($authority)
320
    {
321 22
        if (!is_string($authority)) {
322 6
            throw new \InvalidArgumentException("Authority must be a string");
323 16
        } elseif (stristr($authority, "/")) {
324 1
            throw new \InvalidArgumentException("Authority must not contain a slash");
325
        }
326
327 15
        $uri_parts              = $this->uri_parts;
328 15
        $uri_parts["authority"] = $authority;
329
330 15
        $new_authority_string = $this->toString($uri_parts);
331
332 15
        $new_authority_uri = new Uri($new_authority_string);
333
334 15
        return $new_authority_uri;
335
    }
336
337
    /**
338
     * Return an instance with the specified user information.
339
     *
340
     * This method MUST retain the state of the current instance, and return
341
     * an instance that contains the specified user information.
342
     *
343
     * Password is optional, but the user information MUST include the
344
     * user; an empty string for the user is equivalent to removing user
345
     * information.
346
     *
347
     * @param string $user The user name to use for authority.
348
     * @param null|string $password The password associated with $user.
349
     * @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...
350
     */
351
    public function withUserInfo($user, $password = null)
352
    {
353
        // 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...
354
    }
355
356
    /**
357
     * Return an instance with the specified host.
358
     *
359
     * This method MUST retain the state of the current instance, and return
360
     * an instance that contains the specified host.
361
     *
362
     * An empty host value is equivalent to removing the host.
363
     *
364
     * @param string $host The hostname to use with the new instance.
365
     * @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...
366
     * @throws \InvalidArgumentException for invalid hostnames.
367
     */
368
    public function withHost($host)
369
    {
370
        // 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...
371
    }
372
373
    /**
374
     * Return an instance with the specified port.
375
     *
376
     * This method MUST retain the state of the current instance, and return
377
     * an instance that contains the specified port.
378
     *
379
     * Implementations MUST raise an exception for ports outside the
380
     * established TCP and UDP port ranges.
381
     *
382
     * A null value provided for the port is equivalent to removing the port
383
     * information.
384
     *
385
     * @param null|int $port The port to use with the new instance; a null value
386
     *     removes the port information.
387
     * @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...
388
     * @throws \InvalidArgumentException for invalid ports.
389
     */
390
    public function withPort($port)
391
    {
392
        // 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...
393
    }
394
395
    /**
396
     * Return an instance with the specified path.
397
     *
398
     * This method MUST retain the state of the current instance, and return
399
     * an instance that contains the specified path.
400
     *
401
     * The path can either be empty or absolute (starting with a slash) or
402
     * rootless (not starting with a slash). Implementations MUST support all
403
     * three syntaxes.
404
     *
405
     * If the path is intended to be domain-relative rather than path relative then
406
     * it must begin with a slash ("/"). Paths not starting with a slash ("/")
407
     * are assumed to be relative to some base path known to the application or
408
     * consumer.
409
     *
410
     * Users can provide both encoded and decoded path characters.
411
     * Implementations ensure the correct encoding as outlined in getPath().
412
     *
413
     * @param string $path The path to use with the new instance.
414
     * @return static A new instance with the specified path.
415
     * @throws \InvalidArgumentException for invalid paths.
416
     */
417 18
    public function withPath($path)
418
    {
419 18
        if (!is_string($path)) {
420 6
            throw new \InvalidArgumentException("Supplied path must be a string");
421
        }
422
423 12
        $uri_parts          = $this->uri_parts;
424 12
        $uri_parts["path"]  = $path;
425 12
        $path_string_helper = new StringHelper($path);
426
427 12
        if (!empty($uri_parts["authority"]) && !empty($path) && !$path_string_helper->startsWith("/")) {
428 1
            throw new \InvalidArgumentException("Cannot create a URI with an authority given a rootless path");
429
        }
430
431 11
        $new_path_string = $this->toString($uri_parts);
432
433 11
        $new_path_uri = new Uri($new_path_string);
434
435 11
        return $new_path_uri;
436
    }
437
438
    /**
439
     * Return an instance with the specified query string.
440
     *
441
     * This method MUST retain the state of the current instance, and return
442
     * an instance that contains the specified query string.
443
     *
444
     * Users can provide both encoded and decoded query characters.
445
     * Implementations ensure the correct encoding as outlined in getQuery().
446
     *
447
     * An empty query string value is equivalent to removing the query string.
448
     *
449
     * @param string $query The query string to use with the new instance.
450
     * @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...
451
     * @throws \InvalidArgumentException for invalid query strings.
452
     */
453
    public function withQuery($query)
454
    {
455
        // 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...
456
    }
457
458
    /**
459
     * Return an instance with the specified URI fragment.
460
     *
461
     * This method MUST retain the state of the current instance, and return
462
     * an instance that contains the specified URI fragment.
463
     *
464
     * Users can provide both encoded and decoded fragment characters.
465
     * Implementations ensure the correct encoding as outlined in getFragment().
466
     *
467
     * An empty fragment value is equivalent to removing the fragment.
468
     *
469
     * @param string $fragment The fragment to use with the new instance.
470
     * @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...
471
     */
472
    public function withFragment($fragment)
473
    {
474
        // 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...
475
    }
476
477
    /**
478
     * Return the string representation as a URI reference.
479
     *
480
     * Depending on which components of the URI are present, the resulting
481
     * string is either a full URI or relative reference according to RFC 3986,
482
     * Section 4.1. The method concatenates the various components of the URI,
483
     * using the appropriate delimiters:
484
     *
485
     * - If a scheme is present, it MUST be suffixed by ":".
486
     * - If an authority is present, it MUST be prefixed by "//".
487
     * - The path can be concatenated without delimiters. But there are two
488
     *   cases where the path has to be adjusted to make the URI reference
489
     *   valid as PHP does not allow to throw an exception in __toString():
490
     *     - If the path is rootless and an authority is present, the path MUST
491
     *       be prefixed by "/".
492
     *     - If the path is starting with more than one "/" and no authority is
493
     *       present, the starting slashes MUST be reduced to one.
494
     * - If a query is present, it MUST be prefixed by "?".
495
     * - If a fragment is present, it MUST be prefixed by "#".
496
     *
497
     * @see http://tools.ietf.org/html/rfc3986#section-4.1
498
     * @return string
499
     */
500 8
    public function __toString()
501
    {
502 8
        return $this->toString($this->uri_parts);
503
    }
504
505
    /**
506
     * @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...
507
     * Converts a given array of URI parts to a string according to the specification of the __toString magic method
508
     *
509
     * @see Uri::__toString
510
     *
511
     * @param array $uri_parts  The URI parts to be combined into a string
512
     * @return string           The string combined from the array of URI parts
513
     */
514 31
    private function toString(array $uri_parts)
515
    {
516 31
        $uri_string = "";
517
518 31
        $uri_string .= $this->scheme->toUriString();
519
520 31
        $uri_string .= $this->authorityToString($uri_parts["authority"]);
521
522 31
        $uri_string .= $this->pathToString($uri_parts["path"], $uri_parts["authority"]);
523
524 31
        $uri_string .= $this->query->toUriString();
525
526 31
        $uri_string .= $this->fragment->toUriString();
527
528 31
        return $uri_string;
529
    }
530
531
    /**
532
     * Splits a string URI into its component parts, returning true if the URI string matches a valid URI's syntax
533
     * and false if the URI string does not
534
     *
535
     * @param string $uri   The URI string to be decomposed
536
     * @return bool         Returns true if the URI string matches a valid URI's syntax
537
     *                      Returns false otherwise
538
     */
539 75
    private function explodeUri($uri)
540
    {
541 75
        $reg_start     = '/^';
542 75
        $scheme_part   = '(?:(?\'scheme\'[A-Za-z0-9][^\/\?#:]+):)?';
543 75
        $hier_part     = '(?\'hier_part\'[^\?#]+)?';
544 75
        $query_part    = '(?:\?(?\'query\'[^#]*))?';
545 75
        $fragment_part = '(?:#(?\'fragment\'.*))?';
546 75
        $reg_end       = '/';
547
548 75
        $uri_syntax = $reg_start . $scheme_part . $hier_part . $query_part . $fragment_part . $reg_end;
549
550 75
        $uri_valid = preg_match($uri_syntax, $uri, $parts);
551
552 75
        $this->uri_parts = array_merge($this->uri_parts, $parts); //overwriting default values with matches
553
554 75
        $this->explodeHierParts($this->uri_parts["hier_part"]);
555
556 75
        $this->scheme   = new Scheme($this->uri_parts["scheme"]);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 3 spaces

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
557 75
        $this->query    = new Query($this->uri_parts["query"]);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 4 spaces

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
558 75
        $this->fragment = new Fragment($this->uri_parts["fragment"]);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
559 75
        $this->user_info = new UserInfo($this->uri_parts["user_info"]);
560
561 75
        $this->sanitizeUriPartsArray();
562
563 75
        return (bool) $uri_valid;
564
    }
565
566
    /**
567
     * Splits URI hierarchy data into authority and path data.
568
     *
569
     * @param string $hier_part     The hierarchy part of a URI to be decomposed
570
     * @return void
571
     */
572 75
    private function explodeHierParts($hier_part)
573
    {
574 75
        $authority_parts = array();
575
576 75
        $reg_start      = '/^';
577 75
        $authority_part = '(?:(?:\/\/)(?\'authority\'.[^\/]+))?';
578 75
        $path_part      = '(?\'path\'.+)?';
579 75
        $reg_end        = '/';
580
581 75
        $hier_part_syntax = $reg_start . $authority_part . $path_part . $reg_end;
582
583 75
        preg_match($hier_part_syntax, $hier_part, $hier_parts);
584
585 75
        if (isset($hier_parts["authority"])) {
586 74
            $authority_parts = $this->explodeAuthority($hier_parts["authority"]);
587 74
        }
588
589 75
        $hier_parts = array_merge($hier_parts, $authority_parts);
590
591 75
        $this->uri_parts = array_merge($this->uri_parts, $hier_parts);
592 75
    }
593
594
    /**
595
     * Splits URI authority data into user info, host, and port data, returning an array with named keys.
596
     *
597
     * For the host component, it will capture everything within brackets to support ipv6 or match all characters until
598
     * it finds a colon indicating the start of the port component.
599
     *
600
     * @param string $authority     The authority part of a URI to be decomposed
601
     * @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...
602
     *                              authority
603
     */
604 74
    private function explodeAuthority($authority)
605
    {
606 74
        $reg_start      = '/^';
607 74
        $user_info_part = '(?:(?\'user_info\'.+)@)?';
608 74
        $host_part      = '(?\'host\'\[.+\]|.[^:]+)';
609 74
        $port_part      = '(?::(?\'port\'[0-9]+))?';
610 74
        $reg_end        = '/';
611
612 74
        $authority_syntax = $reg_start . $user_info_part . $host_part . $port_part . $reg_end;
613
614 74
        preg_match($authority_syntax, $authority, $authority_parts);
615
616 74
        if (isset($authority_parts["port"])) {
617 33
            $authority_parts["port"] = (int) $authority_parts["port"];
618 33
        }
619
620 74
        return $authority_parts;
621
    }
622
623
    /**
624
     * Normalizes a port string based on whether the URI's port is standard for its scheme
625
     *
626
     * @return int|null     Returns null if the port is standard for the scheme
627
     *                      Returns the port prepended with a colon if the port is not standard for the scheme
628
     */
629 8
    private function normalizePort()
630
    {
631 8
        $scheme_port_array = new ArrayHelper($this->scheme_ports);
632
633 8
        $standard_port = $scheme_port_array->valueLookup($this->uri_parts["scheme"]);
634
635 8
        if ($this->uri_parts["port"] == $standard_port) {
636 5
            $normalized_port = null;
637 5
        } else {
638 3
            $normalized_port = $this->uri_parts["port"];
639
        }
640
641 8
        return $normalized_port;
642
    }
643
644
    /**
645
     * Sanitizes the URI component array by removing redundant key/value pairs
646
     *
647
     * @return void
648
     */
649 75
    private function sanitizeUriPartsArray()
650
    {
651 75
        $uri_part_array = new ArrayHelper($this->uri_parts);
652
653 75
        $this->uri_parts = $uri_part_array->removeNumericKeys();
654 75
    }
655
656
    /**
657
     * Percent encodes a component string except for sub-delims and unencoded pchar characters as defined by RFC 3986
658
     * in addition to any component-specific unencoded characters
659
     *
660
     * @param string $component_string          The string representing a URI component
661
     * @param string[] $component_unencoded     [OPTIONAL] Any additional unencoded characters specific to the component
662
     *
663
     * @return string                           The string with appropriate characters percent-encoded
664
     */
665 4
    private function encodeComponent($component_string, array $component_unencoded = array())
666
    {
667 4
        $uri_unencoded = array_merge($component_unencoded, $this->sub_delims, $this->pchar_unencoded);
668
669 4
        $string_helper = new StringHelper($component_string);
670
671 4
        $encoded_string = $string_helper->affectChunks("rawurlencode", ...$uri_unencoded);
672
673 4
        return $encoded_string;
674
    }
675
676
    /**
677
     * Determines whether a string contains unallowed URI characters, provided a string of allowed characters for a
678
     * given component.
679
     *
680
     * Note that a percent-encoded character (e.g. %20 for space) automatically counts as an allowed character, whereas
681
     * a percent sign not followed by two hex digits (e.g. %2X) does not count as an allowed character.
682
     *
683
     * @param string $string    The string to be checked for unallowed characters
684
     * @param string $allowed   A string containing all allowed characters for a given component
685
     *
686
     * @return bool             Returns true if the string contains unallowed characters
687
     *                          Returns false if the string contains only allowed characters (including percent-encoded
688
     *                          characters)
689
     */
690 8
    private function containsUnallowedUriCharacters($string, $allowed)
691
    {
692 8
        $allowed = preg_quote($allowed, "/");
693
694 8
        $pattern = "/^([0-9a-zA-Z\\.\\-_~{$allowed}]|%[0-9a-fA-F]{2})*\$/";
695
696 8
        $matches_allowed = preg_match($pattern, $string);
697
698 8
        return (bool) !$matches_allowed;
699
    }
700
701
    /**
702
     * Returns the appropriate authority string based upon __toString specification rules.
703
     *
704
     * @see Uri::__toString()
705
     *
706
     * @param string $authority     The authority to compile into a URI-friendly string
707
     *
708
     * @return string               The URI-friendly authority string
709
     */
710 31
    private function authorityToString($authority)
711
    {
712 31
        $authority_string = "";
713
714 31
        if (!empty($authority)) {
715 23
            $authority_string .= "//" . $authority;
716 23
        }
717
718 31
        return $authority_string;
719
    }
720
721
    /**
722
     * Returns the appropriate path string based upon __toString specification rules.
723
     *
724
     * @see Uri::__toString()
725
     *
726
     * @param string $path          The path to compile into a URI-friendly string
727
     * @param string $authority     [optional] The authority of the URI
728
     *
729
     * @return string               The URI-friendly path string
730
     */
731 31
    private function pathToString($path, $authority = "")
732
    {
733 31
        $path_string        = "";
734 31
        $path_string_helper = new StringHelper($path);
735
736 31
        if (empty($authority)) {
737 9
            $collapsed_slashes = $path_string_helper->collapseStartingRepetition("/");
738
739 9
            $path_string .= $collapsed_slashes;
740 31
        } elseif (!empty($path)) {
741 21
            if (!$path_string_helper->startsWith("/")) {
742 1
                $path_string .= "/" . $path;
743 1
            } else {
744 21
                $path_string .= $path;
745
            }
746 21
        }
747
748 31
        return $path_string;
749
    }
750
}
751