Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
89.91% |
98 / 109 |
|
88.46% |
23 / 26 |
CRAP | |
0.00% |
0 / 1 |
HttpRequest | |
89.91% |
98 / 109 |
|
88.46% |
23 / 26 |
109.07 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
initRequest | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
initCurrentRequest | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
initNonGetData | |
20.00% |
2 / 10 |
|
0.00% |
0 / 1 |
901.67 | |||
setMethod | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRequestLanguage | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getRequestCountry | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getLocale | |
60.00% |
3 / 5 |
|
0.00% |
0 / 1 |
5.02 | |||
cleanupGlobals | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
createFromSuperglobals | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
setUri | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
createRequestHashs | |
90.00% |
9 / 10 |
|
0.00% |
0 / 1 |
4.02 | |||
isMobile | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
getRequestInfo | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getBrowser | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
4 | |||
setBrowser | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getOS | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
4 | |||
setOS | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getOrigin | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isHttps | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
7 | |||
getBody | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
getRouteVerb | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
6 | |||
getReferer | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getMethod | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
rest | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__toString | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
4 |
1 | <?php |
2 | /** |
3 | * Jingga |
4 | * |
5 | * PHP Version 8.1 |
6 | * |
7 | * @package phpOMS\Message\Http |
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\Message\Http; |
16 | |
17 | use phpOMS\Localization\ISO3166TwoEnum; |
18 | use phpOMS\Localization\ISO639x1Enum; |
19 | use phpOMS\Localization\Localization; |
20 | use phpOMS\Message\RequestAbstract; |
21 | use phpOMS\Router\RouteVerb; |
22 | use phpOMS\Security\Guard; |
23 | use phpOMS\Uri\HttpUri; |
24 | use phpOMS\Uri\UriInterface; |
25 | |
26 | /** |
27 | * Request class. |
28 | * |
29 | * @package phpOMS\Message\Http |
30 | * @license OMS License 2.0 |
31 | * @link https://jingga.app |
32 | * @since 1.0.0 |
33 | * |
34 | * @SuppressWarnings(PHPMD.Superglobals) |
35 | * |
36 | * @property HttpHeader $header |
37 | */ |
38 | final class HttpRequest extends RequestAbstract |
39 | { |
40 | /** |
41 | * Request method. |
42 | * |
43 | * @var string |
44 | * @since 1.0.0 |
45 | */ |
46 | protected string $method; |
47 | |
48 | /** |
49 | * Browser type. |
50 | * |
51 | * @var string |
52 | * @since 1.0.0 |
53 | */ |
54 | private string $browser; |
55 | |
56 | /** |
57 | * OS type. |
58 | * |
59 | * @var string |
60 | * @since 1.0.0 |
61 | */ |
62 | private string $os; |
63 | |
64 | /** |
65 | * Request information. |
66 | * |
67 | * @var array{browser:string, os:string} |
68 | * @since 1.0.0 |
69 | */ |
70 | private array $info; |
71 | |
72 | /** |
73 | * Constructor. |
74 | * |
75 | * @param UriInterface $uri Uri |
76 | * @param Localization $l11n Localization |
77 | * |
78 | * @since 1.0.0 |
79 | */ |
80 | public function __construct(UriInterface $uri = null, Localization $l11n = null) |
81 | { |
82 | $this->header = new HttpHeader(); |
83 | $this->header->l11n = $l11n ?? new Localization(); |
84 | $this->uri = $uri ?? new HttpUri(''); |
85 | } |
86 | |
87 | /** |
88 | * Init request. |
89 | * |
90 | * This is used in order to either initialize the current http request or a batch of GET requests |
91 | * |
92 | * @return void |
93 | * |
94 | * @since 1.0.0 |
95 | */ |
96 | public function initRequest() : void |
97 | { |
98 | $this->initCurrentRequest(); |
99 | self::cleanupGlobals(); |
100 | |
101 | $this->data = \array_change_key_case($this->data, \CASE_LOWER); |
102 | $this->data = Guard::unslash($this->data); |
103 | } |
104 | |
105 | /** |
106 | * Init current request |
107 | * |
108 | * @return void |
109 | * |
110 | * @since 1.0.0 |
111 | */ |
112 | private function initCurrentRequest() : void |
113 | { |
114 | $this->uri = HttpUri::fromCurrent(); |
115 | $this->data = $_POST + $_GET; |
116 | $this->files = $_FILES; |
117 | $this->header->initCurrentRequest(); |
118 | $this->header->l11n->setLanguage($this->getRequestLanguage()); |
119 | $this->header->l11n->setCountry($this->getRequestCountry()); |
120 | |
121 | $this->initNonGetData(); |
122 | } |
123 | |
124 | /** |
125 | * Init non get data |
126 | * |
127 | * @return void |
128 | * |
129 | * @throws \Exception |
130 | * |
131 | * @since 1.0.0 |
132 | */ |
133 | private function initNonGetData() : void |
134 | { |
135 | if (!isset($_SERVER['CONTENT_TYPE'])) { |
136 | return; |
137 | } |
138 | |
139 | if (\stripos($_SERVER['CONTENT_TYPE'], 'application/json') !== false) { |
140 | // @codeCoverageIgnoreStart |
141 | // Tested but coverage doesn't show up |
142 | $stream = \fopen('php://input', 'r'); |
143 | if ($stream === false) { |
144 | return; |
145 | } |
146 | |
147 | $input = ''; |
148 | $size = 0; |
149 | |
150 | while (($lineRaw = \fgets($stream, 1024)) !== false) { |
151 | // Limit json data to 1MB |
152 | if ($size > 1000000) { |
153 | \fclose($stream); |
154 | |
155 | return; |
156 | } |
157 | |
158 | $input .= $lineRaw; |
159 | $size += \strlen($lineRaw); |
160 | } |
161 | |
162 | \fclose($stream); |
163 | |
164 | if (empty($input)) { |
165 | return; |
166 | } |
167 | |
168 | $json = \json_decode($input, true); |
169 | if (!\is_array($json)) { |
170 | throw new \Exception('Is not valid json ' . $input); |
171 | } |
172 | |
173 | $this->data = $json + $this->data; |
174 | // @codeCoverageIgnoreEnd |
175 | } elseif (\stripos($_SERVER['CONTENT_TYPE'], 'application/x-www-form-urlencoded') !== false) { |
176 | // @codeCoverageIgnoreStart |
177 | // Tested but coverage doesn't show up |
178 | $stream = \fopen('php://input', 'r'); |
179 | if ($stream === false) { |
180 | return; |
181 | } |
182 | |
183 | $content = ''; |
184 | $size = 0; |
185 | |
186 | while (($lineRaw = \fgets($stream, 1024)) !== false) { |
187 | // Limit json data to 1MB |
188 | if ($size > 1000000) { |
189 | \fclose($stream); |
190 | |
191 | return; |
192 | } |
193 | |
194 | $content .= $lineRaw; |
195 | $size += \strlen($lineRaw); |
196 | } |
197 | |
198 | \fclose($stream); |
199 | |
200 | if (empty($content)) { |
201 | return; |
202 | } |
203 | |
204 | \parse_str($content, $temp); |
205 | $this->data += $temp; |
206 | // @codeCoverageIgnoreEnd |
207 | } elseif (\stripos($_SERVER['CONTENT_TYPE'], 'multipart/form-data') !== false) { |
208 | // @codeCoverageIgnoreStart |
209 | // Tested but coverage doesn't show up |
210 | $stream = \fopen('php://input', 'r'); |
211 | $partInfo = null; |
212 | $boundary = null; |
213 | |
214 | if ($stream === false) { |
215 | return; |
216 | } |
217 | |
218 | // @codeCoverageIgnoreEnd |
219 | while (($lineRaw = \fgets($stream)) !== false) { |
220 | // @codeCoverageIgnoreStart |
221 | // Tested but coverage doesn't show up |
222 | if (\str_starts_with($lineRaw, '--')) { |
223 | if ($boundary === null) { |
224 | $boundary = \rtrim($lineRaw); |
225 | } |
226 | |
227 | continue; |
228 | } |
229 | |
230 | $line = \rtrim($lineRaw); |
231 | |
232 | if ($line === '') { |
233 | if (!empty($partInfo['Content-Disposition']['filename'])) { /* Is file */ |
234 | $tempdir = \sys_get_temp_dir(); |
235 | $name = $partInfo['Content-Disposition']['name']; |
236 | $this->files[$name] = []; |
237 | $this->files[$name]['name'] = $partInfo['Content-Disposition']['filename']; |
238 | $this->files[$name]['type'] = $partInfo['Content-Type']['value'] ?? null; |
239 | |
240 | $tempname = \tempnam($tempdir, 'oms_upl_'); |
241 | |
242 | if ($tempname === false) { |
243 | $this->files[$name]['error'] = \UPLOAD_ERR_NO_TMP_DIR; |
244 | return; |
245 | } |
246 | |
247 | $outFP = \fopen($tempname, 'wb'); |
248 | |
249 | if ($outFP === false) { |
250 | $this->files[$name]['error'] = \UPLOAD_ERR_CANT_WRITE; |
251 | return; |
252 | } |
253 | |
254 | $lastLine = null; |
255 | while (($lineRaw = \fgets($stream, 4096)) !== false) { |
256 | if ($lastLine !== null) { |
257 | if ($boundary === null || \str_starts_with($lineRaw, $boundary)) { |
258 | break; |
259 | } |
260 | |
261 | if (\fwrite($outFP, $lastLine) === false) { |
262 | $this->files[$name] = \UPLOAD_ERR_CANT_WRITE; |
263 | return; |
264 | } |
265 | } |
266 | |
267 | $lastLine = $lineRaw; |
268 | } |
269 | |
270 | if ($lastLine !== null && \fwrite($outFP, \rtrim($lastLine, "\r\n")) === false) { |
271 | $this->files[$name]['error'] = \UPLOAD_ERR_CANT_WRITE; |
272 | return; |
273 | } |
274 | |
275 | \fclose($outFP); |
276 | |
277 | $this->files[$name]['error'] = \UPLOAD_ERR_OK; |
278 | $this->files[$name]['size'] = \filesize($tempname); |
279 | $this->files[$name]['tmp_name'] = $tempname; |
280 | // @codeCoverageIgnoreEnd |
281 | } elseif ($partInfo !== null) { /* Is variable */ |
282 | // @codeCoverageIgnoreStart |
283 | // Tested but coverage doesn't show up |
284 | $fullValue = ''; |
285 | $lastLine = null; |
286 | |
287 | while (($lineRaw = \fgets($stream)) !== false && $boundary !== null && !\str_starts_with($lineRaw, $boundary)) { |
288 | if ($lastLine !== null) { |
289 | $fullValue .= $lastLine; |
290 | } |
291 | |
292 | $lastLine = $lineRaw; |
293 | } |
294 | |
295 | if ($lastLine !== null) { |
296 | $fullValue .= \rtrim($lastLine, "\r\n"); |
297 | } |
298 | |
299 | $this->data[$partInfo['Content-Disposition']['name']] = $fullValue; |
300 | // @codeCoverageIgnoreEnd |
301 | } |
302 | |
303 | $partInfo = null; |
304 | |
305 | continue; |
306 | } |
307 | |
308 | // @codeCoverageIgnoreStart |
309 | // Tested but coverage doesn't show up |
310 | $delim = \strpos($line, ':'); |
311 | |
312 | if ($delim === false) { |
313 | continue; |
314 | } |
315 | |
316 | $headerKey = \substr($line, 0, $delim); |
317 | $headerVal = \substr($line, $delim + 1); |
318 | |
319 | $header = []; |
320 | $regex = '/(^|;)\s*(?P<name>[^=:,;\s"]*):?(=("(?P<quotedValue>[^"]*(\\.[^"]*)*)")|(\s*(?P<value>[^=,;\s"]*)))?/mx'; |
321 | $matches = null; |
322 | |
323 | \preg_match_all($regex, $headerVal, $matches, \PREG_SET_ORDER); |
324 | |
325 | $length = \count($matches); |
326 | for ($i = 0; $i < $length; ++$i) { |
327 | $match = $matches[$i]; |
328 | $name = $match['name']; |
329 | $quotedValue = $match['quotedValue']; |
330 | |
331 | $value = empty($quotedValue) ? $value = $match['value'] : \stripcslashes($quotedValue); |
332 | |
333 | if ($name === $headerKey && $i === 0) { |
334 | $name = 'value'; |
335 | } elseif ($value === '') { |
336 | $value = $name; |
337 | $name = 'value'; |
338 | } |
339 | |
340 | $header[$name] = $value; |
341 | } |
342 | |
343 | $partInfo[$headerKey] = $header; |
344 | // @codeCoverageIgnoreEnd |
345 | } |
346 | |
347 | \fclose($stream); |
348 | } |
349 | } |
350 | |
351 | /** |
352 | * Set request method. |
353 | * |
354 | * @param string $method Request method |
355 | * |
356 | * @return void |
357 | * |
358 | * @since 1.0.0 |
359 | */ |
360 | public function setMethod(string $method) : void |
361 | { |
362 | $this->method = $method; |
363 | } |
364 | |
365 | /** |
366 | * Get request language |
367 | * |
368 | * @return string |
369 | * |
370 | * @since 1.0.0 |
371 | */ |
372 | private function getRequestLanguage() : string |
373 | { |
374 | $locale = $this->getLocale(); |
375 | $firstLocalComponents = \explode('_', $locale); |
376 | $language = \strtolower($firstLocalComponents[0]); |
377 | |
378 | return ISO639x1Enum::isValidValue($language) ? $language : 'en'; |
379 | } |
380 | |
381 | /** |
382 | * Get request country |
383 | * |
384 | * @return string |
385 | * |
386 | * @since 1.0.0 |
387 | */ |
388 | private function getRequestCountry() : string |
389 | { |
390 | $locale = $this->getLocale(); |
391 | $firstLocalComponents = \explode('_', $locale); |
392 | $country = \strtoupper($firstLocalComponents[1] ?? ''); |
393 | |
394 | return ISO3166TwoEnum::isValidValue($country) ? $country : 'US'; |
395 | } |
396 | |
397 | /** |
398 | * Get request locale |
399 | * |
400 | * @return string |
401 | * |
402 | * @since 1.0.0 |
403 | */ |
404 | public function getLocale() : string |
405 | { |
406 | if (!empty($this->locale)) { |
407 | return $this->locale = $this->header->l11n->language . '_' . $this->header->l11n->country; |
408 | } |
409 | |
410 | if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { |
411 | return 'en_US'; |
412 | } |
413 | |
414 | // @codeCoverageIgnoreStart |
415 | $components = \explode(';', $_SERVER['HTTP_ACCEPT_LANGUAGE']); |
416 | $locals = \stripos($components[0], ',') !== false |
417 | ? $locals = \explode(',', $components[0]) |
418 | : $components; |
419 | // @codeCoverageIgnoreEnd |
420 | |
421 | $this->locale = \strtr($locals[0], '-', '_'); // @codeCoverageIgnore |
422 | |
423 | return $this->locale; |
424 | } |
425 | |
426 | /** |
427 | * Clean up globals that musn't be used any longer |
428 | * |
429 | * @return void |
430 | * |
431 | * @since 1.0.0 |
432 | */ |
433 | public static function cleanupGlobals() : void |
434 | { |
435 | unset($_FILES); |
436 | unset($_GET); |
437 | unset($_POST); |
438 | unset($_REQUEST); |
439 | } |
440 | |
441 | /** |
442 | * Create request from superglobals. |
443 | * |
444 | * @return HttpRequest |
445 | * |
446 | * @since 1.0.0 |
447 | */ |
448 | public static function createFromSuperglobals() : self |
449 | { |
450 | $request = new self(); |
451 | $request->initRequest(); |
452 | |
453 | return $request; |
454 | } |
455 | |
456 | /** |
457 | * Set request uri. |
458 | * |
459 | * @param UriInterface $uri Uri |
460 | * |
461 | * @return void |
462 | * |
463 | * @since 1.0.0 |
464 | */ |
465 | public function setUri(UriInterface $uri) : void |
466 | { |
467 | $this->uri = $uri; |
468 | $this->data += $uri->getQueryArray(); |
469 | } |
470 | |
471 | /** |
472 | * Create request hashs of current request |
473 | * |
474 | * The hashes are based on the request path and can be used as unique id. |
475 | * |
476 | * @param int $start Start hash from n-th path element |
477 | * |
478 | * @return void |
479 | * |
480 | * @since 1.0.0 |
481 | */ |
482 | public function createRequestHashs(int $start = 0) : void |
483 | { |
484 | $this->hash = [\sha1('')]; |
485 | $pathArray = $this->uri->getPathElements(); |
486 | $pathLength = \count($pathArray); |
487 | |
488 | for ($i = $start; $i < $pathLength; ++$i) { |
489 | if ($pathArray[$i] === '') { |
490 | continue; |
491 | } |
492 | |
493 | $paths = []; |
494 | for ($j = $start; $j <= $i; ++$j) { |
495 | $paths[] = $pathArray[$j]; |
496 | } |
497 | |
498 | $this->hash[] = \sha1(\implode('', $paths)); |
499 | } |
500 | } |
501 | |
502 | /** |
503 | * Is Mobile |
504 | * |
505 | * @return bool |
506 | * |
507 | * @since 1.0.0 |
508 | */ |
509 | public function isMobile() : bool |
510 | { |
511 | $useragent = $_SERVER['HTTP_USER_AGENT'] ?? ''; |
512 | |
513 | if (\preg_match('/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i', $useragent) || \preg_match('/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i', $useragent)) { |
514 | return true; // @codeCoverageIgnore |
515 | } |
516 | |
517 | return false; |
518 | } |
519 | |
520 | /** |
521 | * {@inheritdoc} |
522 | */ |
523 | public function getRequestInfo() : array |
524 | { |
525 | if (!isset($this->info)) { |
526 | $this->info['browser'] = $this->getBrowser(); |
527 | $this->info['os'] = $this->getOS(); |
528 | } |
529 | |
530 | return $this->info; |
531 | } |
532 | |
533 | /** |
534 | * Determine request browser. |
535 | * |
536 | * @return string |
537 | * |
538 | * @since 1.0.0 |
539 | */ |
540 | public function getBrowser() : string |
541 | { |
542 | if (!isset($this->browser)) { |
543 | $arr = BrowserType::getConstants(); |
544 | $httpUserAgent = \strtolower($_SERVER['HTTP_USER_AGENT'] ?? ''); |
545 | |
546 | foreach ($arr as $key => $val) { |
547 | if (\stripos($httpUserAgent, $val)) { |
548 | // @codeCoverageIgnoreStart |
549 | $this->browser = $val; |
550 | |
551 | return $this->browser; |
552 | // @codeCoverageIgnoreEnd |
553 | } |
554 | } |
555 | |
556 | $this->browser = BrowserType::UNKNOWN; |
557 | } |
558 | |
559 | return $this->browser; |
560 | } |
561 | |
562 | /** |
563 | * Set browser type |
564 | * |
565 | * @param string $browser Browser type |
566 | * |
567 | * @return void |
568 | * |
569 | * @since 1.0.0 |
570 | */ |
571 | public function setBrowser(string $browser) : void |
572 | { |
573 | $this->browser = $browser; |
574 | } |
575 | |
576 | /** |
577 | * Determine request OS. |
578 | * |
579 | * @return string |
580 | * |
581 | * @since 1.0.0 |
582 | */ |
583 | public function getOS() : string |
584 | { |
585 | if (!isset($this->os)) { |
586 | $arr = OSType::getConstants(); |
587 | $httpUserAgent = \strtolower($_SERVER['HTTP_USER_AGENT'] ?? ''); |
588 | |
589 | foreach ($arr as $val) { |
590 | if (\stripos($httpUserAgent, $val)) { |
591 | // @codeCoverageIgnoreStart |
592 | $this->os = $val; |
593 | |
594 | return $this->os; |
595 | // @codeCoverageIgnoreEnd |
596 | } |
597 | } |
598 | |
599 | $this->os = OSType::UNKNOWN; |
600 | } |
601 | |
602 | return $this->os; |
603 | } |
604 | |
605 | /** |
606 | * Set OS type |
607 | * |
608 | * @param string $os OS type |
609 | * |
610 | * @return void |
611 | * |
612 | * @since 1.0.0 |
613 | */ |
614 | public function setOS(string $os) : void |
615 | { |
616 | $this->os = $os; |
617 | } |
618 | |
619 | /** |
620 | * {@inheritdoc} |
621 | */ |
622 | public function getOrigin() : string |
623 | { |
624 | return $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'; |
625 | } |
626 | |
627 | /** |
628 | * Is request made via https. |
629 | * |
630 | * @param int $port Secure port |
631 | * |
632 | * @return bool |
633 | * |
634 | * @throws \OutOfRangeException This exception is thrown if the port is out of range |
635 | * |
636 | * @since 1.0.0 |
637 | */ |
638 | public static function isHttps(int $port = 443) : bool |
639 | { |
640 | if ($port < 1 || $port > 65535) { |
641 | throw new \OutOfRangeException('Value "' . $port . '" is out of range.'); |
642 | } |
643 | |
644 | return (!empty($_SERVER['HTTPS'] ?? '') && ($_SERVER['HTTPS'] ?? '') !== 'off') |
645 | || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https') |
646 | || (($_SERVER['HTTP_X_FORWARDED_SSL'] ?? '') === 'on') |
647 | || ($_SERVER['SERVER_PORT'] ?? '') === $port; |
648 | } |
649 | |
650 | /** |
651 | * {@inheritdoc} |
652 | */ |
653 | public function getBody() : string |
654 | { |
655 | $body = \file_get_contents('php://input'); |
656 | return $body === false ? '' : $body; |
657 | } |
658 | |
659 | /** |
660 | * Get route verb. |
661 | * |
662 | * @return int |
663 | * |
664 | * @throws \Exception |
665 | * |
666 | * @since 1.0.0 |
667 | */ |
668 | public function getRouteVerb() : int |
669 | { |
670 | switch ($this->getMethod()) { |
671 | case RequestMethod::GET: |
672 | return RouteVerb::GET; |
673 | case RequestMethod::PUT: |
674 | return RouteVerb::PUT; |
675 | case RequestMethod::POST: |
676 | return RouteVerb::SET; |
677 | case RequestMethod::DELETE: |
678 | return RouteVerb::DELETE; |
679 | default: |
680 | throw new \Exception(); |
681 | } |
682 | } |
683 | |
684 | /** |
685 | * Get referer. |
686 | * |
687 | * @return string |
688 | * |
689 | * @since 1.0.0 |
690 | */ |
691 | public function getReferer() : string |
692 | { |
693 | return $_SERVER['HTTP_REFERER'] ?? ''; |
694 | } |
695 | |
696 | /** |
697 | * Get request method. |
698 | * |
699 | * @return string |
700 | * |
701 | * @since 1.0.0 |
702 | */ |
703 | public function getMethod() : string |
704 | { |
705 | if (!isset($this->method)) { |
706 | $this->method = $_SERVER['REQUEST_METHOD'] ?? RequestMethod::GET; |
707 | } |
708 | |
709 | return $this->method; |
710 | } |
711 | |
712 | /** |
713 | * Perform rest request |
714 | * |
715 | * @return HttpResponse |
716 | * |
717 | * @since 1.0.0 |
718 | */ |
719 | public function rest() : HttpResponse |
720 | { |
721 | return Rest::request($this); |
722 | } |
723 | |
724 | /** |
725 | * {@inheritdoc} |
726 | */ |
727 | public function __toString() : string |
728 | { |
729 | if ($this->getMethod() === RequestMethod::GET && !empty($this->data)) { |
730 | return $this->uri->__toString() |
731 | . (\parse_url($this->uri->__toString(), \PHP_URL_QUERY) ? '&' : '?') |
732 | . \http_build_query($this->data); |
733 | } |
734 | |
735 | return parent::__toString(); |
736 | } |
737 | } |