Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
99.12% |
112 / 113 |
|
97.87% |
46 / 47 |
CRAP | |
0.00% |
0 / 1 |
Request | |
99.12% |
112 / 113 |
|
97.87% |
46 / 47 |
85 | |
0.00% |
0 / 1 |
__construct | |
95.45% |
21 / 22 |
|
0.00% |
0 / 1 |
11 | |||
create | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setAuth | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getAuth | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasAuth | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isGet | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
isHead | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
isPost | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
isPut | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
isDelete | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
isTrace | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
isOptions | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
isConnect | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
isPatch | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
isSecure | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
3 | |||
getDocumentRoot | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getMethod | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPort | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getScheme | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
getHost | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
4 | |||
getFullHost | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
5 | |||
getIp | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
6 | |||
getCookie | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getServer | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getEnv | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getBasePath | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getUriString | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getFullUriString | |
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 | |||
setBasePath | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
hasFiles | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getQuery | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPost | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getFiles | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPut | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPatch | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getDelete | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getQueryData | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasQueryData | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getParsedData | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasParsedData | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRawData | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasRawData | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getData | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasData | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__get | |
100.00% |
15 / 15 |
|
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\Server; |
15 | |
16 | use Pop\Http\Auth; |
17 | use Pop\Http\Uri; |
18 | use Pop\Http\AbstractRequest; |
19 | use Pop\Mime\Part\Body; |
20 | |
21 | /** |
22 | * HTTP server request class |
23 | * |
24 | * @category Pop |
25 | * @package Pop\Http |
26 | * @author Nick Sagona, III <dev@nolainteractive.com> |
27 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
28 | * @license http://www.popphp.org/license New BSD License |
29 | * @version 5.2.0 |
30 | */ |
31 | class Request extends AbstractRequest |
32 | { |
33 | |
34 | /** |
35 | * Server request data object |
36 | * @var ?Data |
37 | */ |
38 | protected ?Data $data = null; |
39 | |
40 | /** |
41 | * COOKIE array |
42 | * @var array |
43 | */ |
44 | protected array $cookie = []; |
45 | |
46 | /** |
47 | * SERVER array |
48 | * @var array |
49 | */ |
50 | protected array $server = []; |
51 | |
52 | /** |
53 | * ENV array |
54 | * @var array |
55 | */ |
56 | protected array $env = []; |
57 | |
58 | /** |
59 | * HTTP auth object |
60 | * @var ?Auth |
61 | */ |
62 | protected ?Auth $auth = null; |
63 | |
64 | /** |
65 | * Constructor |
66 | * |
67 | * Instantiate the request object |
68 | * |
69 | * @param Uri|string|null $uri |
70 | * @param mixed $filters |
71 | * @param mixed $streamToFile |
72 | * @throws Exception|\Pop\Http\Exception |
73 | */ |
74 | public function __construct(Uri|string|null $uri = null, mixed $filters = null, mixed $streamToFile = null) |
75 | { |
76 | parent::__construct($uri); |
77 | |
78 | $this->cookie = (isset($_COOKIE)) ? $_COOKIE : []; |
79 | $this->server = (isset($_SERVER)) ? $_SERVER : []; |
80 | $this->env = (isset($_ENV)) ? $_ENV : []; |
81 | |
82 | // Get any possible request headers |
83 | if (function_exists('getallheaders')) { |
84 | $this->addHeaders(getallheaders()); |
85 | } else { |
86 | foreach ($_SERVER as $key => $value) { |
87 | if (str_starts_with($key, 'HTTP_')) { |
88 | $key = ucfirst(strtolower(str_replace('HTTP_', '', $key))); |
89 | if (str_contains($key, '_')) { |
90 | $ary = explode('_', $key); |
91 | foreach ($ary as $k => $v){ |
92 | $ary[$k] = ucfirst(strtolower($v)); |
93 | } |
94 | $key = implode('-', $ary); |
95 | } |
96 | $this->addHeader($key, $value); |
97 | } |
98 | } |
99 | } |
100 | |
101 | if ($this->hasHeader('Authorization')) { |
102 | $this->setAuth(Auth::parse($this->getHeader('Authorization'))); |
103 | } |
104 | |
105 | $this->data = new Data( |
106 | $this->getHeaderValue('Content-Type'), $this->getHeaderValue('Content-Encoding'), $filters, $streamToFile |
107 | ); |
108 | |
109 | if ($this->data->hasRawData()) { |
110 | $this->body = new Body($this->data->getRawData()); |
111 | } |
112 | } |
113 | |
114 | /** |
115 | * Factory to create a new request object |
116 | * |
117 | * @param ?Uri $uri |
118 | * @param mixed $filters |
119 | * @param mixed $streamToFile |
120 | * @throws Exception|\Pop\Http\Exception |
121 | * @return Request |
122 | */ |
123 | public static function create(?Uri $uri = null, mixed $filters = null, mixed $streamToFile = null): Request |
124 | { |
125 | return new self($uri, $filters, $streamToFile); |
126 | } |
127 | |
128 | /** |
129 | * Set the auth object |
130 | * |
131 | * @param Auth $auth |
132 | * @return Request |
133 | */ |
134 | public function setAuth(Auth $auth): Request |
135 | { |
136 | $this->auth = $auth; |
137 | return $this; |
138 | } |
139 | |
140 | /** |
141 | * Get the auth object |
142 | * |
143 | * @return Auth |
144 | */ |
145 | public function getAuth(): Auth |
146 | { |
147 | return $this->auth; |
148 | } |
149 | |
150 | /** |
151 | * Has auth object |
152 | * |
153 | * @return bool |
154 | */ |
155 | public function hasAuth(): bool |
156 | { |
157 | return ($this->auth !== null); |
158 | } |
159 | |
160 | /** |
161 | * Return whether or not the method is GET |
162 | * |
163 | * @return bool |
164 | */ |
165 | public function isGet(): bool |
166 | { |
167 | return (isset($this->server['REQUEST_METHOD']) && ($this->server['REQUEST_METHOD'] == 'GET')); |
168 | } |
169 | |
170 | /** |
171 | * Return whether or not the method is HEAD |
172 | * |
173 | * @return bool |
174 | */ |
175 | public function isHead(): bool |
176 | { |
177 | return (isset($this->server['REQUEST_METHOD']) && ($this->server['REQUEST_METHOD'] == 'HEAD')); |
178 | } |
179 | |
180 | /** |
181 | * Return whether or not the method is POST |
182 | * |
183 | * @return bool |
184 | */ |
185 | public function isPost(): bool |
186 | { |
187 | return (isset($this->server['REQUEST_METHOD']) && ($this->server['REQUEST_METHOD'] == 'POST')); |
188 | } |
189 | |
190 | /** |
191 | * Return whether or not the method is PUT |
192 | * |
193 | * @return bool |
194 | */ |
195 | public function isPut(): bool |
196 | { |
197 | return (isset($this->server['REQUEST_METHOD']) && ($this->server['REQUEST_METHOD'] == 'PUT')); |
198 | } |
199 | |
200 | /** |
201 | * Return whether or not the method is DELETE |
202 | * |
203 | * @return bool |
204 | */ |
205 | public function isDelete(): bool |
206 | { |
207 | return (isset($this->server['REQUEST_METHOD']) && ($this->server['REQUEST_METHOD'] == 'DELETE')); |
208 | } |
209 | |
210 | /** |
211 | * Return whether or not the method is TRACE |
212 | * |
213 | * @return bool |
214 | */ |
215 | public function isTrace(): bool |
216 | { |
217 | return (isset($this->server['REQUEST_METHOD']) && ($this->server['REQUEST_METHOD'] == 'TRACE')); |
218 | } |
219 | |
220 | /** |
221 | * Return whether or not the method is OPTIONS |
222 | * |
223 | * @return bool |
224 | */ |
225 | public function isOptions(): bool |
226 | { |
227 | return (isset($this->server['REQUEST_METHOD']) && ($this->server['REQUEST_METHOD'] == 'OPTIONS')); |
228 | } |
229 | |
230 | /** |
231 | * Return whether or not the method is CONNECT |
232 | * |
233 | * @return bool |
234 | */ |
235 | public function isConnect(): bool |
236 | { |
237 | return (isset($this->server['REQUEST_METHOD']) && ($this->server['REQUEST_METHOD'] == 'CONNECT')); |
238 | } |
239 | |
240 | /** |
241 | * Return whether or not the method is PATCH |
242 | * |
243 | * @return bool |
244 | */ |
245 | public function isPatch(): bool |
246 | { |
247 | return (isset($this->server['REQUEST_METHOD']) && ($this->server['REQUEST_METHOD'] == 'PATCH')); |
248 | } |
249 | |
250 | /** |
251 | * Return whether or not the request is secure |
252 | * |
253 | * @return bool |
254 | */ |
255 | public function isSecure(): bool |
256 | { |
257 | return (isset($this->server['HTTPS']) || (isset($_SERVER['SERVER_PORT']) && ($_SERVER['SERVER_PORT'] == '443'))); |
258 | } |
259 | |
260 | /** |
261 | * Get the document root |
262 | * |
263 | * @return string|null |
264 | */ |
265 | public function getDocumentRoot(): string|null |
266 | { |
267 | return $this->server['DOCUMENT_ROOT'] ?? null; |
268 | } |
269 | |
270 | /** |
271 | * Get the method |
272 | * |
273 | * @return string|null |
274 | */ |
275 | public function getMethod(): string|null |
276 | { |
277 | return $this->server['REQUEST_METHOD'] ?? null; |
278 | } |
279 | |
280 | /** |
281 | * Get the server port |
282 | * |
283 | * @return string|null |
284 | */ |
285 | public function getPort(): string|null |
286 | { |
287 | return $this->server['SERVER_PORT'] ?? null; |
288 | } |
289 | |
290 | /** |
291 | * Get scheme |
292 | * |
293 | * @return string |
294 | */ |
295 | public function getScheme(): string |
296 | { |
297 | return ($this->isSecure()) ? 'https' : 'http'; |
298 | } |
299 | |
300 | /** |
301 | * Get host (without port) |
302 | * |
303 | * @return string |
304 | */ |
305 | public function getHost(): string |
306 | { |
307 | $hostname = null; |
308 | |
309 | if (!empty($this->server['HTTP_HOST'])) { |
310 | $hostname = $this->server['HTTP_HOST']; |
311 | } else if (!empty($this->server['SERVER_NAME'])) { |
312 | $hostname = $this->server['SERVER_NAME']; |
313 | } |
314 | |
315 | if (str_contains($hostname, ':')) { |
316 | $hostname = substr($hostname, 0, strpos($hostname, ':')); |
317 | } |
318 | |
319 | return $hostname; |
320 | } |
321 | |
322 | /** |
323 | * Get host with port |
324 | * |
325 | * @return string |
326 | */ |
327 | public function getFullHost(): string |
328 | { |
329 | $port = $this->getPort(); |
330 | $hostname = null; |
331 | |
332 | if (!empty($this->server['HTTP_HOST'])) { |
333 | $hostname = $this->server['HTTP_HOST']; |
334 | } else if (!empty($this->server['SERVER_NAME'])) { |
335 | $hostname = $this->server['SERVER_NAME']; |
336 | } |
337 | |
338 | if ((!str_contains($hostname, ':')) && ($port !== null)) { |
339 | $hostname .= ':' . $port; |
340 | } |
341 | |
342 | return $hostname; |
343 | } |
344 | |
345 | /** |
346 | * Get client's IP |
347 | * |
348 | * @param bool $proxy |
349 | * @return string |
350 | */ |
351 | public function getIp(bool $proxy = true): string |
352 | { |
353 | $ip = null; |
354 | |
355 | if ($proxy && isset($this->server['HTTP_CLIENT_IP'])) { |
356 | $ip = $this->server['HTTP_CLIENT_IP']; |
357 | } else if ($proxy && isset($this->server['HTTP_X_FORWARDED_FOR'])) { |
358 | $ip = $this->server['HTTP_X_FORWARDED_FOR']; |
359 | } else if (isset($this->server['REMOTE_ADDR'])) { |
360 | $ip = $this->server['REMOTE_ADDR']; |
361 | } |
362 | |
363 | return $ip; |
364 | } |
365 | |
366 | /** |
367 | * Get a value from $_COOKIE, or the whole array |
368 | * |
369 | * @param ?string $key |
370 | * @return string|array|null |
371 | */ |
372 | public function getCookie(?string $key = null): string|array|null |
373 | { |
374 | if ($key === null) { |
375 | return $this->cookie; |
376 | } else { |
377 | return $this->cookie[$key] ?? null; |
378 | } |
379 | } |
380 | |
381 | /** |
382 | * Get a value from $_SERVER, or the whole array |
383 | * |
384 | * @param ?string $key |
385 | * @return string|array|null |
386 | */ |
387 | public function getServer(?string $key = null): string|array|null |
388 | { |
389 | if ($key === null) { |
390 | return $this->server; |
391 | } else { |
392 | return $this->server[$key] ?? null; |
393 | } |
394 | } |
395 | |
396 | /** |
397 | * Get a value from $_ENV, or the whole array |
398 | * |
399 | * @param ?string $key |
400 | * @return string|array|null |
401 | */ |
402 | public function getEnv(?string $key = null): string|array|null |
403 | { |
404 | if ($key === null) { |
405 | return $this->env; |
406 | } else { |
407 | return $this->env[$key] ?? null; |
408 | } |
409 | } |
410 | |
411 | /** |
412 | * Get the base path |
413 | * |
414 | * @return string |
415 | */ |
416 | public function getBasePath(): string |
417 | { |
418 | return $this->uri->getBasePath(); |
419 | } |
420 | |
421 | /** |
422 | * Get the request URI |
423 | * |
424 | * @return string |
425 | */ |
426 | public function getUriString(): string |
427 | { |
428 | return $this->uri->getUri(); |
429 | } |
430 | |
431 | /** |
432 | * Get the full request URI, including base path |
433 | * |
434 | * @return string |
435 | */ |
436 | public function getFullUriString(): string |
437 | { |
438 | return $this->uri->getFullUri(); |
439 | } |
440 | |
441 | /** |
442 | * Get a path segment, divided by the forward slash, |
443 | * where $i refers to the array key index, i.e., |
444 | * 0 1 2 |
445 | * /hello/world/page |
446 | * |
447 | * @param int $i |
448 | * @return string|null |
449 | */ |
450 | public function getSegment(int $i): string|null |
451 | { |
452 | return $this->uri->getSegment($i); |
453 | } |
454 | |
455 | /** |
456 | * Get all path segments |
457 | * |
458 | * @return array |
459 | */ |
460 | public function getSegments(): array |
461 | { |
462 | return $this->uri->getSegments(); |
463 | } |
464 | |
465 | /** |
466 | * Set the base path |
467 | * |
468 | * @param ?string $path |
469 | * @return Request |
470 | */ |
471 | public function setBasePath(?string $path = null): Request |
472 | { |
473 | if ($this->uri !== null) { |
474 | $this->uri->setBasePath($path); |
475 | } |
476 | return $this; |
477 | } |
478 | |
479 | /** |
480 | * Return whether or not the request has FILES |
481 | * |
482 | * @return bool |
483 | */ |
484 | public function hasFiles(): bool |
485 | { |
486 | return $this->data->hasFiles(); |
487 | } |
488 | |
489 | /** |
490 | * Get a value from $_GET, or the whole array |
491 | * |
492 | * @param ?string $key |
493 | * @return string|array|null |
494 | */ |
495 | public function getQuery(?string $key = null): string|array|null |
496 | { |
497 | return $this->data->getQuery($key); |
498 | } |
499 | |
500 | /** |
501 | * Get a value from $_POST, or the whole array |
502 | * |
503 | * @param ?string $key |
504 | * @return string|array|null |
505 | */ |
506 | public function getPost(?string $key = null): string|array|null |
507 | { |
508 | return $this->data->getPost($key); |
509 | } |
510 | |
511 | /** |
512 | * Get a value from $_FILES, or the whole array |
513 | * |
514 | * @param ?string $key |
515 | * @return string|array|null |
516 | */ |
517 | public function getFiles(?string $key = null): string|array|null |
518 | { |
519 | return $this->data->getFiles($key); |
520 | } |
521 | |
522 | /** |
523 | * Get a value from PUT query data, or the whole array |
524 | * |
525 | * @param ?string $key |
526 | * @return string|array|null |
527 | */ |
528 | public function getPut(?string $key = null): string|array|null |
529 | { |
530 | return $this->data->getPut($key); |
531 | } |
532 | |
533 | /** |
534 | * Get a value from PATCH query data, or the whole array |
535 | * |
536 | * @param ?string $key |
537 | * @return string|array|null |
538 | */ |
539 | public function getPatch(?string $key = null): string|array|null |
540 | { |
541 | return $this->data->getPatch($key); |
542 | } |
543 | |
544 | /** |
545 | * Get a value from DELETE query data, or the whole array |
546 | * |
547 | * @param ?string $key |
548 | * @return string|array|null |
549 | */ |
550 | public function getDelete(?string $key = null): string|array|null |
551 | { |
552 | return $this->data->getDelete($key); |
553 | } |
554 | |
555 | |
556 | /** |
557 | * Get a value from query data, or the whole array |
558 | * |
559 | * @param ?string $key |
560 | * @return string|array|null |
561 | */ |
562 | public function getQueryData(?string $key = null): string|array|null |
563 | { |
564 | return $this->data->getQueryData($key); |
565 | } |
566 | |
567 | /** |
568 | * Has query data |
569 | * |
570 | * @return bool |
571 | */ |
572 | public function hasQueryData(): bool |
573 | { |
574 | return $this->data->hasQueryData(); |
575 | } |
576 | |
577 | /** |
578 | * Get a value from parsed data, or the whole array |
579 | * |
580 | * @param ?string $key |
581 | * @return string|array|null |
582 | */ |
583 | public function getParsedData(?string $key = null): string|array|null |
584 | { |
585 | return $this->data->getParsedData($key); |
586 | } |
587 | |
588 | /** |
589 | * Has parsed data |
590 | * |
591 | * @return bool |
592 | */ |
593 | public function hasParsedData(): bool |
594 | { |
595 | return $this->data->hasParsedData(); |
596 | } |
597 | |
598 | /** |
599 | * Get the raw data |
600 | * |
601 | * @return string|null |
602 | */ |
603 | public function getRawData(): string|null |
604 | { |
605 | return $this->data->getRawData(); |
606 | } |
607 | |
608 | /** |
609 | * Has raw data |
610 | * |
611 | * @return bool |
612 | */ |
613 | public function hasRawData(): bool |
614 | { |
615 | return $this->data->hasRawData(); |
616 | } |
617 | |
618 | /** |
619 | * Get data |
620 | * |
621 | * @return Uri |
622 | */ |
623 | public function getData(): Data |
624 | { |
625 | return $this->data; |
626 | } |
627 | |
628 | /** |
629 | * Has data |
630 | * |
631 | * @return bool |
632 | */ |
633 | public function hasData(): bool |
634 | { |
635 | return ($this->data !== null); |
636 | } |
637 | |
638 | /** |
639 | * Magic method to get a value from one of the server/environment variables |
640 | * |
641 | * @param string $name |
642 | * @return mixed |
643 | */ |
644 | public function __get(string $name): mixed |
645 | { |
646 | return match ($name) { |
647 | 'get' => $this->data->get, |
648 | 'post' => $this->data->post, |
649 | 'files' => $this->data->files, |
650 | 'put' => $this->data->put, |
651 | 'patch' => $this->data->patch, |
652 | 'delete' => $this->data->delete, |
653 | 'parsed' => $this->data->parsed, |
654 | 'raw' => $this->data->raw, |
655 | 'cookie' => $this->cookie, |
656 | 'server' => $this->server, |
657 | 'env' => $this->env, |
658 | 'headers' => $this->headers, |
659 | default => null, |
660 | }; |
661 | } |
662 | |
663 | } |