URI::setUserInfo()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 1
rs 10
1
<?php
2
3
/**
4
 * This file is part of the miBadger package.
5
 *
6
 * @author Michael Webbers <[email protected]>
7
 * @license http://opensource.org/licenses/Apache-2.0 Apache v2 License
8
 */
9
10
namespace miBadger\Http;
11
12
use Psr\Http\Message\UriInterface;
13
14
/**
15
 * The URI class.
16
 *
17
 * @see http://tools.ietf.org/html/rfc3986
18
 * @since 1.0.0
19
 */
20
class URI implements UriInterface
21
{
22
	const DELIMITER_SCHEME = ':';
23
	const DELIMITER_AUTHORITY = '//';
24
	const DELIMITER_USER = '@';
25
	const DELIMITER_PASSWORD = ':';
26
	const DELIMITER_PORT = ':';
27
	const DELIMITER_PATH = '/';
28
	const DELIMITER_QUERY = '?';
29
	const DELIMITER_QUERY_PAIR = '&';
30
	const DELIMITER_QUERY_KEY_VALUE = '=';
31
	const DELIMITER_FRAGMENT = '#';
32
33
	const SCHEME = 'scheme';
34
	const AUTHORITY = 'authority';
35
	const USERNAME = 'user';
36
	const PASSWORD = 'pass';
37
	const HOST = 'host';
38
	const PORT = 'port';
39
	const PATH = 'path';
40
	const DIRECTORY = 'directory';
41
	const FILE = 'file';
42
	const QUERY = 'query';
43
	const FRAGMENT = 'fragment';
44
45
	/** @var string The scheme. */
46
	private $scheme;
47
48
	/** @var string The username. */
49
	private $username;
50
51
	/** @var string|null The password. */
52
	private $password;
53
54
	/** @var string The host. */
55
	private $host;
56
57
	/** @var int|null The port. */
58
	private $port;
59
60
	/** @var string The directory. */
61
	private $directory;
62
63
	/** @var string The file. */
64
	private $file;
65
66
	/** @var array The query. */
67
	private $query;
68
69
	/** @var string The fragment. */
70
	private $fragment;
71
72
	/**
73
	 * Construct a URI object with the given URI.
74
	 *
75
	 * @param string $uri
76
	 * @throws \UnexpectedValueException
77
	 */
78 55
	public function __construct($uri)
79
	{
80 55
		$component = parse_url($uri);
81
82 55
		if ($component === false) {
83 1
			throw new \UnexpectedValueException('Invalid uri');
84
		}
85
86
		// Replace with the null coalescing in PHP7. E.g. $component[scheme] ?? ''
87
		$component += [
88 55
			static::SCHEME => '',
89 55
			static::USERNAME => '',
90 55
			static::PASSWORD => null,
91 55
			static::HOST => '',
92 55
			static::PORT => null,
93 55
			static::PATH => '',
94 55
			static::QUERY => '',
95 55
			static::FRAGMENT => ''
96
		];
97
98 55
		$this->setScheme($component[static::SCHEME]);
99 55
		$this->setUserInfo($component[static::USERNAME], $component[static::PASSWORD]);
100 55
		$this->setHost($component[static::HOST]);
101 55
		$this->setPort($component[static::PORT]);
102 55
		$this->setPath($component[static::PATH]);
103 55
		$this->setQuery($component[static::QUERY]);
104 55
		$this->setFragment($component[static::FRAGMENT]);
105 55
	}
106
107
	/**
108
	 * Returns a string representation of the URI object.
109
	 *
110
	 * @return string a string representation of the URI object.
111
	 */
112 3
	public function __toString()
113
	{
114 3
		return $this->getUri();
115
	}
116
117
	/**
118
	 * Returns the URI with the given start and stop component.
119
	 *
120
	 * @param string $start = self::SCHEME
121
	 * @param string $end = self::FRAGMENT
122
	 * @return string the URI.
123
	 */
124 6
	public function getUri($start = self::SCHEME, $end = self::FRAGMENT)
125
	{
126 6
		$result = '';
127
128
		switch ($start) {
129
			default:
130 6
			case static::SCHEME:
131 5
				$scheme = $this->getScheme();
132
133 5
				if ($scheme) {
134 4
					$result .= $scheme . static::DELIMITER_SCHEME;
135
				}
136
137 5
				if ($end === static::SCHEME) {
138 1
					break;
139
				}
140
141
				// no break
142
143 2
			case static::AUTHORITY:
144 2
			case static::USERNAME:
145 6
				$username = $this->getUserInfo();
146
147 6
				if ($username && $this->getHost()) {
148 5
					$result .= static::DELIMITER_AUTHORITY . $username . static::DELIMITER_USER;
149
				}
150
151 6
				if ($end === static::USERNAME) {
152 1
					break;
153
				}
154
155
				// no break
156
157 1
			case static::HOST:
158 6
				$host = $this->getHost();
159
160 6
				if ($host && ($result === '' || !$this->getUserInfo())) {
161 1
					$result .= static::DELIMITER_AUTHORITY;
162
				}
163
164 6
				$result .= $host;
165
166 6
				if ($end === static::HOST) {
167 1
					break;
168
				}
169
170
				// no break
171
172 1
			case static::PORT:
173 6
				$port = $this->getPort();
174
175 6
				if ($port !== null && $this->getHost()) {
176 5
					$result .= static::DELIMITER_PORT . $port;
177
				}
178
179 6
				if ($end === static::PORT || $end === static::AUTHORITY) {
180 2
					break;
181
				}
182
183
				// no break
184
185 1
			case static::PATH:
186 1
			case static::DIRECTORY:
187 5
				$directory = $this->getDirectory();
188
189 5
				if ($result !== '' && $directory !== '' && mb_substr($directory, 0, 1) !== static::DELIMITER_PATH) {
190 1
					$result .= static::DELIMITER_PATH;
191
				}
192
193 5
				$result .= $directory;
194
195 5
				if ($end === static::DIRECTORY) {
196 1
					break;
197
				}
198
199
				// no break
200
201 1
			case static::FILE:
202 5
				$file = $this->getFile();
203
204 5
				if ($result !== '' && mb_substr($result, -1) !== static::DELIMITER_PATH && $file !== '') {
205 1
					$result .= static::DELIMITER_PATH;
206
				}
207
208 5
				$result .= $this->getFile();
209
210 5
				if ($end === static::FILE || $end === static::PATH) {
211 1
					break;
212
				}
213
214
				// no break
215
216 1
			case static::QUERY:
217 5
				$query = $this->getQuery();
218
219 5
				if ($query) {
220 5
					$result .= static::DELIMITER_QUERY . $query;
221
				}
222
223 5
				if ($end === static::QUERY) {
224 1
					break;
225
				}
226
227
				// no break
228
229 1
			case static::FRAGMENT:
230 4
				$fragment = $this->getFragment();
231
232 4
				if ($fragment) {
233 3
					$result .= static::DELIMITER_FRAGMENT . $fragment;
234
				}
235
236
				// no break
237
		}
238
239 6
		return $result;
240
	}
241
242
	/**
243
	 * {@inheritdoc}
244
	 */
245 7
	public function getScheme()
246
	{
247 7
		return $this->scheme;
248
	}
249
250
	/**
251
	 * Set the scheme.
252
	 *
253
	 * @param string $scheme
254
	 * @return $this
255
	 */
256 55
	private function setScheme($scheme)
257
	{
258 55
		$this->scheme = strtolower($scheme);
259
260 55
		return $this;
261
	}
262
263
	/**
264
	 * {@inheritdoc}
265
	 */
266 1
	public function withScheme($scheme)
267
	{
268 1
		$result = clone $this;
269
270 1
		return $result->setScheme($scheme);
271
	}
272
273
	/**
274
	 * {@inheritdoc}
275
	 */
276 1
	public function getAuthority()
277
	{
278 1
		if (!$this->getHost()) {
279 1
			return '';
280
		}
281
282 1
		return mb_substr($this->getUri(self::USERNAME, self::PORT), 2);
283
	}
284
285
	/**
286
	 * {@inheritdoc}
287
	 */
288 8
	public function getUserInfo()
289
	{
290 8
		$result = $this->username;
291
292 8
		if ($this->password !== null) {
293 7
			$result .= static::DELIMITER_PASSWORD . $this->password;
294
		}
295
296 8
		return $result;
297
	}
298
299
	/**
300
	 * Set the user info.
301
	 *
302
	 * @param string $username
303
	 * @param string|null $password = null
304
	 * @return $this
305
	 */
306 55
	private function setUserInfo($username, $password = null)
307
	{
308 55
		$this->username = $username;
309 55
		$this->password = $password;
310
311 55
		return $this;
312
	}
313
314
	/**
315
	 * {@inheritdoc}
316
	 */
317 1
	public function withUserInfo($username, $password = null)
318
	{
319 1
		$result = clone $this;
320
321 1
		return $result->setUserInfo($username, $password);
322
	}
323
324
	/**
325
	 * {@inheritdoc}
326
	 */
327 33
	public function getHost()
328
	{
329 33
		return $this->host;
330
	}
331
332
	/**
333
	 * Set the host.
334
	 *
335
	 * @param string $host
336
	 * @return $this
337
	 */
338 55
	private function setHost($host)
339
	{
340 55
		$this->host = strtolower($host);
341
342 55
		return $this;
343
	}
344
345
	/**
346
	 * {@inheritdoc}
347
	 */
348 2
	public function withHost($host)
349
	{
350 2
		$result = clone $this;
351
352 2
		return $result->setHost($host);
353
	}
354
355
	/**
356
	 * {@inheritdoc}
357
	 */
358 9
	public function getPort()
359
	{
360 9
		return $this->port;
361
	}
362
363
	/**
364
	 * Set the port.
365
	 *
366
	 * @param int|null $port
367
	 * @return $this
368
	 * @throws \InvalidArgumentException
369
	 */
370 55
	private function setPort($port = null)
371
	{
372 55
		if ($port !== null && (1 > $port || 0xffff < $port)) {
373 1
			throw new \InvalidArgumentException('Invalid port');
374
		}
375
376 55
		$this->port = $port;
377
378 55
		return $this;
379
	}
380
381
	/**
382
	 * {@inheritdoc}
383
	 */
384 2
	public function withPort($port)
385
	{
386 2
		$result = clone $this;
387
388 2
		return $result->setPort($port);
389
	}
390
391
	/**
392
	 * {@inheritdoc}
393
	 */
394 5
	public function getPath()
395
	{
396 5
		$result = $this->getDirectory();
397
398 5
		if ($result !== '' && mb_substr($result, -1) !== static::DELIMITER_PATH && $this->getFile()) {
399 1
			$result .= static::DELIMITER_PATH;
400
		}
401
402 5
		return $result . $this->getFile();
403
	}
404
405
	/**
406
	 * Set the path.
407
	 *
408
	 * @param string $path
409
	 * @return $this
410
	 */
411 55
	private function setPath($path)
412
	{
413 55
		$directory = dirname($path);
414 55
		$file = basename($path);
415
416
		// If dirname is '.'. Then remove it.
417 55
		if ($directory === '.') {
418 1
			$directory = '';
419
		}
420
421
		// If the path ends with '/'. Then there is no file.
422 55
		if (mb_substr($path, -1) === static::DELIMITER_PATH) {
423 23
			$directory = $path;
424 23
			$file = '';
425
		}
426
427
		// If the dirname and basename are both set. Then add the missing '/'.
428 55
		if (mb_substr($directory, -1) !== static::DELIMITER_PATH && $directory !== '' && $file !== '') {
429 35
			$directory .= static::DELIMITER_PATH;
430
		}
431
432 55
		$this->setDirectory($directory);
433 55
		$this->setFile($file);
434
435 55
		return $this;
436
	}
437
438
	/**
439
	 * {@inheritdoc}
440
	 */
441 3
	public function withPath($path)
442
	{
443 3
		$result = clone $this;
444
445 3
		return $result->setPath($path);
446
	}
447
448
	/**
449
	 * Returns the URI segements
450
	 *
451
	 * @return string[] the URI segments
452
	 */
453 2
	public function getSegments()
454
	{
455
		// array_values reindexes the array and array_diff removes the empty elements.
456 2
		return array_values(array_diff(explode(static::DELIMITER_PATH, $this->getPath()), ['']));
457
	}
458
459
	/**
460
	 * Returns the segment at the given index or null if the segment at the given index doesn't exists.
461
	 *
462
	 * @param int $index
463
	 * @return string|null the segment at the given index or null if the segment at the given index doesn't exists
464
	 */
465 1
	public function getSegment($index)
466
	{
467 1
		$result = $this->getSegments();
468
469 1
		return isset($result[$index]) ? $result[$index] : null;
470
	}
471
472
	/**
473
	 * Returns the directory.
474
	 *
475
	 * @return string the directory.
476
	 */
477 11
	public function getDirectory()
478
	{
479 11
		return $this->directory;
480
	}
481
482
	/**
483
	 * Set the directory.
484
	 *
485
	 * @param string $directory
486
	 * @return $this
487
	 */
488 55
	private function setDirectory($directory)
489
	{
490 55
		$this->directory = $directory;
491
492 55
		return $this;
493
	}
494
495
	/**
496
	 * Return an instance with the specified directory.
497
	 *
498
	 * @param string $directory
499
	 * @return self
500
	 */
501 3
	public function withDirectory($directory)
502
	{
503 3
		$result = clone $this;
504
505 3
		return $result->setDirectory($directory);
506
	}
507
508
	/**
509
	 * Returns the file.
510
	 *
511
	 * @return string the file.
512
	 */
513 11
	public function getFile()
514
	{
515 11
		return $this->file;
516
	}
517
518
	/**
519
	 * Set the file.
520
	 *
521
	 * @param string $file
522
	 * @return $this
523
	 */
524 55
	private function setFile($file)
525
	{
526 55
		$this->file = $file;
527
528 55
		return $this;
529
	}
530
531
	/**
532
	 * Return an instance with the specified file.
533
	 *
534
	 * @param string $file
535
	 * @return self
536
	 */
537 1
	public function withFile($file)
538
	{
539 1
		$result = clone $this;
540
541 1
		return $result->setFile($file);
542
	}
543
544
	/**
545
	 * {@inheritdoc}
546
	 */
547 7
	public function getQuery()
548
	{
549 7
		return http_build_query($this->query);
550
	}
551
552
	/**
553
	 * Set the query.
554
	 *
555
	 * @param string $query
556
	 * @return $this
557
	 */
558 55
	private function setQuery($query)
559
	{
560 55
		$this->query = [];
561
562 55
		parse_str($query, $this->query);
563
564 55
		return $this;
565
	}
566
567
	/**
568
	 * {@inheritdoc}
569
	 */
570 1
	public function withQuery($query)
571
	{
572 1
		$result = clone $this;
573
574 1
		return $result->setQuery($query);
575
	}
576
577
	/**
578
	 * Returns the value to which the specified key is mapped, or null if the query map contains no mapping for the key.
579
	 *
580
	 * @param string $key
581
	 * @return string the value to which the specified key is mapped, or null if the query map contains no mapping for the key.
582
	 */
583 2
	public function getQueryValue($key)
584
	{
585 2
		return isset($this->query[$key]) ? $this->query[$key] : null;
586
	}
587
588
	/**
589
	 * Associates the specified value with the specified key in the query map.
590
	 *
591
	 * @param string $key
592
	 * @param string $value
593
	 * @return $this
594
	 */
595 1
	private function setQueryValue($key, $value)
596
	{
597 1
		$this->query[$key] = $value;
598
599 1
		return $this;
600
	}
601
602
	/**
603
	 * Return an instance with the specified query value.
604
	 *
605
	 * @param string $key
606
	 * @param string $value
607
	 * @return self
608
	 */
609 1
	public function withQueryValue($key, $value)
610
	{
611 1
		$result = clone $this;
612
613 1
		return $result->setQueryValue($key, $value);
614
	}
615
616
	/**
617
	 * {@inheritdoc}
618
	 */
619 6
	public function getFragment()
620
	{
621 6
		return $this->fragment;
622
	}
623
624
	/**
625
	 * Set the fragment.
626
	 *
627
	 * @param string $fragment
628
	 * @return $this
629
	 */
630 55
	private function setFragment($fragment)
631
	{
632 55
		$this->fragment = $fragment;
633
634 55
		return $this;
635
	}
636
637
	/**
638
	 * {@inheritdoc}
639
	 */
640 1
	public function withFragment($fragment)
641
	{
642 1
		$result = clone $this;
643
644 1
		return $result->setFragment($fragment);
645
	}
646
647
	/**
648
	 * Returns an instance with the decoded URI.
649
	 *
650
	 * @return self
651
	 */
652 1
	public function decode()
653
	{
654 1
		return new URI(html_entity_decode($this));
655
	}
656
657
	/**
658
	 * Returns an instance with the encoded URI.
659
	 *
660
	 * @return self
661
	 */
662 1
	public function encode()
663
	{
664 1
		return new URI(htmlentities($this));
665
	}
666
}
667