header   F
last analyzed

Complexity

Total Complexity 66

Size/Duplication

Total Lines 471
Duplicated Lines 0 %

Importance

Changes 22
Bugs 1 Features 0
Metric Value
eloc 169
c 22
b 1
f 0
dl 0
loc 471
rs 3.12
wmc 66

23 Methods

Rating   Name   Duplication   Size   Complexity  
A requestType() 0 3 1
A setAllowedMethods() 0 11 2
B requestMethod() 0 38 7
A getServerMethod() 0 6 2
A getRequestType() 0 3 1
A __construct() 0 2 1
A getBody() 0 6 3
A getMethod() 0 3 1
A hasBearerToken() 0 3 1
A getMaxWindow() 0 15 5
A setHeader() 0 6 1
C setHeaders() 0 92 15
A headersList() 0 8 1
A setHeaderStatus() 0 3 1
A setAccessControllAllowedHeaders() 0 4 1
A headerAuth() 0 7 2
A apacheRequestHeaders() 0 20 6
A getHeaderStatus() 0 3 1
B getHeaders() 0 34 9
A authorizationHeaders() 0 3 1
A getAccessControllAllowedHeaders() 0 7 2
A setData() 0 3 1
A unauthorised() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like header often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use header, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * ==================================
5
 * Responsible PHP API
6
 * ==================================
7
 *
8
 * @link Git https://github.com/vince-scarpa/responsibleAPI.git
9
 *
10
 * @api Responible API
11
 * @package responsible\core\headers
12
 *
13
 * @author Vince scarpa <[email protected]>
14
 *
15
 */
