Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
99.12% covered (success)
99.12%
112 / 113
97.87% covered (success)
97.87%
46 / 47
CRAP
0.00% covered (danger)
0.00%
0 / 1
Request
99.12% covered (success)
99.12%
112 / 113
97.87% covered (success)
97.87%
46 / 47
85
0.00% covered (danger)
0.00%
0 / 1
 __construct
95.45% covered (success)
95.45%
21 / 22
0.00% covered (danger)
0.00%
0 / 1
11
 create
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setAuth
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getAuth
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasAuth
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isGet
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 isHead
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 isPost
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 isPut
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 isDelete
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 isTrace
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 isOptions
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 isConnect
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 isPatch
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 isSecure
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
3
 getDocumentRoot
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMethod
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPort
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getScheme
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getHost
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 getFullHost
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
 getIp
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
6
 getCookie
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getServer
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getEnv
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getBasePath
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getUriString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFullUriString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSegment
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSegments
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setBasePath
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 hasFiles
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getQuery
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPost
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFiles
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPut
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPatch
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDelete
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getQueryData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasQueryData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getParsedData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasParsedData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRawData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasRawData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __get
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
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 */
14namespace Pop\Http\Server;
15
16use Pop\Http\Auth;
17use Pop\Http\Uri;
18use Pop\Http\AbstractRequest;
19use 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.0.0
30 */
31class 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}