Passed
Push — release/0.4.1 ( 076d5f )
by Mathieu
04:35
created

Request::parseRemoteIp()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 11
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 18
rs 9.9
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Suricate;
6
7
use InvalidArgumentException;
8
9
/**
10
 * Request class
11
 *
12
 * @SuppressWarnings("StaticAccess")
13
 */
14
class Request
15
{
16
    const HTTP_METHOD_GET = 'GET';
17
    const HTTP_METHOD_POST = 'POST';
18
    const HTTP_METHOD_PUT = 'PUT';
19
    const HTTP_METHOD_DELETE = 'DELETE';
20
    const HTTP_METHOD_HEAD = 'HEAD';
21
    const HTTP_METHOD_OPTIONS = 'OPTIONS';
22
23
    private $method = self::HTTP_METHOD_GET;
24
    private $methods = [
25
        self::HTTP_METHOD_GET => 'GET',
26
        self::HTTP_METHOD_POST => 'POST',
27
        self::HTTP_METHOD_PUT => 'PUT',
28
        self::HTTP_METHOD_DELETE => 'DELETE',
29
        self::HTTP_METHOD_HEAD => 'HEAD',
30
        self::HTTP_METHOD_OPTIONS => 'OPTIONS'
31
    ];
32
33
    private $httpCodeString = [
34
        100 => 'Continue',
35
        101 => 'Switching Protocols',
36
        102 => 'Processing',
37
        200 => 'OK',
38
        201 => 'Created',
39
        202 => 'Accepted',
40
        203 => 'Non-Authoritative Information',
41
        204 => 'No Content',
42
        205 => 'Reset Content',
43
        206 => 'Partial Content',
44
        207 => 'Multi-Status',
45
        208 => 'Already Reported',
46
        226 => 'IM Used',
47
        250 => 'Low on Storage Space',
48
        300 => 'Multiple Choices',
49
        301 => 'Moved Permanently',
50
        302 => 'Found',
51
        303 => 'See Other',
52
        304 => 'Not Modified',
53
        305 => 'Use Proxy',
54
        306 => '306 Switch Proxy',
55
        307 => 'Temporary Redirect',
56
        308 => 'Permanent Redirect',
57
        400 => 'Bad Request',
58
        401 => 'Unauthorized',
59
        402 => 'Payment Required',
60
        403 => 'Forbidden',
61
        404 => 'Not Found',
62
        405 => 'Method Not Allowed',
63
        406 => 'Not Acceptable',
64
        407 => 'Proxy Authentication Required',
65
        408 => 'Request Timeout',
66
        409 => 'Conflict',
67
        410 => 'Gone',
68
        411 => 'Length Required',
69
        412 => 'Precondition Failed',
70
        413 => 'Request Entity Too Large',
71
        414 => 'Request-URI Too Long',
72
        415 => 'Unsupported Media Type',
73
        416 => 'Requested Range Not Satisfiable',
74
        417 => 'Expectation Failed',
75
        422 => 'Unprocessable Entity',
76
        423 => 'Locked',
77
        424 => 'Failed Dependency',
78
        425 => 'Unordered Collection',
79
        426 => 'Upgrade Required',
80
        428 => 'Precondition Required',
81
        429 => 'Too Many Requests',
82
        431 => 'Request Header Fields Too Large',
83
        444 => 'No Response',
84
        449 => 'Retry With',
85
        450 => 'Blocked by Windows Parental Controls',
86
        494 => 'Request Header Too Large',
87
        495 => 'Cert Error',
88
        496 => 'No Cert',
89
        497 => 'HTTP to HTTPS',
90
        499 => 'Client Closed Request',
91
        500 => 'Internal Server Error',
92
        501 => 'Not Implemented',
93
        502 => 'Bad Gateway',
94
        503 => 'Service Unavailable',
95
        504 => 'Gateway Timeout',
96
        505 => 'HTTP Version Not Supported',
97
        506 => 'Variant Also Negotiates',
98
        507 => 'Insufficient Storage',
99
        508 => 'Loop Detected',
100
        509 => 'Bandwidth Limit Exceeded',
101
        510 => 'Not Extended',
102
        511 => 'Network Authentication Required'
103
    ];
104
105
    private $httpCode;
106
    private $headers = [];
107
    private $requestUri = '';
108
    private $remoteIp;
109
    private $url;
110
    private $body;
111
    private $path;
112
    private $query;
113
114
    /**
115
     * Request constructor
116
     */
117
    public function __construct()
118
    {
119
        $this->headers = [];
120
        $this->httpCode = 200;
121
    }
122
123
    /**
124
     * Parse server request
125
     *
126
     * @return void
127
     *
128
     * @SuppressWarnings(PHPMD).Superglobals
129
     */
130
    public function parse()
131
    {
132
        if (isset($_SERVER['REQUEST_URI'])) {
133
            $this->setRequestUri($_SERVER['REQUEST_URI']);
134
            $parseResult = parse_url($_SERVER['REQUEST_URI']);
135
            $this->path = dataGet($parseResult, 'path');
136
            $this->query = dataGet($parseResult, 'query');
137
        }
138
139
        if (isset($_SERVER['REQUEST_METHOD'])) {
140
            $this->setMethod($_SERVER['REQUEST_METHOD']);
141
        }
142
        if (isset($_POST['_method'])) {
143
            $this->setMethod($_POST['_method']);
144
        }
145
146
        $this->parseRemoteIp();
147
    }
148
149
    /**
150
     * Parse request and extract remote IP
151
     *
152
     * @return void
153
     *
154
     * @SuppressWarnings(PHPMD).Superglobals
155
     */
156
    private function parseRemoteIp()
157
    {
158
        // FIXME: check for trusted_proxy and void forged header
159
        if (
160
            array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER) &&
161
            !empty($_SERVER['HTTP_X_FORWARDED_FOR'])
162
        ) {
163
            if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') > 0) {
164
                $addr = explode(",", $_SERVER['HTTP_X_FORWARDED_FOR']);
165
                $this->remoteIp = trim($addr[0]);
166
                return;
167
            }
168
            $this->remoteIp = $_SERVER['HTTP_X_FORWARDED_FOR'];
169
            return;
170
        }