16
17
namespace responsible\core\headers;
18
19
use responsible\core\exception;
20
use responsible\core\interfaces;
21
use responsible\core\server;
22
23
class header extends server implements interfaces\optionsInterface
24
{
25
    use \responsible\core\traits\optionsTrait;
26
27
    /**
28
     * Max age constant
29
     */
30
    private const MAX_WINDOW = 3600;
31
32
    /**
33
     * [$REQUEST_APPLICATION]
34
     * @var array
35
     */
36
    private $REQUEST_APPLICATION = array(
37
        'xml' => 'text/xml',
38
        'json' => 'application/json',
39
        'html' => 'text/html',
40
        'array' => 'text/plain',
41
        'object' => 'text/plain',
42
    );
43
44
    /**
45
     * [$REQUEST_TYPE / Default is json]
46
     * @var string
47
     */
48
    private $REQUEST_TYPE = 'json';
49
50
    /**
51
     * [$REQUEST_METHOD]
52
     * @var array
53
     */
54
    private $REQUEST_METHOD = [];
55
56
    /**
57
     * [$headerAuth Header authorise class object]
58
     * @var object
59
     */
60
    protected $headerAuth;
61
62
    /**
63
     * Options can set additional CORS headers
64
     * @var array
65
     */
66
    private $additionalCORSHeaders = [];
67
68
    /**
69
     * [__construct]
70
     */
71
    public function __construct()
72
    {
73
    }
74
75
    /**
76
     * [requestType]
77
     * @return void
78
     */
79
    public function requestType($type = 'json')
80
    {
81
        $this->REQUEST_TYPE = $type;
82
    }
83
84
    /**
85
     * [getRequestType]
86
     * @return string
87
     */
88
    public function getRequestType()
89
    {
90
        return $this->REQUEST_TYPE;
91
    }
92
93
    /**
94
     * [requestMethod Set and return the request method]
95
     * @return array
96
     */
97
    public function requestMethod()
98
    {
99
        $verbs = new headerVerbs();
100
101
        switch (strtolower($_SERVER['REQUEST_METHOD'])) {
102
            case 'get':
103
                $this->REQUEST_METHOD = $verbs->get();
104
                break;
105
106
            case 'post':
107
                $this->REQUEST_METHOD = $verbs->post();
108
                break;
109
110
            case 'options':
111
                $isOriginRequest = ($_SERVER['HTTP_ORIGIN']) ?? false;
112
                $this->REQUEST_METHOD = $verbs->post();
113
                echo json_encode(['success' => true]);
114
                $this->setHeaders($isOriginRequest);
115
                exit;
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...
116
                break;
117
118
            case 'put':
119
                $this->REQUEST_METHOD = $verbs->put();
120
                break;
121
122
            case 'patch':
123
                $this->REQUEST_METHOD = $verbs->patch();
124
                break;
125
126
            case 'delete':
127
                $this->REQUEST_METHOD = $verbs->delete();
128
                break;
129
130
            default:
131
                $this->REQUEST_METHOD = [];
132
                break;
133
        }
134
        return $this->REQUEST_METHOD;
135
    }
136
137
    /**
138
     * [getMethod Get the request method]
139
     * @return object
140
     */
141
    public function getMethod()
142
    {
143
        return (object) $this->REQUEST_METHOD;
144
    }
145
146
    /**
147
     * [getBody Get the post body]
148
     * @return array
149
     */
150
    public function getBody(): array
151
    {
152
        if (isset($this->getMethod()->data) && !empty($this->getMethod()->data)) {
153
            return $this->getMethod()->data;
154
        }
155
        return [];
156
    }
157
158
    /**
159
     * [setAllowedMethods Set the allowed methods for endpoints]
160
     * @param array $methods [GET, POST, PUT, PATCH, DELETE, ect..]
161
     */
162
    public function setAllowedMethods(array $methods)
163
    {
164
        $this->setHeader('Access-Control-Allow-Methods', array(
165
            implode(',', $methods),
166
        ));
167
168
        $requestMethod = $this->getServerMethod();
169
        if (!in_array($requestMethod, $methods)) {
170
            (new exception\errorException())
171
                ->setOptions($this->getOptions())
172
                ->error('METHOD_NOT_ALLOWED');
173
        }
174
    }
175
176
    /**
177
     * [getMethod Get the request method]
178
     * @return string
179
     */
180
    public function getServerMethod()
181
    {
182
        if (!isset($_SERVER['REQUEST_METHOD'])) {
183
            return '';
184
        }
185
        return $_SERVER['REQUEST_METHOD'];
186
    }
187
188
    /**
189
     * [getHeaders List all headers Server headers and Apache headers]
190
     * @return array
191
     */
192
    public function getHeaders()
193
    {
194
        $headers_list = $this->headersList();
195
        foreach ($headers_list as $index => $headValue) {
196
            @list($key, $value) = explode(": ", $headValue);
197
198
            if (!is_null($key) && !is_null($value)) {
199
                $headers_list[$key] = $value;
200
                unset($headers_list[$index]);
201
            }
202
        }
203
204
        if (!function_exists('apache_request_headers')) {
205
            $apacheRequestHeaders = $this->apacheRequestHeaders();
206
        } else {
207
            $apacheRequestHeaders = apache_request_headers();
208
        }
209
210
        if (is_null($apacheRequestHeaders) || empty($apacheRequestHeaders)) {
211
            return [];
212
        }
213
214
        $apache_headers = array_replace($headers_list, array_filter($apacheRequestHeaders));
0 ignored issues
show
Bug introduced by
It seems like $apacheRequestHeaders can also be of type true; however, parameter $array of array_filter() does only seem to accept array, 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

214
        $apache_headers = array_replace($headers_list, array_filter(/** @scrutinizer ignore-type */ $apacheRequestHeaders));
Loading history...
215
216
        $headers = array();
217
        foreach ($_SERVER as $key => $value) {
218
            if (substr($key, 0, 5) != 'HTTP_') {
219
                continue;
220
            }
221
            $header = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5)))));
222
            $headers[$header] = $value;
223
        }
224
225
        return array_merge($headers, $apache_headers);
226
    }
227
228
    /**
229
     * [headersList Get the default header list]
230
     * @return array
231
     */
232
    private function headersList()
233
    {
234
        /*$server = new server([], $this->getOptions());
235
        if ($isMockTest = $server->isMockTest()) {
236
            return $this->apacheRequestHeaders();
237
        }*/
238
239
        return headers_list();
240
    }
