Passed
Push — master ( 131ae1...4b0040 )
by Vince
01:44
created

header::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 0
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 2
rs 10
1
<?php
2
/**
3
 * ==================================
4
 * Responsible PHP API
5
 * ==================================
6
 *
7
 * @link Git https://github.com/vince-scarpa/responsibleAPI.git
8
 *
9
 * @api Responible API
10
 * @package responsible\core\headers
11
 *
12
 * @author Vince scarpa <[email protected]>
13
 *
14
 */
15
namespace responsible\core\headers;
16
17
use responsible\core\auth;
18
use responsible\core\exception;
19
use responsible\core\interfaces;
20
use responsible\core\server;
21
22
class header extends server implements interfaces\optionsInterface
23
{
24
    use \responsible\core\traits\optionsTrait;
25
26
    /**
27
     * Max age constant
28
     */
29
    const MAX_WINDOW = 3600;
30
31
    /**
32
     * [$REQUEST_APPLICATION]
33
     * @var array
34
     */
35
    private $REQUEST_APPLICATION = array(
36
        'xml' => 'text/xml',
37
        'json' => 'application/json',
38
        'html' => 'text/html',
39
        'array' => 'text/plain',
40
        'object' => 'text/plain',
41
    );
42
43
    /**
44
     * [$REQUEST_TYPE / Default is json]
45
     * @var string
46
     */
47
    private $REQUEST_TYPE = 'json';
48
49
    /**
50
     * [$REQUEST_METHOD]
51
     * @var array
52
     */
53
    private $REQUEST_METHOD = [];
54
55
    /**
56
     * [$headerAuth Header authorise class object]
57
     * @var object
58
     */
59
    protected $headerAuth;
60
61
    /**
62
     * Options can set additional CORS headers
63
     * @var array
64
     */
65
    private $additionalCORSHeaders = [];
66
67
    /**
68
     * [__construct]
69
     */
70
    public function __construct()
71
    {}
72
73
    /**
74
     * [requestType]
75
     * @return void
76
     */
77
    public function requestType($type = 'json')
78
    {
79
        $this->REQUEST_TYPE = $type;
80
    }
81
82
    /**
83
     * [getRequestType]
84
     * @return string
85
     */
86
    public function getRequestType()
87
    {
88
        return $this->REQUEST_TYPE;
89
    }
90
91
    /**
92
     * [requestMethod Set and return the request method]
93
     * @return object
94
     */
95
    public function requestMethod()
96
    {
97
        $verbs = new headerVerbs;
98
99
        switch (strtolower($_SERVER['REQUEST_METHOD'])) {
100
101
            case 'get':
102
                return $this->REQUEST_METHOD = $verbs->get();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->REQUEST_METHOD = $verbs->get() returns the type array<string,array|mixed|string> which is incompatible with the documented return type object.
Loading history...
103
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
104
105
            case 'post':
106
                return $this->REQUEST_METHOD = $verbs->post();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->REQUEST_METHOD = $verbs->post() returns the type array<string,array|string> which is incompatible with the documented return type object.
Loading history...
107
                break;
108
109
            case 'options':
110
                $isOriginRequest = ($_SERVER['HTTP_ORIGIN']) ?? false;
111
                $this->REQUEST_METHOD = $verbs->post();
112
                echo json_encode(['success' => true]);
113
                $this->setHeaders($isOriginRequest);
114
                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...
115
                break;
116
117
            case 'put':
118
                $this->REQUEST_METHOD = $verbs->put();
119
                break;
120
121
            case 'patch':
122
                $this->REQUEST_METHOD = $verbs->patch();
123
                break;
124
125
            case 'delete':
126
                $this->REQUEST_METHOD = $verbs->delete();
127
                break;
128
129
            default:
130
                $this->REQUEST_METHOD = [];
131
                break;
132
        }
133
    }
134
135
    /**
136
     * [getMethod Get the request method]
137
     * @return object
138
     */
139
    public function getMethod()
140
    {
141
        return (object) $this->REQUEST_METHOD;
142
    }
143
144
    /**
145
     * [getBody Get the post body]
146
     * @return array
147
     */
148
    public function getBody():array
149
    {
150
        if (isset($this->getMethod()->data) && !empty($this->getMethod()->data)) {
151
            return $this->getMethod()->data;
152
        }
153
        return [];
154
    }
155
156
    /**
157
     * [setAllowedMethods Set the allowed methods for endpoints]
158
     * @param array $methods [GET, POST, PUT, PATCH, DELETE, ect..]
159
     */
160
    public function setAllowedMethods(array $methods)
161
    {
162
        $this->setHeader('Access-Control-Allow-Methods', array(
163
            implode(',', $methods),
164
        ));
165
166
        $requestMethod = $this->getServerMethod();
167
        if (!in_array($requestMethod, $methods)) {
168
            (new exception\errorException)
169
                ->setOptions($this->getOptions())
170
                ->error('METHOD_NOT_ALLOWED');
171
        }
172
    }
173
174
    /**
175
     * [getMethod Get the request method]
176
     * @return string
177
     */
178
    public function getServerMethod()
179
    {
180
        if (!isset($_SERVER['REQUEST_METHOD'])) {
181
            return '';
182
        }
183
        return $_SERVER['REQUEST_METHOD'];
184
    }
185
186
    /**
187
     * [getHeaders List all headers Server headers and Apache headers]
188
     * @return array
189
     */
190
    public function getHeaders()
191
    {
192
        $headers_list = $this->headersList();
193
        foreach ($headers_list as $index => $headValue) {
194
            @list($key, $value) = explode(": ", $headValue);
195
196
            if (!is_null($key) && !is_null($value)) {
197
                $headers_list[$key] = $value;
198
                unset($headers_list[$index]);
199
            }
200
        }
201
202
        if (!function_exists('apache_request_headers')) {
203
            $apacheRequestHeaders = $this->apacheRequestHeaders();
204
        } else {
205
            $apacheRequestHeaders = apache_request_headers();
206
        }
207
208
        if (is_null($apacheRequestHeaders) || empty($apacheRequestHeaders)) {
209
            return [];
210
        }
211
212
        $apache_headers = array_merge($headers_list, $apacheRequestHeaders);
0 ignored issues
show
Bug introduced by
It seems like $apacheRequestHeaders can also be of type true; however, parameter $arrays of array_merge() 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

212
        $apache_headers = array_merge($headers_list, /** @scrutinizer ignore-type */ $apacheRequestHeaders);
Loading history...
213
214
        $headers = array();
215
216
        foreach ($_SERVER as $key => $value) {
217
            if (substr($key, 0, 5) != 'HTTP_') {
218
                continue;
219
            }
220
            $header = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5)))));