171
172
        $this->remoteIp = $_SERVER['REMOTE_ADDR'];
173
        return;
174
    }
175
176
    /**
177
     * Set Request method
178
     *
179
     * @param string $method Request method
180
     * @return Request
181
     *
182
     * @throws InvalidArgumentException
183
     */
184
    public function setMethod($method): Request
185
    {
186
        if (!isset($this->methods[$method])) {
187
            throw new InvalidArgumentException(
188
                'Invalid HTTP Method ' . $method
189
            );
190
        }
191
192
        $this->method = $method;
193
194
        return $this;
195
    }
196
197
    /**
198
     * Get Request method
199
     *
200
     * @return string
201
     */
202
    public function getMethod(): string
203
    {
204
        return $this->method;
205
    }
206
207
    /**
208
     * Set Request URL
209
     *
210
     * @param string $url
211
     *
212
     * @return Request
213
     */
214
    public function setUrl($url): Request
215
    {
216
        $this->url = $url;
217
218
        return $this;
219
    }
220
221
    /**
222
     * Get Request URL
223
     *
224
     * @return string|null
225
     */
226
    public function getUrl(): ?string
227
    {
228
        return $this->url;
229
    }
230
231
    /**
232
     * Get Remote IP Address
233
     *
234
     * @return string|null
235
     */
236
    public function getRemoteIp(): ?string
237
    {
238
        return $this->remoteIp;
239
    }
240
241
    public function setRequestUri($uri)
242
    {
243
        $this->requestUri = $uri;
244
245
        return $this;
246
    }
247
248
    public function getRequestUri()
249
    {
250
        return $this->requestUri;
251
    }
252
253
    public function getPath()
254
    {
255
        return $this->path;
256
    }
257
258
    public function getQuery()
259
    {
260
        return $this->query;
261
    }
262
263
    /**
264
     * Get POST parameter
265
     *
266
     * @param string $variable     Parameter name
267
     * @param mixed  $defaultValue Fallback value when parameter not set
268
     *
269
     * @return mixed
270
     *
271
     * @SuppressWarnings(PHPMD).Superglobals
272
     */
273
    public static function getPostParam($variable, $defaultValue = null)
274
    {
275
        if (array_key_exists($variable, $_POST)) {
276
            return $_POST[$variable];
277
        }
278
        return $defaultValue;
279
    }
280
281
    /**
282
     * Get Request parameter, GET first, then POST
283
     *
284
     * @param string $variable     Parameter name
285
     * @param mixed  $defaultValue Fallback value when parameter not set
286
     *
287
     * @return mixed
288
     *
289
     * @SuppressWarnings(PHPMD).Superglobals
290
     */
291
    public static function getParam($variable, $defaultValue = null)
292
    {
293
        if (array_key_exists($variable, $_GET)) {
294
            return $_GET[$variable];
295
        }
296
        if (array_key_exists($variable, $_POST)) {
297
            return $_POST[$variable];
298
        }
299
300
        return $defaultValue;
301
    }
302
303
    /**
304
     * Check if parameter exists in Request
305
     *
306
     * @param string $variable parameter name
307
     *
308
     * @return boolean
309
     *
310
     * @SuppressWarnings(PHPMD).Superglobals
311
     */
312
    public static function hasParam($variable): bool
313
    {
314
        return array_key_exists($variable, $_GET) ||
315
            array_key_exists($variable, $_POST);
316
    }