241
242
    /**
243
     * [apacheRequestHeaders Native replacment fuction]
244
     * https://www.php.net/manual/en/function.apache-request-headers.php#70810
245
     * @return array
246
     */
247
    public function apacheRequestHeaders()
248
    {
249
        $arh = array();
250
        $rx_http = '/\AHTTP_/';
251
252
        foreach ($_SERVER as $key => $val) {
253
            if (preg_match($rx_http, $key)) {
254
                $arh_key = preg_replace($rx_http, '', $key);
255
                $rx_matches = explode('_', $arh_key);
256
                if (count($rx_matches) > 0 and strlen($arh_key) > 2) {
257
                    foreach ($rx_matches as $ak_key => $ak_val) {
258
                        $rx_matches[$ak_key] = ucfirst($ak_val);
259
                    }
260
261
                    $arh_key = implode('-', $rx_matches);
262
                }
263
                $arh[$arh_key] = $val;
264
            }
265
        }
266
        return ($arh);
267
    }
268
269
    /**
270
     * [setHeader Append aditional headers]
271
     * @return void
272
     */
273
    public function setHeader($header, $headerValue = array(), $status = '', $delimiter = ';')
274
    {
275
        $header = trim(str_replace(':', '', $header)) . ': ';
276
        $headerValue = implode($delimiter . ' ', $headerValue);
277
278
        header($header . $status . $headerValue);
279
    }
280
281
    /**
282
     * [setHeaders Default headers]
283
     * @return void
284
     */
285
    public function setHeaders($CORS = false)
286
    {
287
        $auth_headers = $this->getHeaders();
288
        if (!array_key_exists('Content-Type', $auth_headers)) {
289
            $application = 'json';
290
            if (isset($this->REQUEST_APPLICATION[$this->getRequestType()])) {
291
                $application = $this->REQUEST_APPLICATION[$this->getRequestType()];
292
            }
293
            $this->setHeader('Content-Type', array(
294
                $application, 'charset=UTF-8',
295
            ));
296
        }
297
298
        $this->setHeader('Accept-Ranges', array(
299
            'bytes',
300
        ));
301
302
        $this->setHeader('Access-Control-Allow-Credentials', array(
303
            true,
304
        ));
305
306
        if (!array_key_exists('Access-Control-Allow-Methods', $auth_headers)) {
307
            $this->setHeader('Access-Control-Allow-Methods', array(
308
                'GET,POST,DELETE',
309
            ));
310
        }
311
312
        $this->setHeader('Access-Control-Expose-Headers', array(
313
            'Content-Range',
314
        ));
315
316
        $this->setHeader('Access-Control-Max-Age', array(
317
            $this->getMaxWindow(),
318
        ));
319
320
        $this->setHeader('Expires', array(
321
            'Wed, 20 September 1978 00:00:00 GMT',
322
        ));
323
324
        $this->setHeader('Cache-Control', array(
325
            'no-store, no-cache, must-revalidate',
326
        ));
327
328
        $this->setHeader('Cache-Control', array(
329
            'post-check=0, pre-check=0',
330
        ));
331
332
        $this->setHeader('Pragma', array(
333
            'no-cache',
334
        ));
335
336
        $this->setHeader('X-Content-Type-Options', array(
337
            'nosniff',
338
        ));
339
340
        $this->setHeader('X-XSS-Protection', array(
341
            '1', 'mode=block',
342
        ));
343
344
        if ($CORS) {
345
            if (
346
                isset($this->getOptions()['aditionalCORSHeaders']) &&
347
                (is_array($this->getOptions()['aditionalCORSHeaders']) &&
348
                !empty($this->getOptions()['aditionalCORSHeaders']))
349
            ) {
350
                $this->setAccessControllAllowedHeaders($this->getOptions()['aditionalCORSHeaders']);
351
            }
352
353
            $origin = ($auth_headers['Origin']) ?? false;
354
            $origin = ($origin) ? $auth_headers['Origin'] : '*';
355
            $this->setHeader('Access-Control-Allow-Origin', array(
356
                $origin,
357
            ));
358
359
            $this->setHeader('Access-Control-Allow-Headers', array(
360
                'origin,x-requested-with,Authorization,cache-control,content-type,x-header-csrf,x-auth-token'
361
                . $this->getAccessControllAllowedHeaders(),
362
            ));
363
364
            $this->setHeader('Access-Control-Allow-Methods', array(
365
                'GET,POST,OPTIONS',
366
            ));
367
        }
368
369
        if (
370
            isset($this->getOptions()['addHeaders']) &&
371
            (is_array($this->getOptions()['addHeaders']) && !empty($this->getOptions()['addHeaders']))
372
        ) {
373
            foreach ($this->getOptions()['addHeaders'] as $i => $customHeader) {
374
                if (is_array($customHeader) && sizeof($customHeader) == 2) {
375
                    $this->setHeader($customHeader[0], array(
376
                        $customHeader[1],
377
                    ));
378
                }
379
            }
380
        }
381
    }
