Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
92.44% |
110 / 119 |
|
97.37% |
37 / 38 |
CRAP | |
0.00% |
0 / 1 |
Uri | |
92.44% |
110 / 119 |
|
97.37% |
37 / 38 |
78.50 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
22 / 22 |
|
100.00% |
1 / 1 |
11 | |||
create | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getBasePath | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getScheme | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getHost | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getFullHost | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getUsername | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPassword | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPort | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getQuery | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getQueryAsArray | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getFragment | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getUri | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getFullUri | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSegment | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSegments | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasBasePath | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasScheme | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasHost | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasUsername | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasPassword | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasUri | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasPort | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasQuery | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasFragment | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasSegments | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasSegment | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setBasePath | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setScheme | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setHost | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setUsername | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setPassword | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setPort | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setQuery | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
setFragment | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setUri | |
70.97% |
22 / 31 |
|
0.00% |
0 / 1 |
27.83 | |||
render | |
100.00% |
15 / 15 |
|
100.00% |
1 / 1 |
8 | |||
__toString | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (http://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@nolainteractive.com> |
7 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
8 | * @license http://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Http; |
15 | |
16 | /** |
17 | * HTTP URI class |
18 | * |
19 | * @category Pop |
20 | * @package Pop\Http |
21 | * @author Nick Sagona, III <dev@nolainteractive.com> |
22 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
23 | * @license http://www.popphp.org/license New BSD License |
24 | * @version 5.2.0 |
25 | */ |
26 | class Uri |
27 | { |
28 | |
29 | /** |
30 | * Scheme |
31 | * @var ?string |
32 | */ |
33 | protected ?string $scheme = null; |
34 | |
35 | /** |
36 | * Host |
37 | * @var ?string |
38 | */ |
39 | protected ?string $host = null; |
40 | |
41 | /** |
42 | * Username |
43 | * @var ?string |
44 | */ |
45 | protected ?string $username = null; |
46 | |
47 | /** |
48 | * Password |
49 | * @var ?string |
50 | */ |
51 | protected ?string $password = null; |
52 | |
53 | /** |
54 | * URI |
55 | * @var ?string |
56 | */ |
57 | protected ?string $uri = null; |
58 | |
59 | /** |
60 | * Port |
61 | * @var string|int|null |
62 | */ |
63 | protected string|int|null $port = null; |
64 | |
65 | /** |
66 | * Query |
67 | * @var ?string |
68 | */ |
69 | protected ?string $query = null; |
70 | |
71 | /** |
72 | * Fragment |
73 | * @var ?string |
74 | */ |
75 | protected ?string $fragment = null; |
76 | |
77 | /** |
78 | * Base path |
79 | * @var ?string |
80 | */ |
81 | protected ?string $basePath = null; |
82 | |
83 | /** |
84 | * Path segments |
85 | * @var array |
86 | */ |
87 | protected array $segments = []; |
88 | |
89 | /** |
90 | * Constructor |
91 | * |
92 | * Instantiate the URI object |
93 | * |
94 | * @param ?string $uri |
95 | * @param ?string $basePath |
96 | * @throws Exception |
97 | */ |
98 | public function __construct(?string $uri = null, ?string $basePath = null) |
99 | { |
100 | $path = null; |
101 | if ($uri !== null) { |
102 | $uriInfo = parse_url($uri); |
103 | |
104 | if ($uriInfo === false) { |
105 | throw new Exception('Error: Unable to parse the URI value.'); |
106 | } |
107 | |
108 | if (!empty($uriInfo['scheme'])) { |
109 | $this->setScheme($uriInfo['scheme']); |
110 | } |
111 | if (!empty($uriInfo['host'])) { |
112 | $this->setHost($uriInfo['host']); |
113 | } |
114 | if (!empty($uriInfo['user'])) { |
115 | $this->setUsername($uriInfo['user']); |
116 | } |
117 | if (!empty($uriInfo['pass'])) { |
118 | $this->setPassword($uriInfo['pass']); |
119 | } |
120 | if (!empty($uriInfo['port'])) { |
121 | $this->setPort($uriInfo['port']); |
122 | } |
123 | if (!empty($uriInfo['query'])) { |
124 | $this->setQuery($uriInfo['query']); |
125 | } |
126 | if (!empty($uriInfo['fragment'])) { |
127 | $this->setFragment($uriInfo['fragment']); |
128 | } |
129 | if (!empty($uriInfo['path'])) { |
130 | $path = $uriInfo['path']; |
131 | } |
132 | } |
133 | |
134 | $this->setUri($path, $basePath); |
135 | } |
136 | |
137 | /** |
138 | * Create URI object |
139 | * |
140 | * @param ?string $uri |
141 | * @param ?string $basePath |
142 | * @throws Exception |
143 | * @return Uri |
144 | */ |
145 | public static function create(?string $uri = null, ?string $basePath = null): Uri |
146 | { |
147 | return new self($uri, $basePath); |
148 | } |
149 | |
150 | /** |
151 | * Get the base path |
152 | * |
153 | * @return string |
154 | */ |
155 | public function getBasePath(): string |
156 | { |
157 | return $this->basePath; |
158 | } |
159 | |
160 | /** |
161 | * Get the scheme |
162 | * |
163 | * @return string |
164 | */ |
165 | public function getScheme(): string |
166 | { |
167 | return $this->scheme; |
168 | } |
169 | |
170 | /** |
171 | * Get the host |
172 | * |
173 | * @return string |
174 | */ |
175 | public function getHost(): string |
176 | { |
177 | return $this->host; |
178 | } |
179 | |
180 | /** |
181 | * Get the host with the port |
182 | * |
183 | * @return string |
184 | */ |
185 | public function getFullHost(): string |
186 | { |
187 | $host = $this->host; |
188 | if ($this->hasPort()) { |
189 | $host .= ':' . $this->port; |
190 | } |
191 | |
192 | return $host; |
193 | } |
194 | |
195 | /** |
196 | * Get the username |
197 | * |
198 | * @return string |
199 | */ |
200 | public function getUsername(): string |
201 | { |
202 | return $this->username; |
203 | } |
204 | |
205 | /** |
206 | * Get the password |
207 | * |
208 | * @return string |
209 | */ |
210 | public function getPassword(): string |
211 | { |
212 | return $this->password; |
213 | } |
214 | |
215 | /** |
216 | * Get the port |
217 | * |
218 | * @return string|int|null |
219 | */ |
220 | public function getPort(): string|int|null |
221 | { |
222 | return $this->port; |
223 | } |
224 | |
225 | /** |
226 | * Get the query |
227 | * |
228 | * @return string |
229 | */ |
230 | public function getQuery(): string |
231 | { |
232 | return $this->query; |
233 | } |
234 | |
235 | /** |
236 | * Get the query |
237 | * |
238 | * @return array |
239 | */ |
240 | public function getQueryAsArray(): array |
241 | { |
242 | $result = []; |
243 | |
244 | if ($this->query !== null) { |
245 | parse_str($this->query, $result); |
246 | } |
247 | |
248 | return $result; |
249 | } |
250 | |
251 | /** |
252 | * Get the fragment |
253 | * |
254 | * @return string |
255 | */ |
256 | public function getFragment(): string |
257 | { |
258 | return $this->fragment; |
259 | } |
260 | |
261 | /** |
262 | * Get the URI |
263 | * |
264 | * @return string |
265 | */ |
266 | public function getUri(): string |
267 | { |
268 | return $this->uri; |
269 | } |
270 | |
271 | /** |
272 | * Get the full URI, including base path |
273 | * |
274 | * @return string |
275 | */ |
276 | public function getFullUri(): string |
277 | { |
278 | return $this->basePath . $this->uri; |
279 | } |
280 | |
281 | /** |
282 | * Get a path segment, divided by the forward slash, |
283 | * where $i refers to the array key index, i.e., |
284 | * 0 1 2 |
285 | * /hello/world/page |
286 | * |
287 | * @param int $i |
288 | * @return string|null |
289 | */ |
290 | public function getSegment(int $i): string|null |
291 | { |
292 | return $this->segments[(int)$i] ?? null; |
293 | } |
294 | |
295 | /** |
296 | * Get all path segments |
297 | * |
298 | * @return array |
299 | */ |
300 | public function getSegments(): array |
301 | { |
302 | return $this->segments; |
303 | } |
304 | |
305 | /** |
306 | * Has a base path |
307 | * |
308 | * @return bool |
309 | */ |
310 | public function hasBasePath(): bool |
311 | { |
312 | return ($this->basePath !== null); |
313 | } |
314 | |
315 | /** |
316 | * Has a scheme |
317 | * |
318 | * @return bool |
319 | */ |
320 | public function hasScheme(): bool |
321 | { |
322 | return ($this->scheme !== null); |
323 | } |
324 | |
325 | /** |
326 | * Has a host |
327 | * |
328 | * @return bool |
329 | */ |
330 | public function hasHost(): bool |
331 | { |
332 | return ($this->host !== null); |
333 | } |
334 | |
335 | /** |
336 | * Has a username |
337 | * |
338 | * @return bool |
339 | */ |
340 | public function hasUsername(): bool |
341 | { |
342 | return ($this->query !== null); |
343 | } |
344 | |
345 | /** |
346 | * Has a password |
347 | * |
348 | * @return bool |
349 | */ |
350 | public function hasPassword(): bool |
351 | { |
352 | return ($this->fragment !== null); |
353 | } |
354 | |
355 | /** |
356 | * Has a uri |
357 | * |
358 | * @return bool |
359 | */ |
360 | public function hasUri(): bool |
361 | { |
362 | return ($this->uri !== null); |
363 | } |
364 | |
365 | /** |
366 | * Has a port |
367 | * |
368 | * @return bool |
369 | */ |
370 | public function hasPort(): bool |
371 | { |
372 | return ($this->port !== null); |
373 | } |
374 | |
375 | /** |
376 | * Has a query |
377 | * |
378 | * @return bool |
379 | */ |
380 | public function hasQuery(): bool |
381 | { |
382 | return ($this->query !== null); |
383 | } |
384 | |
385 | /** |
386 | * Has a fragment |
387 | * |
388 | * @return bool |
389 | */ |
390 | public function hasFragment(): bool |
391 | { |
392 | return ($this->fragment !== null); |
393 | } |
394 | |
395 | /** |
396 | * Has segments |
397 | * |
398 | * @return bool |
399 | */ |
400 | public function hasSegments(): bool |
401 | { |
402 | return !empty($this->segments); |
403 | } |
404 | |
405 | /** |
406 | * Has segment |
407 | * |
408 | * @return bool |
409 | */ |
410 | public function hasSegment($i): bool |
411 | { |
412 | return isset($this->segments[$i]); |
413 | } |
414 | |
415 | /** |
416 | * Set the base path |
417 | * |
418 | * @param ?string $path |
419 | * @return Uri |
420 | */ |
421 | public function setBasePath(?string $path = null): Uri |
422 | { |
423 | $this->basePath = $path; |
424 | return $this; |
425 | } |
426 | |
427 | /** |
428 | * Set the scheme |
429 | * |
430 | * @param string $scheme |
431 | * @return Uri |
432 | */ |
433 | public function setScheme(string $scheme): Uri |
434 | { |
435 | $this->scheme = $scheme; |
436 | return $this; |
437 | } |
438 | |
439 | /** |
440 | * Set the host |
441 | * |
442 | * @param string $host |
443 | * @return Uri |
444 | */ |
445 | public function setHost(string $host): Uri |
446 | { |
447 | $this->host = $host; |
448 | return $this; |
449 | } |
450 | |
451 | /** |
452 | * Set the username |
453 | * |
454 | * @param string $username |
455 | * @return Uri |
456 | */ |
457 | public function setUsername(string $username): Uri |
458 | { |
459 | $this->username = $username; |
460 | return $this; |
461 | } |
462 | |
463 | /** |
464 | * Set the password |
465 | * |
466 | * @param string $password |
467 | * @return Uri |
468 | */ |
469 | public function setPassword(string $password): Uri |
470 | { |
471 | $this->password = $password; |
472 | return $this; |
473 | } |
474 | |
475 | /** |
476 | * Set the port |
477 | * |
478 | * @param string|int $port |
479 | * @return Uri |
480 | */ |
481 | public function setPort(string|int $port): Uri |
482 | { |
483 | $this->port = $port; |
484 | return $this; |
485 | } |
486 | |
487 | /** |
488 | * Set the query |
489 | * |
490 | * @param string|array $query |
491 | * @return Uri |
492 | */ |
493 | public function setQuery(string|array $query): Uri |
494 | { |
495 | if (is_array($query)) { |
496 | $query = http_build_query($query); |
497 | } |
498 | $this->query = $query; |
499 | return $this; |
500 | } |
501 | |
502 | /** |
503 | * Set the fragment |
504 | * |
505 | * @param string $fragment |
506 | * @return Uri |
507 | */ |
508 | public function setFragment(string $fragment): Uri |
509 | { |
510 | $this->fragment = $fragment; |
511 | return $this; |
512 | } |
513 | |
514 | /** |
515 | * Set the URI |
516 | * |
517 | * @param ?string $uri |
518 | * @param ?string $basePath |
519 | * @return Uri |
520 | */ |
521 | public function setUri(?string $uri = null, ?string $basePath = null): Uri |
522 | { |
523 | $isServerRequest = false; |
524 | if (($uri === null) && isset($_SERVER['REQUEST_URI'])) { |
525 | $uri = $_SERVER['REQUEST_URI']; |
526 | $isServerRequest = true; |
527 | } |
528 | |
529 | if (!empty($basePath)) { |
530 | if (substr($uri, 0, (strlen($basePath) + 1)) == $basePath . '/') { |
531 | $uri = substr($uri, (strpos($uri, $basePath) + strlen($basePath))); |
532 | } else if (substr($uri, 0, (strlen($basePath) + 1)) == $basePath . '?') { |
533 | $uri = '/' . substr($uri, (strpos($uri, $basePath) + strlen($basePath))); |
534 | } |
535 | } |
536 | |
537 | if (($uri == '') || ($uri == $basePath)) { |
538 | $uri = '/'; |
539 | } |
540 | |
541 | // Some slash clean up |
542 | $this->uri = $uri; |
543 | |
544 | if ($isServerRequest) { |
545 | $docRoot = (isset($_SERVER['DOCUMENT_ROOT'])) ? str_replace('\\', '/', $_SERVER['DOCUMENT_ROOT']) : null; |
546 | $dir = str_replace('\\', '/', getcwd()); |
547 | |
548 | if (($dir != $docRoot) && (strlen($dir) > strlen($docRoot))) { |
549 | $realBasePath = str_replace($docRoot, '', $dir); |
550 | if (str_starts_with($uri, $realBasePath)) { |
551 | $this->uri = substr($uri, strlen($realBasePath)); |
552 | } |
553 | } |
554 | |
555 | $this->setBasePath((($basePath === null) ? str_replace($docRoot, '', $dir) : $basePath)); |
556 | } else { |
557 | $this->setBasePath($basePath); |
558 | } |
559 | |
560 | // Get fragment |
561 | if (str_contains($this->uri, '#')) { |
562 | $this->fragment = substr($this->uri, (strpos($this->uri, '#') + 1)); |
563 | $this->uri = substr($this->uri, 0, strpos($this->uri, '#')); |
564 | } |
565 | |
566 | // Get query |
567 | if (str_contains($this->uri, '?')) { |
568 | $this->query = substr($this->uri, (strpos($this->uri, '?') + 1)); |
569 | $this->uri = substr($this->uri, 0, strpos($this->uri, '?')); |
570 | } |
571 | |
572 | // Get segments |
573 | if (($this->uri != '/') && (str_contains($this->uri, '/'))) { |
574 | $uri = (str_starts_with($this->uri, '/')) ? substr($this->uri, 1) : $this->uri; |
575 | $this->segments = explode('/', $uri); |
576 | } |
577 | |
578 | return $this; |
579 | } |
580 | |
581 | /** |
582 | * Render the URI |
583 | * |
584 | * @return string |
585 | */ |
586 | public function render(): string |
587 | { |
588 | $uri = ''; |
589 | |
590 | if ($this->hasScheme()) { |
591 | $uri .= $this->getScheme() . '://'; |
592 | } |
593 | if (($this->hasUsername()) && ($this->hasPassword())) { |
594 | $uri .= $this->getUsername() . ':' . $this->getPassword() . '@'; |
595 | } |
596 | if ($this->hasHost()) { |
597 | $uri .= $this->getHost(); |
598 | } |
599 | if ($this->hasPort()) { |
600 | $uri .= ':' . $this->getPort(); |
601 | } |
602 | |
603 | $uri .= $this->getFullUri(); |
604 | |
605 | if ($this->hasQuery()) { |
606 | $uri .= '?' . $this->getQuery(); |
607 | } |
608 | if ($this->hasFragment()) { |
609 | $uri .= '#' . $this->getFragment(); |
610 | } |
611 | |
612 | return $uri; |
613 | } |
614 | |
615 | /** |
616 | * Render the URI |
617 | * |
618 | * @return string |
619 | */ |
620 | public function __toString(): string |
621 | { |
622 | return $this->render(); |
623 | } |
624 | |
625 | } |