221
            $headers[$header] = $value;
222
        }
223
224
        return array_merge($headers, $apache_headers);
225
    }
226
227
    /**
228
     * [headersList Get the default header list]
229
     * @return array
230
     */
231
    private function headersList()
232
    {
233
        /*$server = new server([], $this->getOptions());
234
        if ($isMockTest = $server->isMockTest()) {
235
            return $this->apacheRequestHeaders();
236
        }*/
237
        
238
        return headers_list();
239
    }
240
241
    /**
242
     * [apacheRequestHeaders Native replacment fuction]
243
     * https://www.php.net/manual/en/function.apache-request-headers.php#70810
244
     * @return array
245
     */
246
    public function apacheRequestHeaders()
247
    {
248
        $arh = array();
249
        $rx_http = '/\AHTTP_/';
250
251
        foreach ($_SERVER as $key => $val) {
252
            if (preg_match($rx_http, $key)) {
253
                $arh_key = preg_replace($rx_http, '', $key);
254
                $rx_matches = explode('_', $arh_key);
255
                if (count($rx_matches) > 0 and strlen($arh_key) > 2) {
256
                    foreach ($rx_matches as $ak_key => $ak_val) {
257
                        $rx_matches[$ak_key] = ucfirst($ak_val);
258
                    }
259
260
                    $arh_key = implode('-', $rx_matches);
261
                }
262
                $arh[$arh_key] = $val;
263
            }
264
        }
265
        return ($arh);
266
    }
267
268
    /**
269
     * [setHeader Append aditional headers]
270
     * @return void
271
     */
272
    public function setHeader($header, $headerValue = array(), $status = '', $delimiter = ';')
273
    {
274
        $header = trim(str_replace(':', '', $header)) . ': ';
275
        $headerValue = implode($delimiter . ' ', $headerValue);
276
277
        header($header . $status . $headerValue);
278
    }
279
280
    /**
281
     * [setHeaders Default headers]
282
     * @return void
283
     */
284
    public function setHeaders($CORS = false)