317
318
    /**
319
     * Set request headers
320
     *
321
     * @param array $headers Headers to set key => $value
322
     *
323
     * @return Request
324
     */
325
    public function setHeaders(array $headers): Request
326
    {
327
        $this->headers = $headers;
328
329
        return $this;
330
    }
331
332
    /**
333
     * Add specific header
334
     *
335
     * @param string $header header name
336
     * @param string $value  header value
337
     * @return Request
338
     */
339
    public function addHeader($header, $value): Request
340
    {
341
        $this->headers[$header] = $value;
342
343
        return $this;
344
    }
345
346
    /**
347
     * Get request headers
348
     *
349
     * @return array
350
     */
351
    public function getHeaders(): array
352
    {
353
        return $this->headers;
354
    }
355
356
    public function setContentType(
357
        string $contentType,
358
        $encoding = null
359
    ): Request {
360
        if ($encoding !== null) {
361
            $contentType .= '; charset=' . $encoding;
362
        }
363
        $this->addHeader('Content-type', $contentType);
364
365
        return $this;
366
    }
367
368
    public function setBody(string $body): Request
369
    {
370
        $this->body = $body;
371
372
        return $this;
373
    }
374
375
    public function getBody(): string
376
    {
377
        return $this->body;
378
    }
379
380
    public function write()
381
    {
382
        if (!headers_sent()) {
383
            if (substr(php_sapi_name(), 0, 3) == 'cgi') {
384
                $headerString = 'Status: ' . $this->getStringForHttpCode();
385
            } else {
386
                $headerString = 'HTTP/1.1 ' . $this->getStringForHttpCode();
387
            }
388
389
            header($headerString);
390
391
            // Send headers
392
            foreach ($this->headers as $headerName => $headerValue) {
393
                header($headerName . ':' . $headerValue);
394
            }
395
        }
396
397
        /**
398
         TODO HANDLE HTTP RESPONSE CODE
399
         */
400
        if ($this->httpCode !== null) {
401
        }
402
403
        // Send body
404
        echo $this->body;
405
    }
406
407
    //
408
    // HTTP Code
409
    //
410
411
    public function setHttpCode($code): Request
412
    {
413
        $this->httpCode = $code;
414
415
        return $this;
416
    }
417
418
    public function getHttpCode()
419
    {
420
        return $this->httpCode;
421
    }
422
423
    /**
424
     * Flash message
425
     *
426
     * @param string $type message type
427
     * @param array|string $data data to be flashed
428
     */
429
    public function flash(string $type, $data)
430
    {
431
        Flash::writeMessage($type, $data);
432
433
        return $this;
434
    }
435
436
    public function flashData($name, $value)
437
    {
438
        Flash::writeData($name, $value);
439
440
        return $this;
441
    }
442
443
    public function redirect($url, $httpCode = 302)
444
    {
445
        $this->setHttpCode($httpCode);
446
        $this->addHeader('Location', $url);
447
448
        $this->write();
449
        die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
450
    }
451
452
    public function redirectWithSuccess($url, $message)
453
    {
454
        $this->flash('success', $message)->redirect($url);
455
    }
456
457
    public function redirectWithInfo($url, $message)
458
    {
459
        $this->flash('info', $message)->redirect($url);
460
    }
461
462
    public function redirectWithError($url, $message)
463
    {
464
        $this->flash('error', $message)->redirect($url);
465
    }
466
467
    public function redirectWithData($url, $key, $value)
468
    {
469
        $this->flashData($key, $value)->redirect($url);
470
    }
471
472
    /**
473
     * Check if request has a 200 OK Code
474
     *
475
     * @return boolean
476
     */
477
    public function isOK(): bool
478
    {
479
        return $this->httpCode == 200;
480
    }
481
482
    /**
483
     * Check if request has a 3XX HTTP code
484
     *
485
     * @return boolean
486
     */
487
    public function isRedirect(): bool
488
    {
489
        return $this->httpCode >= 300 && $this->httpCode < 400;
490
    }
491
492
    /**
493
     * Check is request has a 4XX HTTP code
494
     *
495
     * @return boolean
496
     */
497
    public function isClientError(): bool
498
    {
499
        return $this->httpCode >= 400 && $this->httpCode < 500;
500
    }
501
502
    /**
503
     * Check if request has a 5XX HTTP code
504
     *
505
     * @return boolean
506
     */
507
    public function isServerError(): bool
508
    {
509
        return $this->httpCode >= 500 && $this->httpCode < 600;
510
    }
511
512
    /**
513
     * Get string correspondig to HTTP code
514
     *
515
     * @return string|null
516
     */
517
    private function getStringForHttpCode(): ?string
518
    {
519
        if (isset($this->httpCodeString[$this->httpCode])) {
520
            return $this->httpCode .
521
                ' ' .
522
                $this->httpCodeString[$this->httpCode];
523
        }
524
525
        return null;
526
    }
527
}
528