Completed
Push — master ( 3999cc...7004fd )
by Anton
04:02 queued 01:32
created

Request::parseAcceptHeader()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 7.116

Importance

Changes 0
Metric Value
cc 7
nc 6
nop 1
dl 0
loc 34
ccs 13
cts 15
cp 0.8667
crap 7.116
rs 8.4426
c 0
b 0
f 0
1
<?php
2
/**
3
 * Bluz Framework Component
4
 *
5
 * @copyright Bluz PHP Team
6
 * @link      https://github.com/bluzphp/framework
7
 */
8
9
declare(strict_types=1);
10
11
namespace Bluz\Proxy;
12
13
use Bluz\Common\Exception\ComponentException;
14
use Bluz\Http\RequestMethod;
15
use Bluz\Request\RequestFactory;
16
use Psr\Http\Message\UriInterface;
17
use Zend\Diactoros\ServerRequest as Instance;
18
19
/**
20
 * Proxy to Request
21
 *
22
 * Example of usage
23
 * <code>
24
 *     use Bluz\Proxy\Request;
25
 *
26
 *     Request::getParam('foo');
27
 * </code>
28
 *
29
 * @package  Bluz\Proxy
30
 * @author   Anton Shevchuk
31
 *
32
 * @todo     Proxy class should be clean
33
 *
34
 * @method   static Instance getInstance()
35
 *
36
 * @method   static UriInterface getUri()
37
 * @see      \Zend\Diactoros\RequestTrait::getUri()
38
 */
