Completed
Push — master ( 78c175...379bc2 )
by Arman
03:11 queued 10s
created

HttpRequest::isMethod()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
/**
4
 * Quantum PHP Framework
5
 *
6
 * An open source software development framework for PHP
7
 *
8
 * @package Quantum
9
 * @author Arman Ag. <[email protected]>
10
 * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
11
 * @link http://quantum.softberg.org/
12
 * @since 2.0.0
13
 */
14
15
namespace Quantum\Http;
16
17
use Quantum\Exceptions\ExceptionMessages;
18
use Quantum\Exceptions\RequestException;
19
use Quantum\Environment\Server;
20
use Quantum\Bootstrap;
21
22
/**
23
 * Class HttpRequest
24
 * @package Quantum\Http
25
 */
26
abstract class HttpRequest
27
{
28
29
    /**
30
     * Request headers
31
     * @var array
32
     */
33
    private static $__headers = [];
34
35
    /**
36
     * Request body
37
     * @var array
38
     */
39
    private static $__request = [];
40
41
    /**
42
     * Files
43
     * @var array 
44
     */
45
    private static $__files = [];
46
47
    /**
48
     * Request method
49
     * @var string
50
     */
51
    private static $__method = null;
52
53
    /**
54
     * Scheme
55
     * @var string 
56
     */
57
    private static $__protocol = null;
58
59
    /**
60
     * Host name
61
     * @var string 
62
     */
63
    private static $__host = null;
64
65
    /**
66
     * Server port
67
     * @var string 
68
     */
69
    private static $__port = null;
70
71
    /**
72
     * Request URI
73
     * @var string
74
     */
75
    private static $__uri = null;
76
77
    /**
78
     * Query string
79
     * @var string
80
     */
81
    private static $__query = null;
82
83
    /**
84
     * Available methods
85
     * @var array 
86
     */
87
    private static $availableMetods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
88
89
    /**
90
     * Server
91
     * @var \Quantum\Environment\Server 
92
     */
93
    private static $server;
94
95
    /**
96
     * Initialize the Request
97
     * @throws RequestException When called outside of Bootstrap
98
     */
99
    public static function init(Server $server)
100
    {
101
        if (get_caller_class() !== Bootstrap::class) {
102
            throw new RequestException(ExceptionMessages::UNEXPECTED_REQUEST_INITIALIZATION);
103
        }
104
105
        self::$server = $server;
106
107
        self::$__method = self::$server->method();
108
109
        self::$__protocol = self::$server->protocol();
110
111
        self::$__host = self::$server->host();
112
113
        self::$__port = self::$server->port();
114
115
        self::$__uri = self::$server->uri();
116
117
        self::$__query = self::$server->query();
118
119
        self::$__headers = array_change_key_case((array) getallheaders(), CASE_LOWER);
120
121
        self::$__request = array_merge(
122
                self::$__request,
123
                self::getParams(),
124
                self::postParams(),
125
                self::getRawInputs()
126
        );
127
128
        self::$__files = self::handleFiles($_FILES);
129
    }
130
131
    /**
132
     * Creates new request for internal use
133
     * @param string $method
134
     * @param string $url
135
     * @param array|null $file
136
     */
137
    public static function create(string $method, string $url, array $data = null, array $file = null)
138
    {
139
        $parsed = parse_url($url);
140
141
        self::setMethod($method);
142
143
        if (isset($parsed['scheme'])) {
144
            self::setProtocol($parsed['scheme']);
145
        }
146
147
        if (isset($parsed['host'])) {
148
            self::setHost($parsed['host']);
149
        }
150
151
        if (isset($parsed['port'])) {
152
            self::setPort($parsed['port']);
153
        }
154
        if (isset($parsed['path'])) {
155
            self::setUri($parsed['path']);
156
        }
157
        if (isset($parsed['query'])) {
158
            self::setQuery($parsed['query']);
159
        }
160
161
        if ($data) {
162
            self::$__request = $data;
163
        }
164
165
        if ($file) {
166
            self::$__files = self::handleFiles($file);
167
        }
168
    }
169
170
    /**
171
     * Flushes the request header , body and files
172
     */
173
    public static function flush()
174
    {
175
        self::$__headers = [];
176
        self::$__request = [];
177
        self::$__files = [];
178
        self::$__protocol = null;
179
        self::$__host = null;
180
        self::$__port = null;
181
        self::$__uri = null;
182
        self::$__query = null;
183
    }
184
185
    /**
186
     * Gets the request method
187
     * @return mixed
188
     */
189
    public static function getMethod()
190
    {
191
        return self::$__method;
192
    }
193
194
    /**
195
     * Check the request method
196
     * @param string $method
197
     * @return boolean
198
     */
199
    public static function isMethod(string $method)
200
    {
201
        return strcasecmp($method, self::$__method) == 0;
202
    }
203
204
    /**
205
     * Sets the request method
206
     * @param string $method
207
     * @throws RequestException
208
     */
209
    public static function setMethod(string $method)
210
    {
211
        if (!in_array($method, self::$availableMetods)) {
212
            throw new RequestException();
213
        }
214
215
        self::$__method = $method;
216
    }
217
218
    /**
219
     * Gets the protocol
220
     * @return string
221
     */
222
    public static function getProtocol()
223
    {
224
        return self::$__protocol;
225
    }
226
227
    /**
228
     * Sets the protocol
229
     * @param string $protocol
230
     */
231
    public static function setProtocol(string $protocol)
232
    {
233
        self::$__protocol = $protocol;
234
    }
235
236
    /**
237
     * Gets the host name
238
     * @return string
239
     */
240
    public static function getHost()
241
    {
242
        return self::$__host;
243
    }
244
245
    /**
246
     * Sets the host name
247
     * @param string $host
248
     */
249
    public static function setHost(string $host)
250
    {   
251
        self::$__host = $host;
252
    }
253
254
    /**
255
     * Gets the port
256
     * @return string
257
     */
258
    public static function getPort()
259
    {
260
        return self::$__port;
261
    }
262
263
    /**
264
     * Sets the port
265
     * @param string $port
266
     */
267
    public static function setPort($port)
268
    {
269
        self::$__port = $port;
270
    }
271
272
    /**
273
     * Gets the URI
274
     * @return string|null
275
     */
276
    public static function getUri()
277
    {
278
        return self::$__uri;
279
    }
280
281
    /**
282
     * Sets the URI
283
     * @param string $uri
284
     */
285
    public static function setUri(string $uri)
286
    {
287
        self::$__uri = ltrim($uri, '/');
288
    }
289
290
    /**
291
     * Gets the query string
292
     * @return string
293
     */
294
    public static function getQuery()
295
    {
296
        return self::$__query;
297
    }
298
299
    /**
300
     * Sets the query string
301
     * @param string $query
302
     */
303
    public static function setQuery(string $query)
304
    {
305
        self::$__query = $query;
306
    }
307
308
    /**
309
     * Sets the query param
310
     * @param string $key
311
     * @param string $value
312
     * @return string
313
     */
314
    public static function setQueryParam(string $key, string $value)
315
    {
316
        $queryParams = self::$__query ? explode('&', self::$__query) : []; 
317
        array_push($queryParams, $key . '=' . $value);  
318
        self::$__query =  implode('&', $queryParams);   
319
    }
320
321
    /**
322
     * Gets the query param
323
     * @param string $key
324
     * @return string|null
325
     */
326
    public static function getQueryParam(string $key)
327
    {  
328
        $query = explode('&', self::$__query);    
329
330
        foreach($query as $items){
331
           $item = explode('=', $items); 
332
           if($item[0] == $key){
333
                return $item[1];
334
           }
335
        }
336
337
        return null;
338
    }
339
340
    /**
341
     * Sets new key/value pair into request
342
     * @param string $key
343
     * @param mixed $value
344
     */
345
    public static function set($key, $value)
346
    {
347
        self::$__request[$key] = $value;
348
    }
349
350
    /**
351
     * Checks if request contains a data by given key
352
     * @param string $key
353
     * @return bool
354
     */
355
    public static function has($key)
356
    {
357
        return isset(self::$__request[$key]);
358
    }
359
360
    /**
361
     * Retrieves data from request by given key
362
     * @param string $key
363
     * @param string $default
364
     * @param bool $raw
365
     * @return mixed
366
     */
367
    public static function get($key, $default = null, $raw = false)
368
    {
369
        $data = $default;
370
371
        if (self::has($key)) {
372
            if ($raw) {
373
                $data = self::$__request[$key];
374
            } else {
375
                $data = is_array(self::$__request[$key]) ?
376
                        filter_var_array(self::$__request[$key], FILTER_SANITIZE_STRING) :
377
                        filter_var(self::$__request[$key], FILTER_SANITIZE_STRING);
378
            }
379
        }
380
381
        return $data;
382
    }
383
384
    /**
385
     * Gets all request parameters
386
     * @return array
387
     */
388
    public static function all()
389
    {
390
        return array_merge(self::$__request, self::$__files);
391
    }
392
393
    /**
394
     * Deletes the element from request by given key
395
     * @param string $key
396
     */
397
    public static function delete($key)
398
    {
399
        if (self::has($key)) {
400
            unset(self::$__request[$key]);
401
        }
402
    }
403
404
    /**
405
     * Checks to see if request contains file
406
     * @return bool
407
     */
408
    public static function hasFile($key)
409
    {
410
        return isset(self::$__files[$key]);
411
    }
412
413
    /**
414
     * Gets the file info by given key
415
     * @param string $key
416
     * @return array
417
     * @throws \InvalidArgumentException
418
     */
419
    public static function getFile($key)
420
    {
421
        if (!self::hasFile($key)) {
422
            throw new \InvalidArgumentException(_message(ExceptionMessages::UPLOADED_FILE_NOT_FOUND, $key));
423
        }
424
425
        return self::$__files[$key];
426
    }
427
428
    /**
429
     * Sets the request header
430
     * @param string $key
431
     * @param mixed $value
432
     */
433
    public static function setHeader($key, $value)
434
    {
435
        self::$__headers[strtolower($key)] = $value;
436
    }
437
438
    /**
439
     * Checks the request header existence by given key
440
     * @param string $key
441
     * @return bool
442
     */
443
    public static function hasHeader($key)
444
    {
445
        return isset(self::$__headers[strtolower($key)]);
446
    }
447
448
    /**
449
     * Gets the request header by given key
450
     * @param $key
451
     * @return mixed|null
452
     */
453
    public static function getHeader($key)
454
    {
455
        return self::hasHeader($key) ? self::$__headers[strtolower($key)] : null;
456
    }
457
458
    /**
459
     * Gets all request headers
460
     * @return array
461
     */
462
    public static function allHeaders()
463
    {
464
        return self::$__headers;
465
    }
466
467
    /**
468
     * Deletes the header by given key
469
     * @param string $key
470
     */
471
    public static function deleteHeader($key)
472
    {
473
        if (self::hasHeader($key)) {
474
            unset(self::$__headers[strtolower($key)]);
475
        }
476
    }
477
478
    /**
479
     * Gets the nth segment
480
     * @param integer $number
481
     * @return string|null
482
     */
483
    public static function getSegment($number)
484
    {
485
        $segments = self::getAllSegments();
486
487
        if (isset($segments[$number])) {
488
            return $segments[$number];
489
        }
490
491
        return null;
492
    }
493
494
    /**
495
     * Gets the segments of current URI
496
     * @return array
497
     */
498
    public static function getAllSegments()
499
    {
500
        $segments = explode('/', trim(parse_url(self::$__uri)['path'], '/'));
501
        array_unshift($segments, 'zero_segment');
502
        return $segments;
503
    }
504
505
    /**
506
     * Gets Сross Site Request Forgery Token
507
     * @return string
508
     */
509
    public static function getCSRFToken()
510
    {
511
        $csrfToken = null;
512
513
        if (self::has('token')) {
514
            $csrfToken = self::get('token');
515
        } elseif (self::hasHeader('X-csrf-token')) {
516
            $csrfToken = self::getHeader('X-csrf-token');
517
        }
518
519
        return $csrfToken;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $csrfToken also could return the type array which is incompatible with the documented return type string.
Loading history...
520
    }
521
522
    /**
523
     * Gets Authorization Bearer token
524
     * @return string
525
     */
526
    public static function getAuthorizationBearer()
527
    {
528
        $bearerToken = null;
529
530
        if (self::hasHeader('Authorization')) { 
531
            if (preg_match('/Bearer\s(\S+)/', self::getHeader('Authorization'), $matches)) {
0 ignored issues
show
Bug introduced by
It seems like self::getHeader('Authorization') can also be of type null; however, parameter $subject of preg_match() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

531
            if (preg_match('/Bearer\s(\S+)/', /** @scrutinizer ignore-type */ self::getHeader('Authorization'), $matches)) {
Loading history...
532
                $bearerToken = $matches[1];
533
            }
534
        }
535
536
        return $bearerToken;
537
    }
538
539
    /**
540
     * Checks to see if request was AJAX request
541
     * @return boolean
542
     */
543
    public static function isAjax()
544
    {
545
        if (self::hasHeader('X-REQUESTED-WITH') || self::$server->ajax()) {
546
            return true;
547
        }
548
549
        return false;
550
    }
551
552
    /**
553
     * Gets the referrer
554
     * @return string
555
     */
556
    public static function getReferrer()
557
    {
558
        return self::$server->referrer();
559
    }
560
561
    /**
562
     * Gets the GET params
563
     * @return array
564
     */
565
    private static function getParams(): array
566
    {
567
        $getParams = [];
568
569
        if (!empty($_GET)) {
570
            $getParams = filter_input_array(INPUT_GET, FILTER_DEFAULT);
571
        }
572
573
        return $getParams;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $getParams could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
574
    }
575
576
    /**
577
     * Gets the POST params
578
     * @return array
579
     */
580
    private static function postParams(): array
581
    {
582
        $postParams = [];
583
584
        if (!empty($_POST)) {
585
            $postParams = filter_input_array(INPUT_POST, FILTER_DEFAULT);
586
        }
587
588
        return $postParams;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $postParams could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
589
    }
590
591
    /**
592
     * Get Input parameters sent via PUT, PATCH or DELETE methods
593
     * @return array
594
     */
595
    private static function getRawInputs(): array
596
    {
597
        $inputParams = [];
598
599
        if (in_array(self::$__method, ['PUT', 'PATCH', 'DELETE'])) {
600
601
            $input = file_get_contents('php://input');
602
603
            if (self::$server->contentType()) {
604
                switch (self::$server->contentType()) {
605
                    case 'application/x-www-form-urlencoded':
606
                        parse_str($input, $inputParams);
607
                        break;
608
                    case 'application/json':
609
                        $inputParams = json_decode($input);
610
                        break;
611
                    default :
612
                        $inputParams = parse_raw_http_request($input);
613
                        break;
614
                }
615
            }
616
        }
617
618
        return (array) $inputParams;
619
    }
620
621
    /**
622
     * Handles uploaded files
623
     */
624
    private static function handleFiles($_files)
625
    {
626
        if (!count($_files)) {
627
            return [];
628
        }
629
630
        $key = key($_files);
631
632
        if ($key) {
633
            if (!is_array($_files[$key]['name'])) {
634
                return [$key => (object) $_files[$key]];
635
            } else {
636
                $formattedFiles = [];
637
638
                foreach ($_files[$key]['name'] as $index => $name) {
639
                    $formattedFiles[$key][$index] = (object) [
640
                                'name' => $name,
641
                                'type' => $_files[$key]['type'][$index],
642
                                'tmp_name' => $_files[$key]['tmp_name'][$index],
643
                                'error' => $_files[$key]['error'][$index],
644
                                'size' => $_files[$key]['size'][$index],
645
                    ];
646
                }
647
648
                return $formattedFiles;
649
            }
650
        }
651
    }
652
653
}
654