382
383
    /**
384
     * [headerAuth]
385
     * @return object
386
     */
387
    public function headerAuth()
388
    {
389
        if (is_null($this->headerAuth)) {
390
            $this->headerAuth = new headerAuth();
391
        }
392
        $this->headerAuth->setOptions($this->getOptions());
393
        return $this->headerAuth;
394
    }
395
396
    /**
397
     * [authorizationHeaders Scan for "Authorization" header]
398
     * @return string|array [mixed: string / error]
399
     */
400
    public function authorizationHeaders($skipError = false)
401
    {
402
        return $this->headerAuth()->authorizationHeaders($skipError);
403
    }
404
405
    /**
406
     * [hasBearerToken Check if bearer token is present]
407
     * @return string|null
408
     */
409
    public function hasBearerToken()
410
    {
411
        return $this->headerAuth()->hasBearerToken();
412
    }
413
414
    /**
415
     * [unauthorised Set an unauthorised header]
416
     */
417
    public function unauthorised()
418
    {
419
        $this->headerAuth()->setUnauthorised();
420
    }
421
422
    /**
423
     * [getMaxWindow Get the max control age window]
424
     * @return integer
425
     */
426
    private function getMaxWindow()
427
    {
428
        if ($this->getOptions()) {
429
            if (isset($this->getOptions()['maxWindow']) && !empty($this->getOptions()['maxWindow'])) {
430
                if (!is_numeric($this->getOptions()['maxWindow'])) {
431
                    (new exception\errorException())
432
                        ->setOptions($this->getOptions())
433
                        ->message('maxWindow option must be an integer type')
434
                        ->error('APPLICATION_ERROR');
435
                }
436
437
                return $this->getOptions()['maxWindow'];
438
            }
439
        }
440
        return self::MAX_WINDOW;
441
    }
442
443
    /**
444
     * [setHeaderStatus]
445
     * @return void
446
     */
447
    public function setHeaderStatus($status)
448
    {
449
        http_response_code($status);
450
    }
451
452
    /**
453
     * [getHeaderStatus]
454
     * @return integer
455
     */
456
    public function getHeaderStatus()
457
    {
458
        return http_response_code();
0 ignored issues
show
Bug Best Practice introduced by
The expression return http_response_code() also could return the type true which is incompatible with the documented return type integer.
Loading history...
459
    }
460
461
    /**
462
     * [setData Set request method data]
463
     * @param array $data
464
     * @return void
465
     */
466
    public function setData($data = [])
467
    {
468
        $this->REQUEST_METHOD['data'] = $data;
469
    }
470
471
    /**
472
     * Set additional CORS control headers
473
     *
474
     * @param array
475
     */
476
    private function setAccessControllAllowedHeaders(array $allowedHeaders): array
477
    {
478
        $this->additionalCORSHeaders = $allowedHeaders;
479
        return $this->additionalCORSHeaders;
480
    }
481
482
    /**
483
     * Get a list of additional CORS control headers set by the config
484
     *
485
     * @return string
486
     */
487
    private function getAccessControllAllowedHeaders(): string
488
    {
489
        if (empty($this->additionalCORSHeaders)) {
490
            return '';
491
        }
492
493
        return ',' . implode(',', $this->additionalCORSHeaders);
494
    }
495
}
496