Passed
Push — master ( 3bbc65...908bc3 )
by Marcio
28:45 queued 24:50
created

Request::getRequestMethod()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 12
c 1
b 0
f 0
dl 0
loc 20
rs 9.2222
cc 6
nc 5
nop 0
1
<?php
2
/**
3
 *
4
 * KNUT7 K7F (https://marciozebedeu.com/)
5
 * KNUT7 K7F (tm) : Rapid Development Framework (https://marciozebedeu.com/)
6
 *
7
 * Licensed under The MIT License
8
 * For full copyright and license information, please see the LICENSE.txt
9
 * Redistributions of files must retain the above copyright notice.
10
 *
11
 * @link      https://github.com/knut7/framework/ for the canonical source repository
12
 * @copyright (c) 2015.  KNUT7  Software Technologies AO Inc. (https://marciozebedeu.com/)
13
 * @license   https://marciozebedeu.com/license/new-bsd MIT lIcense
14
 * @author    Marcio Zebedeu - [email protected]
15
 * @version   1.0.14
16
 *
17
 *
18
 */
19
20
namespace Ballybran\Core\Http;
21
22
use Ballybran\Core\Http\RequestBuilder;
23
use Ballybran\Core\Http\Response;
24
25
class Request
26
{
27
    /**
28
     * @var string $validMethods Valid methods for Requests
29
     */
30
    public $validMethods = 'GET|POST|PUT|DELETE|HEAD|OPTIONS|PATCH|ANY|AJAX|XPOST|XPUT|XDELETE|XPATCH';
31
32
    /**
33
     * Request method validation
34
     *
35
     * @param string $data
36
     * @param string $method
37
     *
38
     * @return bool
39
     */
40
    public function validMethod($data, $method)
41
    {
42
        $valid = false;
43
        if (strstr($data, '|')) {
44
            foreach (explode('|', $data) as $value) {
45
                $valid = $this->checkMethods($value, $method);
46
                if ($valid) {
47
                    break;
48
                }
49
            }
50
        } else {
51
            $valid = $this->checkMethods($data, $method);
52
        }
53
54
        return $valid;
55
    }
56
57
    /**
58
     * Get the request method used, taking overrides into account
59
     *
60
     * @return string
61
     */
62
    public function getRequestMethod()
63
    {
64
        // Take the method as found in $_SERVER
65
        $method = $_SERVER['REQUEST_METHOD'];
66
        // If it's a HEAD request override it to being GET and prevent any output, as per HTTP Specification
67
        // @url http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4
68
        if ($method === 'HEAD') {
69
            ob_start();
70
            $method = 'GET';
71
        } elseif ($method === 'POST') {
72
            $headers = $this->getRequestHeaders();
73
            if (isset($headers['X-HTTP-Method-Override']) &&
74
                in_array($headers['X-HTTP-Method-Override'], ['PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD'])) {
75
                $method = $headers['X-HTTP-Method-Override'];
76
            } elseif (!empty($_POST['_method'])) {
77
                $method = strtoupper($_POST['_method']);
78
            }
79
        }
80
81
        return $method;
82
    }
83
84
    /**
85
     * check method valid
86
     *
87
     * @param string $value
88
     * @param string $method
89
     *
90
     * @return bool
91
     */
92
    protected function checkMethods($value, $method)
93
    {
94
        if (in_array($value, explode('|', $this->validMethods))) {
95
            if ($this->isAjax() && $value === 'AJAX') {
96
                return true;
97
            }
98
99
            if ($this->isAjax() && strpos($value, 'X') === 0 && $method === ltrim($value, 'X')) {
100
                return true;
101
            }
102
103
            if (in_array($value, [$method, 'ANY'])) {
104
                return true;
105
            }
106
        }
107
108
        return false;
109
    }
110
111
    /**
112
     * Check ajax request
113
     *
114
     * @return bool
115
     */
116
    protected function isAjax()
117
    {
118
        return (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest');
119
    }
120
121
    /**
122
     * Get all request headers
123
     *
124
     * @return array
125
     */
126
    protected function getRequestHeaders()
127
    {
128
        
129
130
        // Method getallheaders() not available: manually extract 'm
131
        $headers = [];
132
        foreach ($_SERVER as $name => $value) {
133
            if (substr($name, 0, 5) == 'HTTP_' || $name === 'CONTENT_TYPE' || $name === 'CONTENT_LENGTH') {
134
                $headerKey = str_replace(
135
                    [' ', 'Http'],
136
                    ['-', 'HTTP'],
137
                    ucwords(strtolower(str_replace('_', ' ', substr($name, 5))))
138
                );
139
                $headers[$headerKey] = $value;
140
            }
141
        }
142
143
        return $headers;
144
    }
145
146
      /**
147
     * This static method will create a new Request object, based on the
148
     * current PHP request.
149
     */
150
    public static function getRequest(): RequestBuilder
151
    {
152
        $serverArr = $_SERVER;
153
        // $serverArr[] = ['Content-Type' => 'application/json'];
154
155
        $r = self::createFromServerArray($serverArr);
156
        $r->setBody(fopen('php://input', 'r'));
0 ignored issues
show
Bug introduced by
It seems like fopen('php://input', 'r') can also be of type false; however, parameter $body of Ballybran\Core\Http\Message::setBody() does only seem to accept callable|resource|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

156
        $r->setBody(/** @scrutinizer ignore-type */ fopen('php://input', 'r'));
Loading history...
157
        $r->setPostData($_POST);
158
159
        return $r;
160
    }
161
162
    /**
163
     * Sends the HTTP response back to a HTTP client.
164
     *
165
     * This calls php's header() function and streams the body to php://output.
166
     */
167
    public static function sendResponse(Response $response) : Response
168
    {
169
        header('HTTP/'.$response->getHttpVersion().' '.$response->getStatus().' '.$response->getStatusText());
170
        foreach ($response->getHeaders() as $key => $value) {
171
            foreach ($value as $k => $v) {
172
                if (0 === $k) {
173
                    header($key.': '.$v);
174
                } else {
175
                    header($key.': '.$v, false);
176
                }
177
            }
178
        }
179
180
        return $response;
181
        
182
    }
183
184
    /**
185
     * This static method will create a new Request object, based on a PHP
186
     * $_SERVER array.
187
     *
188
     * REQUEST_URI and REQUEST_METHOD are required.
189
     */
190
    public static function createFromServerArray(array $serverArray): RequestBuilder
191
    {
192
        $headers = [];
193
        $method = null;
194
        $url = null;
195
        $httpVersion = '1.1';
196
197
        $protocol = 'http';
198
        $hostName = 'localhost';
199
200
        foreach ($serverArray as $key => $value) {
201
            switch ($key) {
202
                case 'SERVER_PROTOCOL':
203
                    if ('HTTP/1.0' === $value) {
204
                        $httpVersion = '1.0';
205
                    } elseif ('HTTP/2.0' === $value) {
206
                        $httpVersion = '2.0';
207
                    }
208
                    break;
209
                case 'REQUEST_METHOD':
210
                    $method = $value;
211
                    break;
212
                case 'REQUEST_URI':
213
                    $url = $value;
214
                    break;
215
216
                // These sometimes show up without a HTTP_ prefix
217
                case 'CONTENT_TYPE':
218
                    $headers['Content-Type'] = $value;
219
                    break;
220
                case 'CONTENT_LENGTH':
221
                    $headers['Content-Length'] = $value;
222
                    break;
223
224
                // mod_php on apache will put credentials in these variables.
225
                // (fast)cgi does not usually do this, however.
226
                case 'PHP_AUTH_USER':
227
                    if (isset($serverArray['PHP_AUTH_PW'])) {
228
                        $headers['Authorization'] = 'Basic '.base64_encode($value.':'.$serverArray['PHP_AUTH_PW']);
229
                    }
230
                    break;
231
232
                // Similarly, mod_php may also screw around with digest auth.
233
                case 'PHP_AUTH_DIGEST':
234
                    $headers['Authorization'] = 'Digest '.$value;
235
                    break;
236
237
                // Apache may prefix the HTTP_AUTHORIZATION header with
238
                // REDIRECT_, if mod_rewrite was used.
239
                case 'REDIRECT_HTTP_AUTHORIZATION':
240
                    $headers['Authorization'] = $value;
241
                    break;
242
243
                case 'HTTP_HOST':
244
                    $hostName = $value;
245
                    $headers['Host'] = $value;
246
                    break;
247
248
                case 'HTTPS':
249
                    if (!empty($value) && 'off' !== $value) {
250
                        $protocol = 'https';
251
                    }
252
                    break;
253
254
                default:
255
                    if ('HTTP_' === substr($key, 0, 5)) {
256
                        // It's a HTTP header
257
258
                        // Normalizing it to be prettier
259
                        $header = strtolower(substr($key, 5));
260
261
                        // Transforming dashes into spaces, and uppercasing
262
                        // every first letter.
263
                        $header = ucwords(str_replace('_', ' ', $header));
264
265
                        // Turning spaces into dashes.
266
                        $header = str_replace(' ', '-', $header);
267
                        $headers[$header] = $value;
268
                    }
269
                    break;
270
            }
271
        }
272
273
        if (null === $url) {
274
            throw new \InvalidArgumentException('The _SERVER array must have a REQUEST_URI key');
275
        }
276
277
        if (null === $method) {
278
            throw new \InvalidArgumentException('The _SERVER array must have a REQUEST_METHOD key');
279
        }
280
        $r = new RequestBuilder($method, $url, $headers);
281
        $r->setHttpVersion($httpVersion);
282
        $r->setRawServerData($serverArray);
283
        $r->setAbsoluteUrl($protocol.'://'.$hostName.$url);
284
285
        return $r;
286
    }
287
    
288
}
289