285
    {
286
        $auth_headers = $this->getHeaders();
287
        $application = 'json';
288
289
        if (isset($this->REQUEST_APPLICATION[$this->getRequestType()])) {
290
            $application = $this->REQUEST_APPLICATION[$this->getRequestType()];
291
        }
292
293
        $this->setHeader('Content-Type', array(
294
            $application, 'charset=UTF-8',
295
        ));
296
297
        $this->setHeader('Accept-Ranges', array(
298
            'bytes',
299
        ));
300
301
        $this->setHeader('Access-Control-Allow-Credentials', array(
302
            true,
303
        ));
304
305
        if (!array_key_exists('Access-Control-Allow-Methods', $this->getHeaders())) {
306
            $this->setHeader('Access-Control-Allow-Methods', array(
307
                'GET,POST,DELETE',
308
            ));
309
        }
310
311
        $this->setHeader('Access-Control-Expose-Headers', array(
312
            'Content-Range',
313
        ));
314
315
        $this->setHeader('Access-Control-Max-Age', array(
316
            $this->getMaxWindow(),
317
        ));
318
319
        $this->setHeader('Expires', array(
320
            'Wed, 20 September 1978 00:00:00 GMT',
321
        ));
322
323
        $this->setHeader('Cache-Control', array(
324
            'no-store, no-cache, must-revalidate',
325
        ));
326
327
        $this->setHeader('Cache-Control', array(
328
            'post-check=0, pre-check=0',
329
        ));
330
331
        $this->setHeader('Pragma', array(
332
            'no-cache',
333
        ));
334
335
        $this->setHeader('X-Content-Type-Options', array(
336
            'nosniff',
337
        ));
338
339
        $this->setHeader('X-XSS-Protection', array(
340
            '1', 'mode=block',
341
        ));
342
343
        if ($CORS) {
344
            if (isset($this->getOptions()['aditionalCORSHeaders']) &&
345
                (is_array($this->getOptions()['aditionalCORSHeaders']) && !empty($this->getOptions()['aditionalCORSHeaders']))
346
            ) {
347
                $this->additionalCORSHeaders = ','.implode(',', $this->getOptions()['aditionalCORSHeaders']);
0 ignored issues
show
Documentation Bug introduced by
It seems like ',' . implode(',', $this...aditionalCORSHeaders']) of type string is incompatible with the declared type array of property $additionalCORSHeaders.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
348
            }
349
350
            $origin = ($auth_headers['Origin']) ?? false;
351
            $origin = ($origin) ? $auth_headers['Origin'] : '*';
352
            $this->setHeader('Access-Control-Allow-Origin', array(
353
                $origin,
354
            ));
355
356
            $this->setHeader('Access-Control-Allow-Headers', array(
357
                'origin,x-requested-with,Authorization,cache-control,content-type,x-header-csrf,x-auth-token'
358
                .$this->additionalCORSHeaders,
0 ignored issues
show
Bug introduced by
Are you sure $this->additionalCORSHeaders of type array can be used in concatenation? ( Ignorable by Annotation )

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

358
                ./** @scrutinizer ignore-type */ $this->additionalCORSHeaders,
Loading history...
359
            ));
360
361
            $this->setHeader('Access-Control-Allow-Methods', array(
362
                'GET,POST,OPTIONS',
363
            ));
364
        }
365
366
        if (isset($this->getOptions()['addHeaders']) &&
367
            (is_array($this->getOptions()['addHeaders']) && !empty($this->getOptions()['addHeaders']))
368
        ) {
369
            foreach ($this->getOptions()['addHeaders'] as $i => $customHeader) {
370
                if (is_array($customHeader) && sizeof($customHeader) == 2) {
371
                    $this->setHeader($customHeader[0], array(
372
                        $customHeader[1],
373
                    ));
374
                }
375
            }
376
        }
377
    }
378
379
    /**
380
     * [headerAuth]
381
     * @return object
382
     */
383
    public function headerAuth()
384
    {
385
        if (is_null($this->headerAuth)) {
386
            $this->headerAuth = new headerAuth;
387
        }
388
        $this->headerAuth->setOptions($this->getOptions());
389
        return $this->headerAuth;
390
    }
391
392
    /**
393
     * [authorizationHeaders Scan for "Authorization" header]
394
     * @return string|array [mixed: string / error]
395
     */
396
    public function authorizationHeaders($skipError = false)
397
    {
398
        return $this->headerAuth()->authorizationHeaders($skipError);
399
    }
400
401
    /**
402
     * [hasBearerToken Check if bearer token is present]
403
     * @return string|null
404
     */
405
    public function hasBearerToken()
406
    {
407
        return $this->headerAuth()->hasBearerToken();
408
    }
409
410
    /**
411
     * [unauthorised Set an unauthorised header]
412
     * @return array [exit exception message]
413
     */
414
    public function unauthorised()
415
    {
416
        $this->headerAuth()->setUnauthorised();
417
    }
418
419
    /**
420
     * [getMaxWindow Get the max control age window]
421
     * @return integer
422
     */
423
    private function getMaxWindow()
424
    {
425
        if ($this->getOptions()) {
426
            if (isset($this->getOptions()['maxWindow']) && !empty($this->getOptions()['maxWindow'])) {
427
                if (!is_numeric($this->getOptions()['maxWindow'])) {
428
                    (new exception\errorException)
429
                        ->setOptions($this->getOptions())
430
                        ->message('maxWindow option must be an integer type')
431
                        ->error('APPLICATION_ERROR');
432
                }
433
434
                return $this->getOptions()['maxWindow'];
435
            }
436
        }
437
        return self::MAX_WINDOW;
438
    }
439
440
    /**
441
     * [setHeaderStatus]
442
     * @param void
443
     */
444
    public function setHeaderStatus($status)
445
    {
446
        http_response_code($status);
447
    }
448
449
    /**
450
     * [getHeaderStatus]
451
     * @return integer
452
     */
453
    public function getHeaderStatus()
454
    {
455
        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...
456
    }
457
458
    /**
459
     * [setData Set request method data]
460
     * @param array $data
461
     * @return void
462
     */
463
    public function setData($data = [])
464
    {
465
        $this->REQUEST_METHOD['data'] = $data;
466
    }
467
}
468