Passed
Pull Request — master (#30)
by Maximo
03:26
created

SwooleRequest::getMethod()   B

Complexity

Conditions 7
Paths 11

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
cc 7
eloc 14
nc 11
nop 0
dl 0
loc 24
ccs 0
cts 21
cp 0
crap 56
rs 8.8333
c 0
b 0
f 0
1
<?php
2
// +----------------------------------------------------------------------
3
// | SwooleRequest.php [ WE CAN DO IT JUST THINK IT ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2016-2017 limingxinleo All rights reserved.
6
// +----------------------------------------------------------------------
7
// | Author: limx <[email protected]> <https://github.com/limingxinleo>
8
// +----------------------------------------------------------------------
9
10
namespace Gewaer\Http;
11
12
use Phalcon\DiInterface;
13
use Phalcon\Events\Manager;
14
use Phalcon\FilterInterface;
15
use Phalcon\Http\RequestInterface;
16
use Phalcon\Di\InjectionAwareInterface;
17
use Phalcon\Http\Request\File;
18
use Phalcon\Text;
19
use swoole_http_request;
20
use Exception;
21
use Phalcon\Di\FactoryDefault;
22
use function Gewaer\Core\isJson;
23
24
/**
25
 * Class SwooleRequest
26
 *
27
 * To use Swoole Server with Phalcon we need to overwrite the Phalcon Request Object to use swoole Respnose object
28
 * Since swoole is our server he is the one who get all our _GET , _FILES, _POST , _PUT request and we need to parse that info
29
 * to make our phalcon project work
30
 *
31
 * @package Gewaer\Http
32
 *
33
 * @property \Phalcon\Di $di
34
 */
35
class SwooleRequest implements RequestInterface, InjectionAwareInterface
36
{
37
    protected $_dependencyInjector;
38
39
    protected $_httpMethodParameterOverride = false;
40
41
    protected $_filter;
42
43
    protected $_putCache;
44
45
    protected $_strictHostCheck = false;
46
47
    protected $_files;
48
49
    protected $_rawBody;
50
51
    protected $headers;
52
53
    protected $server;
54
55
    protected $get;
56
57
    protected $post;
58
59
    protected $cookies;
60
61
    protected $files;
62
63
    protected $swooleRequest;
64
65
    /**
66
     * Init the object with Swoole reqeust
67
     *
68
     * @param swoole_http_request $request
69
     * @return void
70
     */
71
    public function init(swoole_http_request $request): void
72
    {
73
        $this->swooleRequest = $request;
74
        $this->headers = [];
75
        $this->server = [];
76
77
        $this->get = isset($request->get) ? $request->get : [];
78
        $this->post = isset($request->post) ? $request->post : [];
79
        $this->cookies = isset($request->cookie) ? $request->cookie : [];
80
        $this->files = isset($request->files) ? $request->files : [];
81
        $this->_rawBody = $request->rawContent();
82
83
        //iterate header
84
        $this->setGlobalHeaders($request->header);
85
        $this->setGlobalServers($request->server);
86
87
        //iterate server
88
89
        /** @var Cookies $cookies */
90
        //$cookies = FactoryDefault::getDefault()->getCookies();
91
        //  $cookies->setSwooleCookies($this->cookies);
92
    }
93
94
    /**
95
     * Set global headers
96
     *
97
     * @param array $headers
98
     * @return void
99
     */
100
    private function setGlobalHeaders(array $headers): void
101
    {
102
        foreach ($headers as $key => $val) {
103
            $key = strtoupper(str_replace(['-'], '_', $key));
104
            $this->headers[$key] = $val;
105
            $this->server[$key] = $val;
106
        }
107
    }
108
109
    /**
110
     * Set global Servers
111
     *
112
     * @param array $servers
113
     * @return void
114
     */
115
    private function setGlobalServers(array $servers): void
116
    {
117
        foreach ($servers as $key => $val) {
118
            $key = strtoupper(str_replace(['-'], '_', $key));
119
            $this->server[$key] = $val;
120
        }
121
    }
122
123
    /**
124
     * Set Di
125
     *
126
     * @param DiInterface $dependencyInjector
127
     * @return void
128
     */
129
    public function setDI(DiInterface $dependencyInjector)
130
    {
131
        $this->_dependencyInjector = $dependencyInjector;
132
    }
133
134
    /**
135
     * Get Di
136
     *
137
     * @return void
138
     */
139
    public function getDI()
140
    {
141
        return $this->_dependencyInjector;
142
    }
143
144
    /**
145
     * Access to REQUEST
146
     *
147
     * @param string $name
148
     * @param string $filters
149
     * @param string $defaultValue
150
     * @param boolean $notAllowEmpty
151
     * @param boolean $noRecursive
152
     * @return array|string
153
     */
154
    public function get($name = null, $filters = null, $defaultValue = null, $notAllowEmpty = false, $noRecursive = false)
155
    {
156
        $source = array_merge($this->get, $this->post);
157
        return $this->getHelper($source, $name, $filters, $defaultValue, $notAllowEmpty, $noRecursive);
158
    }
159
160
    /**
161
     * Acces to Post
162
     *
163
     * @param string $name
164
     * @param string $filters
165
     * @param string $defaultValue
166
     * @param boolean $notAllowEmpty
167
     * @param boolean $noRecursive
168
     * @return array|string
169
     */
170
    public function getPost($name = null, $filters = null, $defaultValue = null, $notAllowEmpty = false, $noRecursive = false)
171
    {
172
        $source = $this->post;
173
        return $this->getHelper($source, $name, $filters, $defaultValue, $notAllowEmpty, $noRecursive);
174
    }
175
176
    /**
177
     * Access to GET
178
     *
179
     * @param string $name
180
     * @param string $filters
181
     * @param string $defaultValue
182
     * @param boolean $notAllowEmpty
183
     * @param boolean $noRecursive
184
     * @return array|string
185
     */
186
    public function getQuery($name = null, $filters = null, $defaultValue = null, $notAllowEmpty = false, $noRecursive = false)
187
    {
188
        $source = $this->get;
189
        return $this->getHelper($source, $name, $filters, $defaultValue, $notAllowEmpty, $noRecursive);
190
    }
191
192
    /**
193
     * Get _SERVER
194
     *
195
     * @param string $name
196
     * @return string|null
197
     */
198
    public function getServer($name)
199
    {
200
        $name = strtoupper(str_replace(['-'], '_', $name));
201
        if (isset($this->server[$name])) {
202
            return $this->server[$name];
203
        }
204
205
        return null;
206
    }
207
208
    /**
209
     * Get _PUT
210
     *
211
     * @param string $name
212
     * @param string $filters
213
     * @param string $defaultValue
214
     * @param boolean $notAllowEmpty
215
     * @param boolean $noRecursive
216
     * @return array|string
217
     */
218
    public function getPut($name = null, $filters = null, $defaultValue = null, $notAllowEmpty = false, $noRecursive = false)
219
    {
220
        $put = $this->_putCache;
221
222
        if (empty($put)) {
223
            if (!isJson($this->getRawBody())) {
224
                parse_str($this->getRawBody(), $put);
225
            } else {
226
                $put = $this->getJsonRawBody(true);
227
            }
228
            $this->_putCache = $put;
229
        }
230
231
        return $this->getHelper($put, $name, $filters, $defaultValue, $notAllowEmpty, $noRecursive);
232
    }
233
234
    /**
235
     * Has
236
     *
237
     * @param string $name
238
     * @return boolean
239
     */
240
    public function has($name)
241
    {
242
        $source = array_merge($this->get, $this->post);
243
        return isset($source[$name]);
244
    }
245
246
    /**
247
     * Has Post
248
     *
249
     * @param string $name
250
     * @return boolean
251
     */
252
    public function hasPost($name)
253
    {
254
        return isset($this->post[$name]);
255
    }
256
257
    /**
258
     * Has Put
259
     *
260
     * @param string $name
261
     * @return boolean
262
     */
263
    public function hasPut($name)
264
    {
265
        $put = $this->getPut();
266
267
        return isset($put[$name]);
268
    }
269
270
    /**
271
     * Has GET
272
     *
273
     * @param string $name
274
     * @return boolean
275
     */
276
    public function hasQuery($name)
277
    {
278
        return isset($this->get[$name]);
279
    }
280
281
    /**
282
     * Has SERVER
283
     *
284
     * @param string $name
285
     * @return boolean
286
     */
287
    public function hasServer($name)
288
    {
289
        $name = strtoupper(str_replace(['-'], '_', $name));
290
291
        return isset($this->server[$name]);
292
    }
293
294
    /**
295
     * Has HEADER
296
     *
297
     * @param string $name
298
     * @return boolean
299
     */
300
    public function hasHeader($header)
301
    {
302
        if ($this->hasServer($header)) {
303
            return true;
304
        }
305
        if ($this->hasServer('HTTP_' . $header)) {
306
            return true;
307
        }
308
        return false;
309
    }
310
311
    /**
312
     * Get Header
313
     *
314
     * @param string $name
315
     * @return string|void
316
     */
317
    public function getHeader($header)
318
    {
319
        $header = $this->getServer($header);
320
        if (isset($header)) {
321
            return $header;
322
        }
323
324
        $header = $this->getServer('HTTP_' . $header);
325
        if (isset($header)) {
326
            return $header;
327
        }
328
329
        return '';
330
    }
331
332
    /**
333
     * Get Schema
334
     *
335
     * @return string
336
     */
337
    public function getScheme()
338
    {
339
        $https = $this->getServer('HTTPS');
340
        if ($https && $https != 'off') {
341
            return 'https';
342
        }
343
344
        return 'http';
345
    }
346
347
    /**
348
     * Is ajax
349
     *
350
     * @return boolean
351
     */
352
    public function isAjax()
353
    {
354
        return $this->getServer('HTTP_X_REQUESTED_WITH') === 'XMLHttpRequest';
355
    }
356
357
    /**
358
     * is Soap
359
     *
360
     * @return boolean
361
     */
362
    public function isSoap()
363
    {
364
        if ($this->hasServer('HTTP_SOAPACTION')) {
365
            return true;
366
        }
367
368
        $contentType = $this->getContentType();
369
        if (!empty($contentType)) {
370
            return (bool) strpos($contentType, 'application/soap+xml') !== false;
371
        }
372
373
        return false;
374
    }
375
376
    /**
377
     * is Soap
378
     *
379
     * @return boolean
380
     */
381
    public function isSoapRequested()
382
    {
383
        return $this->isSoap();
384
    }
385
386
    /**
387
     * is HTTPS
388
     *
389
     * @return boolean
390
     */
391
    public function isSecure()
392
    {
393
        return $this->getScheme() === 'https';
394
    }
395
396
    /**
397
     * is HTTPS
398
     *
399
     * @return boolean
400
     */
401
    public function isSecureRequest()
402
    {
403
        return $this->isSecure();
404
    }
405
406
    /**
407
     * get RAW
408
     *
409
     * @return string
410
     */
411
    public function getRawBody()
412
    {
413
        return $this->_rawBody;
414
    }
415
416
    /**
417
     * Get json
418
     *
419
     * @param boolean $associative
420
     * @return void|string
421
     */
422
    public function getJsonRawBody($associative = false)
423
    {
424
        $rawBody = $this->getRawBody();
425
        if (!is_string($rawBody)) {
426
            return false;
427
        }
428
429
        return json_decode($rawBody, $associative);
430
    }
431
432
    /**
433
     * Get servers addres
434
     *
435
     * @return string
436
     */
437
    public function getServerAddress()
438
    {
439
        $serverAddr = $this->getServer('SERVER_ADDR');
440
        if ($serverAddr) {
441
            return $serverAddr;
442
        }
443
444
        return gethostbyname('localhost');
445
    }
446
447
    /**
448
     * Get server name
449
     *
450
     * @return string
451
     */
452
    public function getServerName()
453
    {
454
        $serverName = $this->getServer('SERVER_NAME');
455
        if ($serverName) {
456
            return $serverName;
457
        }
458
459
        return 'localhost';
460
    }
461
462
    /**
463
     * Get https hosts
464
     *
465
     * @return string
466
     */
467
    public function getHttpHost()
468
    {
469
        $strict = $this->_strictHostCheck;
470
471
        /**
472
         * Get the server name from $_SERVER["HTTP_HOST"]
473
         */
474
        $host = $this->getServer('HTTP_HOST');
475
        if (!$host) {
476
            /**
477
             * Get the server name from $_SERVER["SERVER_NAME"]
478
             */
479
            $host = $this->getServer('SERVER_NAME');
480
            if (!$host) {
481
                /**
482
                 * Get the server address from $_SERVER["SERVER_ADDR"]
483
                 */
484
                $host = $this->getServer('SERVER_ADDR');
485
            }
486
        }
487
488
        if ($host && $strict) {
489
            /**
490
             * Cleanup. Force lowercase as per RFC 952/2181
491
             */
492
            $host = strtolower(trim($host));
493
            if (strpos($host, ':') !== false) {
494
                $host = preg_replace('/:[[:digit:]]+$/', '', $host);
495
            }
496
497
            /**
498
             * Host may contain only the ASCII letters 'a' through 'z' (in a case-insensitive manner),
499
             * the digits '0' through '9', and the hyphen ('-') as per RFC 952/2181
500
             */
501
            if ('' !== preg_replace("/[a-z0-9-]+\.?/", '', $host)) {
502
                throw new \UnexpectedValueException('Invalid host ' . $host);
503
            }
504
        }
505
506
        return (string) $host;
507
    }
508
509
    /**
510
     * Sets if the `Request::getHttpHost` method must be use strict validation of host name or not
511
     */
512
    public function setStrictHostCheck($flag = true)
513
    {
514
        $this->_strictHostCheck = $flag;
515
516
        return $this;
517
    }
518
519
    /**
520
     * Checks if the `Request::getHttpHost` method will be use strict validation of host name or not
521
     */
522
    public function isStrictHostCheck()
523
    {
524
        return $this->_strictHostCheck;
525
    }
526
527
    /**
528
     * Get port
529
     *
530
     * @return int
531
     */
532
    public function getPort()
533
    {
534
        /**
535
         * Get the server name from $_SERVER["HTTP_HOST"]
536
         */
537
        $host = $this->getServer('HTTP_HOST');
538
        if ($host) {
539
            if (strpos($host, ':') !== false) {
540
                $pos = strrpos($host, ':');
541
542
                if (false !== $pos) {
543
                    return (int)substr($host, $pos + 1);
544
                }
545
546
                return 'https' === $this->getScheme() ? 443 : 80;
547
            }
548
        }
549
        return (int) $this->getServer('SERVER_PORT');
550
    }
551
552
    /**
553
     * Gets HTTP URI which request has been made
554
     */
555
    public function getURI()
556
    {
557
        $requestURI = $this->getServer('request_uri'); //$this->getServer('REQUEST_URI') == $this->getQuery('_url') ? $this->getServer('REQUEST_URI') : $this->getQuery('_url');
558
        if ($requestURI) {
559
            return $requestURI;
560
        }
561
562
        return '';
563
    }
564
565
    /**
566
     * Get client ip
567
     *
568
     * @param boolean $trustForwardedHeader
569
     * @return string|boolean
570
     */
571
    public function getClientAddress($trustForwardedHeader = true)
572
    {
573
        $address = null;
574
575
        /**
576
         * Proxies uses this IP
577
         */
578
        if ($trustForwardedHeader) {
579
            $address = $this->getServer('X_FORWARDED_FOR');
580
            if ($address === null) {
581
                $address = $this->getServer('X_REAL_IP');
582
            }
583
        }
584
585
        if ($address === null) {
586
            $address = $this->getServer('REMOTE_ADDR');
587
        }
588
589
        if (is_string($address)) {
590
            if (strpos($address, ',') !== false) {
591
                /**
592
                 * The client address has multiples parts, only return the first part
593
                 */
594
                return explode(',', $address)[0];
595
            }
596
            return $address;
597
        }
598
599
        return false;
600
    }
601
602
    /**
603
     * Get method
604
     *
605
     * @return string
606
     */
607
    public function getMethod()
608
    {
609
        $returnMethod = $this->getServer('REQUEST_METHOD');
610
        if (!isset($returnMethod)) {
611
            return 'GET';
612
        }
613
614
        $returnMethod = strtoupper($returnMethod);
615
        if ($returnMethod === 'POST') {
616
            $overridedMethod = $this->getHeader('X-HTTP-METHOD-OVERRIDE');
617
            if (!empty($overridedMethod)) {
618
                $returnMethod = strtoupper($overridedMethod);
619
            } elseif ($this->_httpMethodParameterOverride) {
620
                if ($spoofedMethod = $this->get('_method')) {
621
                    $returnMethod = strtoupper($spoofedMethod);
622
                }
623
            }
624
        }
625
626
        if (!$this->isValidHttpMethod($returnMethod)) {
627
            return 'GET';
628
        }
629
630
        return $returnMethod;
631
    }
632
633
    /**
634
     * Get user agent
635
     *
636
     * @return string|void
637
     */
638
    public function getUserAgent()
639
    {
640
        $userAgent = $this->getServer('HTTP_USER_AGENT');
641
        if ($userAgent) {
642
            return $userAgent;
643
        }
644
        return '';
645
    }
646
647
    /**
648
     * Is method
649
     *
650
     * @param string $methods
651
     * @param boolean $strict
652
     * @return boolean
653
     */
654
    public function isMethod($methods, $strict = false)
655
    {
656
        $httpMethod = $this->getMethod();
657
658
        if (is_string($methods)) {
659
            if ($strict && !$this->isValidHttpMethod($methods)) {
660
                throw new Exception('Invalid HTTP method: ' . $methods);
661
            }
662
            return $methods == $httpMethod;
663
        }
664
665
        if (is_array($methods)) {
666
            foreach ($methods as $method) {
667
                if ($this->isMethod($method, $strict)) {
668
                    return true;
669
                }
670
            }
671
672
            return false;
673
        }
674
675
        if ($strict) {
676
            throw new Exception('Invalid HTTP method: non-string');
677
        }
678
679
        return false;
680
    }
681
682
    /**
683
     * Is post
684
     *
685
     * @return boolean
686
     */
687
    public function isPost()
688
    {
689
        return $this->getMethod() === 'POST';
690
    }
691
692
    /**
693
     * Is GET
694
     *
695
     * @return boolean
696
     */
697
    public function isGet()
698
    {
699
        return $this->getMethod() === 'GET';
700
    }
701
702
    /**
703
     * Is Put
704
     *
705
     * @return boolean
706
     */
707
    public function isPut()
708
    {
709
        return $this->getMethod() === 'PUT';
710
    }
711
712
    /**
713
     * Is patch
714
     *
715
     * @return boolean
716
     */
717
    public function isPatch()
718
    {
719
        return $this->getMethod() === 'PATCH';
720
    }
721
722
    /**
723
     * Is head
724
     *
725
     * @return boolean
726
     */
727
    public function isHead()
728
    {
729
        return $this->getMethod() === 'HEAD';
730
    }
731
732
    /**
733
     * Is dealete
734
     *
735
     * @return boolean
736
     */
737
    public function isDelete()
738
    {
739
        return $this->getMethod() === 'DELETE';
740
    }
741
742
    /**
743
     * Is Options
744
     *
745
     * @return boolean
746
     */
747
    public function isOptions()
748
    {
749
        return $this->getMethod() === 'OPTIONS';
750
    }
751
752
    /**
753
     * Is Purge
754
     *
755
     * @return boolean
756
     */
757
    public function isPurge()
758
    {
759
        return $this->getMethod() === 'PURGE';
760
    }
761
762
    /**
763
     * Is trace
764
     *
765
     * @return boolean
766
     */
767
    public function isTrace()
768
    {
769
        return $this->getMethod() === 'TRACE';
770
    }
771
772
    /**
773
     * Is connect
774
     *
775
     * @return boolean
776
     */
777
    public function isConnect()
778
    {
779
        return $this->getMethod() === 'CONNECT';
780
    }
781
782
    /**
783
     * Has uploaded files?
784
     *
785
     * @param boolean $onlySuccessful
786
     * @return string
787
     */
788
    public function hasFiles($onlySuccessful = false)
789
    {
790
        $numberFiles = 0;
791
792
        $files = $this->files;
793
794
        if (empty($files)) {
795
            return $numberFiles;
796
        }
797
798
        foreach ($files as $file) {
799
            $error = $file['error'];
800
            if ($error) {
801
                if (!is_array($error)) {
802
                    if (!$error || !$onlySuccessful) {
803
                        $numberFiles++;
804
                    }
805
                } else {
806
                    $numberFiles += $this->hasFileHelper($error, $onlySuccessful);
807
                }
808
            }
809
        }
810
811
        return $numberFiles;
812
    }
813
814
    /**
815
     * Recursively counts file in an array of files
816
     */
817
    protected function hasFileHelper($data, $onlySuccessful)
818
    {
819
        $numberFiles = 0;
820
821
        if (!is_array($data)) {
822
            return 1;
823
        }
824
825
        foreach ($data as $value) {
826
            if (!is_array($value)) {
827
                if (!$value || !$onlySuccessful) {
828
                    $numberFiles++;
829
                }
830
            } else {
831
                $numberFiles += $this->hasFileHelper($value, $onlySuccessful);
832
            }
833
        }
834
835
        return $numberFiles;
836
    }
837
838
    /**
839
     * Get the uploaded files
840
     *
841
     * @param boolean $onlySuccessful
842
     * @return array
843
     */
844
    public function getUploadedFiles($onlySuccessful = false)
845
    {
846
        $files = [];
847
848
        $superFiles = $this->files;
849
850
        if (count($superFiles) > 0) {
851
            foreach ($superFiles as $prefix => $input) {
852
                if (is_array(!$input['name'])) {
853
                    $smoothInput = $this->smoothFiles(
854
                        $input['name'],
855
                        $input['type'],
856
                        $input['tmp_name'],
857
                        $input['size'],
858
                        $input['error'],
859
                        $prefix
860
                    );
861
862
                    foreach ($smoothInput as $file) {
863
                        if ($onlySuccessful === false || $file['error'] == UPLOAD_ERR_OK) {
864
                            $dataFile = [
865
                                'name' => $file['name'],
866
                                'type' => $file['type'],
867
                                'tmp_name' => $file['tmp_name'],
868
                                'size' => $file['size'],
869
                                'error' => $file['error']
870
                            ];
871
872
                            $files[] = new File($dataFile, $file['key']);
873
                        }
874
                    }
875
                } else {
876
                    if ($onlySuccessful === false || $input['error'] == UPLOAD_ERR_OK) {
877
                        $files[] = new File($input, $prefix);
878
                    }
879
                }
880
            }
881
        }
882
883
        return $files;
884
    }
885
886
    /**
887
     * Get the files
888
     *
889
     * @param string $key
890
     * @return string|void
891
     */
892
    public function getFile($key)
893
    {
894
        if (!isset($this->_files)) {
895
            $this->_files = [];
896
            $files = $this->getUploadedFiles();
897
            foreach ($files as $file) {
898
                $this->_files[$file->getKey()] = $file;
899
            }
900
        }
901
902
        if (!isset($this->_files[$key])) {
903
            return null;
904
        }
905
906
        return $this->_files[$key];
907
    }
908
909
    /**
910
     * Smooth out $_FILES to have plain array with all files uploaded
911
     */
912
    protected function smoothFiles($names, $types, $tmp_names, $sizes, $errors, $prefix)
913
    {
914
        $files = [];
915
916
        foreach ($names as $idx => $name) {
917
            $p = $prefix . '.' . $idx;
918
919
            if (is_string($name)) {
920
                $files[] = [
921
                    'name' => $name,
922
                    'type' => $types[$idx],
923
                    'tmp_name' => $tmp_names[$idx],
924
                    'size' => $sizes[$idx],
925
                    'error' => $errors[$idx],
926
                    'key' => $p
927
                ];
928
            }
929
930
            if (is_array($name)) {
931
                $parentFiles = $this->smoothFiles(
932
                    $names[$idx],
933
                    $types[$idx],
934
                    $tmp_names[$idx],
935
                    $sizes[$idx],
936
                    $errors[$idx],
937
                    $p
938
                );
939
940
                foreach ($parentFiles as $file) {
941
                    $files[] = $file;
942
                }
943
            }
944
        }
945
946
        return $files;
947
    }
948
949
    /**
950
     * Get the servers
951
     *
952
     * @return array
953
     */
954
    public function getServers()
955
    {
956
        return $this->server;
957
    }
958
959
    /**
960
     * Get the headers
961
     *
962
     * @return array
963
     */
964
    public function getHeaders()
965
    {
966
        $headers = [];
967
        $contentHeaders = ['CONTENT_TYPE' => true, 'CONTENT_LENGTH' => true, 'CONTENT_MD5' => true];
968
969
        $servers = $this->getServers();
970
        foreach ($servers as $name => $value) {
971
            if (Text::startsWith($name, 'HTTP_')) {
972
                $name = ucwords(strtolower(str_replace('_', ' ', substr($name, 5))));
973
                $name = str_replace(' ', '-', $name);
974
                $headers[$name] = $value;
975
            }
976
977
            $name = strtoupper($name);
978
            if (isset($contentHeaders[$name])) {
979
                $name = ucwords(strtolower(str_replace('_', ' ', $name)));
980
                $name = str_replace(' ', '-', $name);
981
                $headers[$name] = $value;
982
            }
983
        }
984
985
        $authHeaders = $this->resolveAuthorizationHeaders();
986
987
        // Protect for future (child classes) changes
988
        if (is_array($authHeaders)) {
989
            $headers = array_merge($headers, $authHeaders);
990
        }
991
992
        return $headers;
993
    }
994
995
    /**
996
     * Get the httpd reference
997
     *
998
     * @return string|void
999
     */
1000
    public function getHTTPReferer()
1001
    {
1002
        $httpReferer = $this->getServer('HTTP_REFERER');
1003
        if ($httpReferer) {
1004
            return $httpReferer;
1005
        }
1006
1007
        return '';
1008
    }
1009
1010
    /**
1011
     * Process a request header and return the one with best quality
1012
     *
1013
     * @return string
1014
     */
1015
    protected function _getBestQuality($qualityParts, $name)
1016
    {
1017
        $i = 0;
1018
        $quality = 0.0;
1019
        $selectedName = '';
1020
1021
        foreach ($qualityParts as $accept) {
1022
            if ($i == 0) {
1023
                $quality = (double)$accept['quality'];
1024
                $selectedName = $accept[$name];
1025
            } else {
1026
                $acceptQuality = (double)$accept['quality'];
1027
                if ($acceptQuality > $quality) {
1028
                    $quality = $acceptQuality;
1029
                    $selectedName = $accept[$name];
1030
                }
1031
            }
1032
            $i++;
1033
        }
1034
1035
        return $selectedName;
1036
    }
1037
1038
    /**
1039
     * Get the content
1040
     *
1041
     * @return array
1042
     */
1043
    public function getAcceptableContent()
1044
    {
1045
        return $this->_getQualityHeader('HTTP_ACCEPT', 'accept');
1046
    }
1047
1048
    /**
1049
     * Get the content
1050
     *
1051
     * @return string
1052
     */
1053
    public function getBestAccept()
1054
    {
1055
        return $this->_getBestQuality($this->getAcceptableContent(), 'accept');
1056
    }
1057
1058
    /**
1059
     * Get the content
1060
     *
1061
     * @return array
1062
     */
1063
    public function getClientCharsets()
1064
    {
1065
        return $this->_getQualityHeader('HTTP_ACCEPT_CHARSET', 'charset');
1066
    }
1067
1068
    /**
1069
     * Get the content
1070
     *
1071
     * @return string
1072
     */
1073
    public function getBestCharset()
1074
    {
1075
        return $this->_getBestQuality($this->getClientCharsets(), 'charset');
1076
    }
1077
1078
    /**
1079
     * Get the content
1080
     *
1081
     * @return array
1082
     */
1083
    public function getLanguages()
1084
    {
1085
        return $this->_getQualityHeader('HTTP_ACCEPT_LANGUAGE', 'language');
1086
    }
1087
1088
    /**
1089
     * Get the content
1090
     *
1091
     * @return string
1092
     */
1093
    public function getBestLanguage()
1094
    {
1095
        return $this->_getBestQuality($this->getLanguages(), 'language');
1096
    }
1097
1098
    /**
1099
     * Get the basic httpd auth
1100
     *
1101
     * @return array|void
1102
     */
1103
    public function getBasicAuth()
1104
    {
1105
        if ($this->hasServer('PHP_AUTH_USER') && $this->hasServer('PHP_AUTH_PW')) {
1106
            return [
1107
                'username' => $this->getServer('PHP_AUTH_USER'),
1108
                'password' => $this->getServer('PHP_AUTH_PW')
1109
            ];
1110
        }
1111
1112
        return null;
1113
    }
1114
1115
    /**
1116
     * Get the server digest
1117
     *
1118
     * @return array
1119
     */
1120
    public function getDigestAuth()
1121
    {
1122
        $auth = [];
1123
        if ($this->hasServer('PHP_AUTH_DIGEST')) {
1124
            $digest = $this->getServer('PHP_AUTH_DIGEST');
1125
            $matches = [];
1126
            if (!preg_match_all("#(\\w+)=(['\"]?)([^'\" ,]+)\\2#", $digest, $matches, 2)) {
1127
                return $auth;
1128
            }
1129
            if (is_array($matches)) {
1130
                foreach ($matches as $match) {
1131
                    $auth[$match[1]] = $match[3];
1132
                }
1133
            }
1134
        }
1135
1136
        return $auth;
1137
    }
1138
1139
    /**
1140
     * Checks if a method is a valid HTTP method
1141
     */
1142
    public function isValidHttpMethod($method)
1143
    {
1144
        switch (strtoupper($method)) {
1145
            case 'GET':
1146
            case 'POST':
1147
            case 'PUT':
1148
            case 'DELETE':
1149
            case 'HEAD':
1150
            case 'OPTIONS':
1151
            case 'PATCH':
1152
            case 'PURGE': // Squid and Varnish support
1153
            case 'TRACE':
1154
            case 'CONNECT':
1155
                return true;
1156
        }
1157
1158
        return false;
1159
    }
1160
1161
    /**
1162
     * Helper to get data from superglobals, applying filters if needed.
1163
     * If no parameters are given the superglobal is returned.
1164
     */
1165
    protected function getHelper($source, $name = null, $filters = null, $defaultValue = null, $notAllowEmpty = false, $noRecursive = false)
1166
    {
1167
        if ($name === null) {
1168
            return $source;
1169
        }
1170
1171
        if (!isset($source[$name])) {
1172
            return $defaultValue;
1173
        }
1174
1175
        $value = $source[$name];
1176
1177
        if ($filters !== null) {
1178
            $filter = $this->_filter;
1179
            if (!$filter instanceof FilterInterface) {
1180
                $dependencyInjector = $this->_dependencyInjector;
1181
                if (!$dependencyInjector instanceof DiInterface) {
1182
                    throw new Exception("A dependency injection object is required to access the 'filter' service");
1183
                }
1184
1185
                $filter = $dependencyInjector->getShared('filter');
1186
                $this->_filter = $filter;
1187
            }
1188
1189
            $value = $filter->sanitize($value, $filters, $noRecursive);
1190
        }
1191
1192
        if (empty($value) && $notAllowEmpty === true) {
1193
            return $defaultValue;
1194
        }
1195
1196
        return $value;
1197
    }
1198
1199
    /**
1200
     * Gets content type which request has been made
1201
     */
1202
    public function getContentType()
1203
    {
1204
        $contentType = $this->getHeader('CONTENT_TYPE');
1205
        if ($contentType) {
1206
            return $contentType;
1207
        }
1208
1209
        return null;
1210
    }
1211
1212
    /**
1213
     * Process a request header and return an array of values with their qualities
1214
     *
1215
     * @return array
1216
     */
1217
    protected function _getQualityHeader($serverIndex, $name)
1218
    {
1219
        $returnedParts = [];
1220
        $parts = preg_split('/,\\s*/', $this->getServer($serverIndex), -1, PREG_SPLIT_NO_EMPTY);
1221
        foreach ($parts as $part) {
1222
            $headerParts = [];
1223
            $hParts = preg_split("/\s*;\s*/", trim($part), -1, PREG_SPLIT_NO_EMPTY);
1224
            foreach ($hParts as $headerPart) {
1225
                if (strpos($headerPart, '=') !== false) {
1226
                    $split = explode('=', $headerPart, 2);
1227
                    if ($split[0] === 'q') {
1228
                        $headerParts['quality'] = (double)$split[1];
1229
                    } else {
1230
                        $headerParts[$split[0]] = $split[1];
1231
                    }
1232
                } else {
1233
                    $headerParts[$name] = $headerPart;
1234
                    $headerParts['quality'] = 1.0;
1235
                }
1236
            }
1237
1238
            $returnedParts[] = $headerParts;
1239
        }
1240
1241
        return $returnedParts;
1242
    }
1243
1244
    /**
1245
     * Resolve authorization headers.
1246
     */
1247
    protected function resolveAuthorizationHeaders()
1248
    {
1249
        $headers = [];
1250
        $hasEventsManager = false;
1251
        $eventsManager = null;
1252
1253
        $dependencyInjector = $this->getDI();
1254
        if ($dependencyInjector instanceof DiInterface) {
1255
            $hasEventsManager = (bool)$dependencyInjector->has('eventsManager');
1256
            if ($hasEventsManager) {
1257
                $eventsManager = $dependencyInjector->getShared('eventsManager');
1258
            }
1259
        }
1260
1261
        if ($hasEventsManager && $eventsManager instanceof Manager) {
1262
            $resolved = $eventsManager->fire(
1263
                'request:beforeAuthorizationResolve',
1264
                $this,
1265
                ['server' => $this->getServers()]
1266
            );
1267
1268
            if (is_array($resolved)) {
1269
                $headers = array_merge($headers, $resolved);
1270
            }
1271
        }
1272
1273
        $this->resolveAuthHeaderPhp($headers);
1274
        $this->resolveAuthHeaderPhpDigest($headers);
1275
1276
        if ($hasEventsManager && $eventsManager instanceof Manager) {
1277
            $resolved = $eventsManager->fire(
1278
                'request:afterAuthorizationResolve',
1279
                $this,
1280
                ['headers' => $headers, 'server' => $this->getServers()]
1281
            );
1282
1283
            if (is_array($resolved)) {
1284
                $headers = array_merge($headers, $resolved);
1285
            }
1286
        }
1287
1288
        return $headers;
1289
    }
1290
1291
    /**
1292
     * Resolve the PHP_AUTH_USER
1293
     *
1294
     * @param array $headers
1295
     * @return void
1296
     */
1297
    protected function resolveAuthHeaderPhp(array &$headers): void
1298
    {
1299
        $authHeader = false;
1300
1301
        if ($this->hasServer('PHP_AUTH_USER') && $this->hasServer('PHP_AUTH_PW')) {
1302
            $headers['Php-Auth-User'] = $this->getServer('PHP_AUTH_USER');
1303
            $headers['Php-Auth-Pw'] = $this->getServer('PHP_AUTH_PW');
1304
        } else {
1305
            if ($this->hasServer('HTTP_AUTHORIZATION')) {
1306
                $authHeader = $this->getServer('HTTP_AUTHORIZATION');
1307
            } elseif ($this->hasServer('REDIRECT_HTTP_AUTHORIZATION')) {
1308
                $authHeader = $this->getServer('REDIRECT_HTTP_AUTHORIZATION');
1309
            }
1310
1311
            if ($authHeader) {
1312
                if (stripos($authHeader, 'basic ') === 0) {
1313
                    $exploded = explode(':', base64_decode(substr($authHeader, 6)), 2);
1314
                    if (count($exploded) == 2) {
1315
                        $headers['Php-Auth-User'] = $exploded[0];
1316
                        $headers['Php-Auth-Pw'] = $exploded[1];
1317
                    }
1318
                } elseif (stripos($authHeader, 'digest ') === 0 && !$this->hasServer('PHP_AUTH_DIGEST')) {
1319
                    $headers['Php-Auth-Digest'] = $authHeader;
1320
                } elseif (stripos($authHeader, 'bearer ') === 0) {
1321
                    $headers['Authorization'] = $authHeader;
1322
                }
1323
            }
1324
        }
1325
    }
1326
1327
    /**
1328
     * Reseolve PHP auth digest
1329
     *
1330
     * @param array $headers
1331
     * @return void
1332
     */
1333
    protected function resolveAuthHeaderPhpDigest(array &$headers): void
1334
    {
1335
        if (!isset($headers['Authorization'])) {
1336
            if (isset($headers['Php-Auth-User'])) {
1337
                $headers['Authorization'] = 'Basic ' . base64_encode($headers['Php-Auth-User'] . ':' . $headers['Php-Auth-Pw']);
1338
            } elseif (isset($headers['Php-Auth-Digest'])) {
1339
                $headers['Authorization'] = $headers['Php-Auth-Digest'];
1340
            }
1341
        }
1342
    }
1343
}
1344