URI::getSegment()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 3
rs 10
1
<?php    
2
3
/**
4
 * Lenevor Framework
5
 *
6
 * LICENSE
7
 *
8
 * This source file is subject to the new BSD license that is bundled
9
 * with this package in the file license.md.
10
 * It is also available through the world-wide-web at this URL:
11
 * https://lenevor.com/license
12
 * If you did not receive a copy of the license and are unable to
13
 * obtain it through the world-wide-web, please send an email
14
 * to [email protected] so we can send you a copy immediately.
15
 *
16
 * @package     Lenevor
17
 * @subpackage  Base
18
 * @link        https://lenevor.com
19
 * @copyright   Copyright (c) 2019 - 2021 Alexander Campo <[email protected]>
20
 * @license     https://opensource.org/licenses/BSD-3-Clause New BSD license or see https://lenevor.com/license or see /license.md
21
 */
22
23
namespace Syscodes\Http;
24
25
use InvalidArgumentException;
26
use Syscodes\Collections\Arr;
27
use Syscodes\Http\Contributors\Parameters;
28
use Syscodes\Http\Exceptions\HttpURIException;
29
30
/**
31
 * Abstraction for a uniform resource identifier (URI).
32
 * 
33
 * @author Alexander Campo <[email protected]>
34
 */