39
final class Request
40
{
41
    use ProxyTrait;
42
43
    /**
44
     * @const string HTTP content types
45
     */
46
    public const TYPE_ANY = '*/*';
47
    public const TYPE_HTML = 'text/html';
48
    public const TYPE_JSON = 'application/json';
49
50
    /**
51
     * @var array|null Accepted type
52
     */
53
    static private $accept;
54
55
    /**
56
     * @var array|null Accepted languages
57
     */
58
    static private $language;
59
60
    /**
61
     * Init instance
62
     *
63
     * @throws ComponentException
64
     */
65
    private static function initInstance()
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
66
    {
67
        throw new ComponentException('Class `Proxy\\Request` required external initialization');
68
    }
69
70
    /**
71
     * Retrieve a member of the $_GET super global
72
     *
73
     * If no $key is passed, returns the entire $_GET array.
74
     *
75
     * @param  string $key
76
     * @param  string $default Default value to use if key not found
77
     *
78
     * @return string Returns null if key does not exist
79
     */
80 75
    public static function getQuery($key = null, $default = null)
81
    {
82 75
        return self::getInstance()->getQueryParams()[$key] ?? $default;
83
    }
84
85
    /**
86
     * Retrieve a member of the $_POST super global
87
     *
88
     * If no $key is passed, returns the entire $_POST array.
89
     *
90
     * @param  string $key
91
     * @param  string $default Default value to use if key not found
92
     *
93
     * @return string Returns null if key does not exist
94
     */
95 65
    public static function getPost($key = null, $default = null)
96
    {
97 65
        return self::getInstance()->getParsedBody()[$key] ?? $default;
98
    }
99
100
    /**
101
     * Retrieve a member of the $_SERVER super global
102
     *
103
     * If no $key is passed, returns the entire $_SERVER array.
104
     *
105
     * @param  string $key
106
     * @param  string $default Default value to use if key not found
107
     *
108
     * @return string Returns null if key does not exist
109
     */
110 8
    public static function getServer($key = null, $default = null)
111
    {
112 8
        return self::getInstance()->getServerParams()[$key] ?? $default;
113
    }
114
115
    /**
116
     * Retrieve a member of the $_COOKIE super global
117
     *
118
     * If no $key is passed, returns the entire $_COOKIE array.
119
     *
120
     * @param  string $key
121
     * @param  string $default Default value to use if key not found
122
     *
123
     * @return string Returns null if key does not exist
124
     */
125 1
    public static function getCookie($key = null, $default = null)
126
    {
127 1
        return self::getInstance()->getCookieParams()[$key] ?? $default;
128
    }
129
130
    /**
131
     * Retrieve a member of the $_ENV super global
132
     *
133
     * If no $key is passed, returns the entire $_ENV array.
134
     *
135
     * @param  string $key
136
     * @param  string $default Default value to use if key not found
137
     *
138
     * @return string Returns null if key does not exist
139
     */
140 1
    public static function getEnv($key = null, $default = null)
141
    {
142 1
        return $_ENV[$key] ?? $default;
143
    }
144
145
    /**
146
     * Search for a header value
147
     *
148
     * @param string $header
149
     * @param mixed  $default
150
     *
151
     * @return string
152
     */
153 40
    public static function getHeader($header, $default = null)
154
    {
155 40
        $header  = strtolower($header);
156 40
        $headers = self::getInstance()->getHeaders();
157 40
        $headers = array_change_key_case($headers, CASE_LOWER);
158 40
        if (array_key_exists($header, $headers)) {
159 5
            $value = \is_array($headers[$header]) ? implode(', ', $headers[$header]) : $headers[$header];
160 5
            return $value;
161
        }
162 40
        return $default;
163
    }
164
165
    /**
166
     * Access values contained in the superglobals as public members
167
     * Order of precedence: 1. GET, 2. POST
168
     *
169
     * @param  string $key
170
     * @param  null   $default
171
     *
172
     * @return string|null
173
     * @link http://msdn.microsoft.com/en-us/library/system.web.httprequest.item.aspx
174
     */
175 75
    public static function getParam($key, $default = null)
176
    {
177
        return
178 75
            self::getQuery($key) ??
179 64
            self::getPost($key) ??
180 75
            $default;
181
    }
182
183
    /**
184
     * Get all params from GET and POST or PUT
185
     *
186
     * @return array
187
     */
188 36
    public static function getParams()
189
    {
190 36
        $body = (array)self::getInstance()->getParsedBody();
191 36
        $query = (array)self::getInstance()->getQueryParams();
192 36
        return array_merge($body, $query);
193
    }
194
195
    /**
196
     * Get uploaded file
197
     *
198
     * @param  string $name
199
     *
200
     * @return \Zend\Diactoros\UploadedFile
201
     */
202
    public static function getFile($name)
203
    {
204
        return self::getInstance()->getUploadedFiles()[$name] ?? false;
205
    }
206
207
    /**
208
     * Get the client's IP address
209
     *
210
     * @param  bool $checkProxy
211
     *
212
     * @return string
213
     */
214 1
    public static function getClientIp($checkProxy = true)
215
    {
216 1
        $result = null;
217 1
        if ($checkProxy) {
218 1
            $result = self::getServer('HTTP_CLIENT_IP') ?? self::getServer('HTTP_X_FORWARDED_FOR') ?? null;
219
        }
220 1
        return $result ?? self::getServer('REMOTE_ADDR');
221
    }
222
223
    /**
224
     * Get module
225
     *
226
     * @return string
227
     */
228 44
    public static function getModule(): string
229
    {
230 44
        return self::getParam('_module', Router::getDefaultModule());
231
    }
232
233
    /**
234
     * Get controller
235
     *
236
     * @return string
237
     */
238 44
    public static function getController(): string
239
    {
240 44
        return self::getParam('_controller', Router::getDefaultController());
241
    }
242
243
    /**
244
     * Get method
245
     *
246
     * @return string
247
     */
248 25
    public static function getMethod(): string
249
    {
250 25
        return self::getParam('_method', self::getInstance()->getMethod());
251
    }
252
253
    /**
254
     * Get Accept MIME Type
255
     *
256
     * @return array
257
     */
258 40
    public static function getAccept(): array
259
    {
260 40
        if (!self::$accept) {
261
            // get header from request
262 40
            self::$accept = self::parseAcceptHeader(self::getHeader('Accept'));
263
        }
264 40
        return self::$accept;
265
    }
266
267
    /**
268
     * Get Accept MIME Type
269
     *
270
     * @return array
271
     */
272
    public static function getAcceptLanguage(): array
273
    {
274
        if (!self::$language) {
275
            // get header from request
276
            self::$language = self::parseAcceptHeader(self::getHeader('Accept-Language'));
277
        }
278
        return self::$language;
279
    }
280
281
    /**
282
     * parseAcceptHeader
283
     *
284
     * @param string $header
285
     *
286
     * @return array
287
     */
288 40
    private static function parseAcceptHeader($header): array
289
    {
290
        // empty array
291 40
        $accept = [];
292
293
        // check empty
294 40
        if (!$header || $header === '') {
295 37
            return $accept;
296
        }
297
298
        // make array from header
299 3
        $values = explode(',', $header);
300 3
        $values = array_map('trim', $values);
301
302 3
        foreach ($values as $a) {
303
            // the default quality is 1.
304 3
            $q = 1;
305
            // check if there is a different quality
306 3
            if (strpos($a, ';q=') or strpos($a, '; q=')) {
307
                // divide "mime/type;q=X" into two parts: "mime/type" i "X"
308
                [$a, $q] = preg_split('/;([ ]?)q=/', $a);
309
            }
310
            // remove other extension
311 3
            if (strpos($a, ';')) {
312
                $a = substr($a, 0, strpos($a, ';'));
313
            }
314
315
            // mime-type $a is accepted with the quality $q
316
            // WARNING: $q == 0 means, that isn’t supported!
317 3
            $accept[$a] = (float)$q;
318
        }
319 3
        arsort($accept);
320 3
        return $accept;
321
    }
322
323
    /**
324
     * Reset accept for tests
325
     *
326
     * @return void
327
     */
328 847
    public static function resetAccept(): void
329
    {
330 847
        self::$accept = null;
331 847
    }
332
333
    /**
334
     * Check Accept header
335
     *
336
     * @param array $allowTypes
337
     *
338
     * @return string|false
339
     */
340 40
    public static function checkAccept(array $allowTypes = [])
341
    {
342 40
        $accept = self::getAccept();
343
344
        // if no parameter was passed, just return first mime type from parsed data
345 40
        if (empty($allowTypes)) {
346
            return current(array_keys($accept));
347
        }
348
349 40
        $allowTypes = array_map('strtolower', $allowTypes);
350
351
        // let’s check our supported types:
352 40
        foreach ($accept as $mime => $quality) {
353 3
            if ($quality && \in_array($mime, $allowTypes, true)) {
354 3
                return $mime;
355
            }
356
        }
357
        // no mime-type found
358 38
        return false;
359
    }
360
361
    /**
362
     * Check CLI
363
     *
364
     * @return bool
365
     */
366 1
    public static function isCli(): bool
367
    {
368 1
        return (PHP_SAPI === 'cli');
369
    }
370
371
    /**
372
     * Check HTTP
373
     *
374
     * @return bool
375
     */
376 3
    public static function isHttp(): bool
377
    {
378 3
        return (PHP_SAPI !== 'cli');
379
    }
380
381
    /**
382
     * Is this a GET method request?
383
     *
384
     * @return bool
385
     */
386 2
    public static function isGet(): bool
387
    {
388 2
        return (self::getInstance()->getMethod() === RequestMethod::GET);
389
    }
390
391
    /**
392
     * Is this a POST method request?
393
     *
394
     * @return bool
395
     */
396 2
    public static function isPost(): bool
397
    {
398 2
        return (self::getInstance()->getMethod() === RequestMethod::POST);
399
    }
400
401
    /**
402
     * Is this a PUT method request?
403
     *
404
     * @return bool
405
     */
406 1
    public static function isPut(): bool
407
    {
408 1
        return (self::getInstance()->getMethod() === RequestMethod::PUT);
409
    }
410
411
    /**
412
     * Is this a DELETE method request?
413
     *
414
     * @return bool
415
     */
416 1
    public static function isDelete(): bool
417
    {
418 1
        return (self::getInstance()->getMethod() === RequestMethod::DELETE);
419
    }
420
421
    /**
422
     * Is the request a Javascript XMLHttpRequest?
423
     *
424
     * @return bool
425
     */
426 9
    public static function isXmlHttpRequest(): bool
427
    {
428 9
        return (self::getHeader('X-Requested-With') === 'XMLHttpRequest');
429
    }
430
}
431