Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
92.00% |
69 / 75 |
|
90.48% |
19 / 21 |
CRAP | |
0.00% |
0 / 1 |
HttpUri | |
92.00% |
69 / 75 |
|
90.48% |
19 / 21 |
43.95 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
set | |
100.00% |
35 / 35 |
|
100.00% |
1 / 1 |
6 | |||
getCurrent | n/a |
0 / 0 |
n/a |
0 / 0 |
5 | |||||
fromCurrent | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
isValid | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRootPath | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setRootPath | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
setPathOffset | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getSubdomain | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
getPath | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setPath | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
getPathOffset | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRoute | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
getQuery | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
setQuery | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getPathElement | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
getPathKey | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
getPathElements | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getQueryArray | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getBase | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__toString | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getAuthority | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
3 | |||
getUserInfo | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Jingga |
4 | * |
5 | * PHP Version 8.1 |
6 | * |
7 | * @package phpOMS\Uri |
8 | * @copyright Dennis Eichhorn |
9 | * @license OMS License 2.0 |
10 | * @version 1.0.0 |
11 | * @link https://jingga.app |
12 | */ |
13 | declare(strict_types=1); |
14 | |
15 | namespace phpOMS\Uri; |
16 | |
17 | use phpOMS\Utils\StringUtils; |
18 | |
19 | /** |
20 | * HTTP Uri. |
21 | * |
22 | * Uri used for http requests (incoming & outgoing) |
23 | * |
24 | * @package phpOMS\Uri |
25 | * @license OMS License 2.0 |
26 | * @link https://jingga.app |
27 | * @since 1.0.0 |
28 | * |
29 | * @SuppressWarnings(PHPMD.Superglobals) |
30 | */ |
31 | final class HttpUri implements UriInterface |
32 | { |
33 | /** |
34 | * Root path. |
35 | * |
36 | * @var string |
37 | * @since 1.0.0 |
38 | */ |
39 | private string $rootPath = ''; |
40 | |
41 | /** |
42 | * Path offset. |
43 | * |
44 | * @var int |
45 | * @since 1.0.0 |
46 | */ |
47 | private int $pathOffset = 0; |
48 | |
49 | /** |
50 | * Path elements. |
51 | * |
52 | * @var string[] |
53 | * @since 1.0.0 |
54 | */ |
55 | private array $pathElements; |
56 | |
57 | /** |
58 | * Uri. |
59 | * |
60 | * @var string |
61 | * @since 1.0.0 |
62 | */ |
63 | public string $uri; |
64 | |
65 | /** |
66 | * Uri scheme. |
67 | * |
68 | * @var string |
69 | * @since 1.0.0 |
70 | */ |
71 | public string $scheme; |
72 | |
73 | /** |
74 | * Uri host. |
75 | * |
76 | * @var string |
77 | * @since 1.0.0 |
78 | */ |
79 | public string $host; |
80 | |
81 | /** |
82 | * Uri port. |
83 | * |
84 | * @var int |
85 | * @since 1.0.0 |
86 | */ |
87 | public int $port; |
88 | |
89 | /** |
90 | * Uri user. |
91 | * |
92 | * @var string |
93 | * @since 1.0.0 |
94 | */ |
95 | public string $user; |
96 | |
97 | /** |
98 | * Uri password. |
99 | * |
100 | * @var string |
101 | * @since 1.0.0 |
102 | */ |
103 | public string $pass; |
104 | |
105 | /** |
106 | * Uri path. |
107 | * |
108 | * @var string |
109 | * @since 1.0.0 |
110 | */ |
111 | public string $path; |
112 | |
113 | /** |
114 | * Uri path with offset. |
115 | * |
116 | * @var string |
117 | * @since 1.0.0 |
118 | */ |
119 | private string $offsetPath = ''; |
120 | |
121 | /** |
122 | * Uri query. |
123 | * |
124 | * @var array<string, string> |
125 | * @since 1.0.0 |
126 | */ |
127 | private array $query = []; |
128 | |
129 | /** |
130 | * Uri query. |
131 | * |
132 | * @var string |
133 | * @since 1.0.0 |
134 | */ |
135 | private string $queryString; |
136 | |
137 | /** |
138 | * Uri fragment. |
139 | * |
140 | * @var string |
141 | * @since 1.0.0 |
142 | */ |
143 | public string $fragment; |
144 | |
145 | /** |
146 | * Uri fragments. |
147 | * |
148 | * @var array |
149 | * @since 1.0.0 |
150 | */ |
151 | public array $fragments = []; |
152 | |
153 | /** |
154 | * Uri base. |
155 | * |
156 | * @var string |
157 | * @since 1.0.0 |
158 | */ |
159 | private string $base; |
160 | |
161 | /** |
162 | * Constructor. |
163 | * |
164 | * @param string $uri Root path for subdirectory |
165 | * |
166 | * @since 1.0.0 |
167 | */ |
168 | public function __construct(string $uri) |
169 | { |
170 | $this->set($uri); |
171 | } |
172 | |
173 | /** |
174 | * {@inheritdoc} |
175 | * |
176 | * @throws \Exception |
177 | */ |
178 | public function set(string $uri) : void |
179 | { |
180 | $this->uri = $uri; |
181 | $url = \parse_url($this->uri); |
182 | |
183 | if ($url === false) { |
184 | $this->scheme = ''; |
185 | $this->host = ''; |
186 | $this->port = 80; |
187 | $this->user = ''; |
188 | $this->pass = ''; |
189 | $this->path = ''; |
190 | $this->queryString = ''; |
191 | $this->query = []; |
192 | $this->pathElements = []; |
193 | $this->fragment = ''; |
194 | $this->base = ''; |
195 | |
196 | return; |
197 | } |
198 | |
199 | $this->scheme = $url['scheme'] ?? ''; |
200 | $this->host = $url['host'] ?? ''; |
201 | $this->port = $url['port'] ?? 80; |
202 | $this->user = $url['user'] ?? ''; |
203 | $this->pass = $url['pass'] ?? ''; |
204 | $this->path = $url['path'] ?? ''; |
205 | |
206 | if (StringUtils::endsWith($this->path, '.php')) { |
207 | $path = \substr($this->path, 0, -4); |
208 | |
209 | if ($path === false) { |
210 | throw new \Exception(); // @codeCoverageIgnore |
211 | } |
212 | |
213 | $this->path = $path; |
214 | } |
215 | |
216 | $this->pathElements = \explode('/', \trim($this->path, '/')); |
217 | |
218 | $path = \array_slice($this->pathElements, $this->pathOffset); |
219 | $this->offsetPath = '/' . \implode('/', $path); |
220 | |
221 | $this->queryString = $url['query'] ?? ''; |
222 | |
223 | if (!empty($this->queryString)) { |
224 | \parse_str($this->queryString, $this->query); |
225 | } |
226 | |
227 | $this->query = \array_change_key_case($this->query, \CASE_LOWER); |
228 | |
229 | $this->fragment = $url['fragment'] ?? ''; |
230 | $this->fragments = \explode('&', $url['fragment'] ?? ''); |
231 | $this->base = $this->scheme . '://' . $this->host . ($this->port !== 80 ? ':' . $this->port : '') . $this->rootPath; |
232 | } |
233 | |
234 | /** |
235 | * Get current uri. |
236 | * |
237 | * @return string Returns the current uri |
238 | * |
239 | * @since 1.0.0 |
240 | * @codeCoverageIgnore |
241 | */ |
242 | public static function getCurrent() : string |
243 | { |
244 | /** @noinspection PhpUndefinedConstantInspection */ |
245 | return ((!empty($_SERVER['HTTPS'] ?? '') && ($_SERVER['HTTPS'] ?? '') !== 'off') |
246 | || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https') |
247 | || (($_SERVER['HTTP_X_FORWARDED_SSL'] ?? '') === 'on') ? 'https' : 'http') |
248 | . '://' . ($_SERVER['HTTP_HOST'] ?? ''). ($_SERVER['REQUEST_URI'] ?? ''); |
249 | } |
250 | |
251 | /** |
252 | * Create uri from current url |
253 | * |
254 | * @return HttpUri Returns the current uri |
255 | * |
256 | * @since 1.0.0 |
257 | * @codeCoverageIgnore |
258 | */ |
259 | public static function fromCurrent() : self |
260 | { |
261 | return new self(self::getCurrent()); |
262 | } |
263 | |
264 | /** |
265 | * {@inheritdoc} |
266 | */ |
267 | public static function isValid(string $uri) : bool |
268 | { |
269 | return (bool) \filter_var($uri, \FILTER_VALIDATE_URL); |
270 | } |
271 | |
272 | /** |
273 | * {@inheritdoc} |
274 | */ |
275 | public function getRootPath() : string |
276 | { |
277 | return $this->rootPath; |
278 | } |
279 | |
280 | /** |
281 | * {@inheritdoc} |
282 | */ |
283 | public function setRootPath(string $root) : void |
284 | { |
285 | $this->rootPath = \rtrim($root, '/'); |
286 | $this->base = $this->scheme . '://' . $this->host . ($this->port !== 80 ? ':' . $this->port : '') . $this->rootPath; |
287 | } |
288 | |
289 | /** |
290 | * {@inheritdoc} |
291 | */ |
292 | public function setPathOffset(int $offset = 0) : void |
293 | { |
294 | $this->pathOffset = $offset; |
295 | |
296 | $path = \array_slice($this->pathElements, $this->pathOffset); |
297 | $this->offsetPath = '/' . \implode('/', $path); |
298 | } |
299 | |
300 | /** |
301 | * Return the subdomain of a host |
302 | * |
303 | * @return string |
304 | * |
305 | * @since 1.0.0 |
306 | */ |
307 | public function getSubdomain() : string |
308 | { |
309 | $host = \explode('.', $this->host); |
310 | $length = \count($host) - 2; |
311 | |
312 | if ($length < 1) { |
313 | return ''; |
314 | } |
315 | |
316 | return \implode('.', \array_slice($host, 0, $length)); |
317 | } |
318 | |
319 | /** |
320 | * {@inheritdoc} |
321 | */ |
322 | public function getPath(int $offset = 0) : string |
323 | { |
324 | return $this->path; |
325 | } |
326 | |
327 | /** |
328 | * {@inheritdoc} |
329 | */ |
330 | public function setPath(string $path) : void |
331 | { |
332 | $this->path = $path; |
333 | $this->pathElements = \explode('/', \ltrim($this->path, '/')); |
334 | |
335 | $path = \array_slice($this->pathElements, $this->pathOffset); |
336 | $this->offsetPath = '/' . \implode('/', $path); |
337 | } |
338 | |
339 | /** |
340 | * Get path offset. |
341 | * |
342 | * @return int |
343 | * |
344 | * @since 1.0.0 |
345 | */ |
346 | public function getPathOffset() : int |
347 | { |
348 | return $this->pathOffset; |
349 | } |
350 | |
351 | /** |
352 | * {@inheritdoc} |
353 | */ |
354 | public function getRoute(bool $ignoreOffset = false) : string |
355 | { |
356 | $path = $ignoreOffset ? $this->path : $this->offsetPath; |
357 | |
358 | $query = $this->getQuery(); |
359 | return $path . (empty($query) ? '' : '?' . $this->getQuery()); |
360 | } |
361 | |
362 | /** |
363 | * {@inheritdoc} |
364 | */ |
365 | public function getQuery(string $key = null) : string |
366 | { |
367 | if ($key !== null) { |
368 | $key = \strtolower($key); |
369 | |
370 | return $this->query[$key] ?? ''; |
371 | } |
372 | |
373 | return $this->queryString; |
374 | } |
375 | |
376 | /** |
377 | * {@inheritdoc} |
378 | */ |
379 | public function setQuery(string $uri) : void |
380 | { |
381 | \parse_str($uri, $this->query); |
382 | |
383 | $this->query = \array_change_key_case($this->query, \CASE_LOWER); |
384 | } |
385 | |
386 | /** |
387 | * {@inheritdoc} |
388 | */ |
389 | public function getPathElement(int $pos = 0, bool $useOffset = true) : string |
390 | { |
391 | return $this->pathElements[$pos + ($useOffset ? $this->pathOffset : 0)] ?? ''; |
392 | } |
393 | |
394 | /** |
395 | * {@inheritdoc} |
396 | */ |
397 | public function getPathKey(string $key) : string |
398 | { |
399 | foreach ($this->pathElements as $index => $element) { |
400 | if ($element === $key) { |
401 | return $this->pathElements[$index + 1] ?? ''; |
402 | } |
403 | } |
404 | |
405 | return ''; |
406 | } |
407 | |
408 | /** |
409 | * {@inheritdoc} |
410 | */ |
411 | public function getPathElements() : array |
412 | { |
413 | return $this->pathElements; |
414 | } |
415 | |
416 | /** |
417 | * {@inheritdoc} |
418 | */ |
419 | public function getQueryArray() : array |
420 | { |
421 | return $this->query; |
422 | } |
423 | |
424 | /** |
425 | * {@inheritdoc} |
426 | */ |
427 | public function getBase() : string |
428 | { |
429 | return $this->base; |
430 | } |
431 | |
432 | /** |
433 | * {@inheritdoc} |
434 | */ |
435 | public function __toString() |
436 | { |
437 | return $this->uri; |
438 | } |
439 | |
440 | /** |
441 | * {@inheritdoc} |
442 | */ |
443 | public function getAuthority() : string |
444 | { |
445 | return ($this->user !== '' ? $this->user . '@' : '') . $this->host |
446 | . ($this->port !== 0 ? ':' . $this->port : ''); |
447 | } |
448 | |
449 | /** |
450 | * {@inheritdoc} |
451 | */ |
452 | public function getUserInfo() : string |
453 | { |
454 | return $this->user . (empty($this->pass) ? '' : ':' . $this->pass); |
455 | } |
456 | } |