35
class URI
36
{
37
	/**
38
	 * Returns default schemes/ports.
39
	 * 
40
	 * @var array $defaultPorts
41
	 */
42
	protected $defaultPorts = [
43
		'http'  => 80,
44
		'https' => 443,
45
		'ftp'   => 21,
46
		'sftp'  => 22
47
	];
48
49
	/**
50
	 * The name of any fragment.
51
	 * 
52
	 * @var string $fragment
53
	 */
54
	protected $fragment = '';
55
56
	/**
57
	 * The URI Host.
58
	 * 
59
	 * @var string $host
60
	 */
61
	protected $host;
62
	
63
	/**
64
	 * The URI User Password.
65
	 * 
66
	 * @var string $password
67
	 */
68
	protected $password;
69
70
	/**
71
	 * The URI path.
72
	 * 
73
	 * @var string $path
74
	 */
75
	protected $path;
76
77
	/**
78
	 * The URI Port.
79
	 * 
80
	 * @var int $port
81
	 */
82
	protected $port;
83
84
	/**
85
	 * The query string.
86
	 * 
87
	 * @var string $query
88
	 */
89
	protected $query;
90
91
	/**
92
	 * The URI Scheme.
93
	 * 
94
	 * @var string $scheme
95
	 */
96
	protected $scheme = 'http';
97
98
	/**
99
	 * The URI segments.
100
	 *
101
	 * @var array $segments
102
	 */
103
	protected $segments = [];
104
105
	/**
106
	 * Whether passwords should be shown in userInfo/authority calls.
107
	 * 
108
	 * @var boolean $showPassword
109
	 */
110
	protected $showPassword = false;
111
	
112
	/**
113
	 * The URI User Info.
114
	 * 
115
	 * @var string $user
116
	 */
117
	protected $user;
118
119
	/**
120
	 * Constructor. The URI class instance.
121
	 * 
122
	 * @param  string|null  $uri  
123
	 * 
124
	 * @return void
125
	 * 
126
	 * @throws \Syscodes\Http\Exceptions\HttpURIException
127
	 */
128
	public function __construct(string $uri = null)
129
	{
130
		if ( ! is_null($uri)) {
131
			$this->setUri($uri);
132
		}
133
	}
134
135
	/**
136
	 * Sets and overwrites any current URI information.
137
	 * 
138
	 * @param  string|null  $uri  
139
	 * 
140
	 * @return mixed
141
	 * 
142
	 * @throws \Syscodes\Http\Exceptions\HttpURIException
143
	 */
144
	public function setUri(string $uri = null)
145
	{
146
		if ( ! is_null($uri)) {
147
			$parts = parse_url($uri);
148
149
			if ($parts === false) {
150
				throw HttpURIException::UnableToParseURI($uri);
151
			}
152
153
			$this->applyParts($parts);
154
		}
155
156
		return $this;
157
	}
158
159
	/**
160
	 * Returns the full URI string.
161
	 *
162
	 * @return string  The URI string
163
	 */
164
	public function get()
165
	{
166
		return '/'.ltrim($this->path, '/');
167
	}
168
169
	/**
170
	 * Sets of URI string.
171
	 * 
172
	 * @param  string  $uri
173
	 * 
174
	 * @return $this
175
	 */
176
	public function set($uri)
177
	{
178
		$this->path = $uri;
179
180
		return $this;
181
	}
182
183
	/**
184
	 * Retrieve the path component of the URI. The path can either be empty or absolute 
185
	 * (starting with a slash) or rootless (not starting with a slash).
186
	 * 
187
	 * @return string
188
	 */
189
	public function getPath()
190
	{
191
		return (is_null($this->path) ? '' : $this->path);
0 ignored issues
show
introduced by
The condition is_null($this->path) is always false.
Loading history...
192
	}
193
194
	/**
195
	 * Sets the path portion of the URI.
196
	 * 
197
	 * @param  string  $path
198
	 *
199
	 * @return void
200
	 */
201
	public function setPath(string $path) 
202
	{
203
		$this->path = $this->filterPath($path);
204
205
		$this->filterSegments($this->path);
206
207
		return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Syscodes\Http\URI which is incompatible with the documented return type void.
Loading history...
208
	} 
209
210
	/**
211
	 * Encodes any dangerous characters.
212
	 * 
213
	 * @param  string|null  $path
214
	 * 
215
	 * @return string
216
	 */
217
	protected function filterPath(string $path = null)
218
	{
219
		$path = urldecode($path);
0 ignored issues
show
Bug introduced by
It seems like $path can also be of type null; however, parameter $string of urldecode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

219
		$path = urldecode(/** @scrutinizer ignore-type */ $path);
Loading history...
220
221
		return $path;
222
	}
223
224
	/**
225
	 * Filter the segments of path.
226
	 * 
227
	 * @param  string  $uri
228
	 * 
229
	 * @return string[]
230
	 */
231
	protected function filterSegments($uri)
232
	{
233
		$this->segments = (empty($uri) ? [] : explode('/', $uri));
234
	}
235
236
	/**
237
	 * Get the specified URI segment, return default if it doesn't exist.
238
	 * Segment index is 1 based, not 0 based.
239
	 *
240
	 * @param  int  $index  The 1-based segment index
241
	 * @param  mixed  $default  The default value
242
	 *
243
	 * @return mixed
244
	 */
245
	public function getSegment(int $index, $default = null)
246
	{
247
		return Arr::get($this->getSegments(), $index - 1, $default);
248
	}
249
250
	/**
251
	 * Returns the segments of the path as an array.
252
	 *
253
	 * @return array  The URI segments
254
	 */
255
	public function getSegments()
256
	{
257
		return array_values(array_filter($this->segments, function ($value) {
258
			return $value != '';
259
		}));
260
	}
261
262
	/**
263
	 * Returns the total number of segment.
264
	 *
265
	 * @return int  
266
	 */
267
	public function getTotalSegments()
268
	{
269
		return count($this->getSegments());
270
	}
271
272
	/**
273
	 * Retrieve the scheme component of the URI.
274
	 * 
275
	 * @return string
276
	 */
277
	public function getScheme()
278
	{
279
		return $this->scheme;
280
	}
281
282
	/**
283
	 * Sets the scheme for this URI.
284
	 * 
285
	 * @param  string  $str
286
	 * 
287
	 * @return $this
288
	 */
289
	public function setScheme(string $str)
290
	{
291
		$str = preg_replace('~:(//)?$~', '', strtolower($str));
292
293
		$this->scheme = $str;
294
295
		return $this->scheme;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->scheme returns the type string which is incompatible with the documented return type Syscodes\Http\URI.
Loading history...
296
	}
297
298
	/**
299
	 * Retrieve the user component of the URI.
300
	 * 
301
	 * @return string|null
302
	 */
303
	public function getUserInfo()
304
	{
305
		$user = $this->user;
306
307
		if ($this->showPassword === true && ! empty($this->password)) {
308
			$user .= ":$this->password";
309
		}
310
311
		return $user;
312
	}
313
314
	/**
315
	 * Sets the userInfo/Authority portion of the URI.
316
	 * 
317
	 * @param  string  $user
318
	 * @param  string  $pass
319
	 * 
320
	 * @return $this
321
	 */
322
	public function setUserInfo(string $user, string $pass)
323
	{
324
		$this->user     = trim($user);
325
		$this->password = trim($pass);
326
327
		return $this;
328
	}
329
330
	/**
331
	 * Temporarily sets the URI to show a password in userInfo.
332
	 * 
333
	 * @param  boolean  $option  
334
	 * 
335
	 * @return $this
336
	 */
337
	public function showPassword(bool $option = true)
338
	{
339
		$this->password = $option;
0 ignored issues
show
Documentation Bug introduced by
The property $password was declared of type string, but $option is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
340
341
		return $this->password;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->password returns the type boolean which is incompatible with the documented return type Syscodes\Http\URI.
Loading history...
342
	}
343
344
	/**
345
	 * Retrieve the authority component of the URI.
346
	 * 
347
	 * @param  boolean  $ignore  
348
	 * 
349
	 * @return string
350
	 */
351
	public function getAuthority(bool $ignore = false)
352
	{
353
		if (empty($this->host)) {
354
			return '';
355
		}
356
357
		$authority = $this->host;
358
359
		if ( ! empty($this->getUserInfo())) {
360
			$authority = $this->getUserInfo().'@'.$authority;
361
		}
362
363
		if ( ! empty($this->port) && ! $ignore) {
364
			if ($this->port !== $this->defaultPorts[$this->scheme]) {
365
				$authority .= ":$this->port";
366
			}
367
		}
368
369
		$this->showPassword = false;
370
371
		return $authority;
372
	}
373
374
	/**
375
	 * Parses the given string an saves the appropriate authority pieces.
376
	 * 
377
	 * @param  string  $str
378
	 * 
379
	 * @return $this
380
	 */
381
	public function setAuthority(string $str)
382
	{
383
		$parts = parse_url($str);
384
385
		if (empty($parts['host']) && ! empty($parts['path'])) {
386
			$parts['host'] = $parts['path'];
387
			unset($parts['path']);
388
		}
389
390
		$this->applyParts($parts);
391
392
		return $this;
393
	}
394
395
	/**
396
	 * Retrieve the host component of the URI.
397
	 * 
398
	 * @return string
399
	 */
400
	public function getHost()
401
	{
402
		return $this->host;
403
	}
404
405
	/**
406
	 * Sets the host name to use.
407
	 * 
408
	 * @param  string  $str
409
	 * 
410
	 * @return $this
411
	 */
412
	public function setHost(string $str)
413
	{
414
		$this->host = trim($str);
415
416
		return $this->host;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->host returns the type string which is incompatible with the documented return type Syscodes\Http\URI.
Loading history...
417
	}
418
419
	/**
420
	 * Retrieve the port component of the URI.
421
	 * 
422
	 * @return int|null
423
	 */
424
	public function getPort()
425
	{
426
		return $this->port;
427
	}
428
429
	/**
430
	 * Sets the port portion of the URI.
431
	 * 
432
	 * @param  int|null  $port  
433
	 * 
434
	 * @return string
435
	 */
436
	public function setPort(int $port = null)
437
	{
438
		if (is_null($port)) {
439
			return $this;
440
		}
441
442
		if ($port <= 0 || $port > 65355) {
443
			throw HttpURIException::invalidPort($port);
444
		}
445
446
		$this->port = $port;
447
448
		return $this->port;
449
	}
450
451
	/**
452
	 * Retrieve a URI fragment.
453
	 * 
454
	 * @return string
455
	 */
456
	public function getFragment()
457
	{
458
		return is_null($this->fragment) ? '' : $this->fragment;
0 ignored issues
show
introduced by
The condition is_null($this->fragment) is always false.
Loading history...
459
	}
460
461
	/**
462
	 * Sets the fragment portion of the URI.
463
	 * 
464
	 * @param  string  $str
465
	 * 
466
	 * @return $this
467
	 */
468
	public function setFragment(string $str)
469
	{
470
		$this->fragment = trim($str, '# ');
471
472
		return $this->fragment;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->fragment returns the type string which is incompatible with the documented return type Syscodes\Http\URI.
Loading history...
473
	}
474
475
	/**
476
	 * Saves our parts from a parse_url call.
477
	 * 
478
	 * @param  array  $parts
479
	 * 
480
	 * @return mixed
481
	 */
482
	public function applyParts(array $paths)
0 ignored issues
show
Unused Code introduced by
The parameter $paths is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

482
	public function applyParts(/** @scrutinizer ignore-unused */ array $paths)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
483
	{
484
		if (isset($parts['scheme'])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $parts seems to never exist and therefore isset should always be false.
Loading history...
485
			$this->SetScheme(rtrim($parts['scheme'], ':/'));
486
		} else {
487
			$this->setScheme('http');
488
		}
489
490
		if ( ! empty($parts['host'])) {
491
			$this->host = $parts['host'];
492
		}
493
494
		if (isset($parts['port'])) {
495
			if ( ! is_null($parts['port'])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $parts seems to be never defined.
Loading history...
496
				$this->port = $parts['port'];
497
			}
498
		}
499
500
		if ( ! empty($parts['user'])) {
501
			$this->user = $parts['user'];
502
		}
503
504
		if ( ! empty($parts['pass'])) {
505
			$this->password = $parts['pass'];
506
		}
507
508
		if ( ! empty($parts['path'])) {
509
			$this->path = $this->filterPath($parts['path']);
510
		}
511
512
		if ( ! empty($parts['fragment'])) {
513
			$this->fragment = $parts['fragment'];
514
		}
515
516
		if ( ! empty($parts['path'])) {
517
			$this->segments = explode('/', $parts['path'], '/');
0 ignored issues
show
Bug introduced by
'/' of type string is incompatible with the type integer expected by parameter $limit of explode(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

517
			$this->segments = explode('/', $parts['path'], /** @scrutinizer ignore-type */ '/');
Loading history...
518
		}
519
	}
520
521
	/**
522
	 * Returns the URI string.
523
	 *
524
	 * @return string
525
	 */
526
	public function __toString()
527
	{
528
		return (string) $this->getPath();
529
	}
530
}
531