Passed
Push — master ( 7a8481...6a477e )
by Francis
01:21
created

REST::api_key_auth()   B

Complexity

Conditions 10
Paths 13

Size

Total Lines 29
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 10
eloc 13
c 3
b 0
f 0
nc 13
nop 1
dl 0
loc 29
rs 7.6666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
defined('BASEPATH') OR exit('No direct script access allowed');
4
5
require_once('RESTAuth.php');
6
require_once('RESTResponse.php');
7
require_once('RESTExceptions.php');
8
9
class REST
10
{
11
  /**
12
   * [private description]
13
   * @var [type]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
14
   */
15
  private $ci;
16
17
  /**
18
   * [private description]
19
   * @var [type]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
20
   */
21
  private $api_key_limit_column;
22
23
  /**
24
   * [private description]
25
   * @var [type]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
26
   */
27
  private $api_key_column;
28
29
  /**
30
   * [private description]
31
   * @var [type]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
32
   */
33
  private $per_hour;
34
35
  /**
36
   * [private description]
37
   * @var [type]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
38
   */
39
  private $ip_per_hour;
40
41
  /**
42
   * [private description]
43
   * @var [type]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
44
   */
45
  private $show_header;
46
47
  /**
48
   * [private description]
49
   * @var [type]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
50
   */
51
  private $whitelist;
52
53
  /**
54
   * [private description]
55
   * @var [type]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
56
   */
57
  private $checked_rate_limit = false;
58
59
  /**
60
   * [private description]
61
   * @var [type]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
62
   */
63
  private $header_prefix;
64
65
  /**
66
   * [private description]
67
   * @var [type]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
68
   */
69
  private $limit_api;
70
71
  /**
72
   * [public description]
73
   * @var [type]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
74
   */
75
  public  $userId;
76
77
  /**
78
   * [public description]
79
   * @var [type]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
80
   */
81
  public $apiKey;
82
83
  /**
84
   * [public description]
85
   * @var [type]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
86
   */
87
  public  $apiKeyHeader;
88
89
  /**
90
   * [public description]
91
   * @var [type]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
92
   */
93
  public $token;
94
95
  /**
96
   * [public description]
97
   * @var [type]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
98
   */
99
  public $allowedIps;
100
101
  /**
102
   * [public description]
103
   * @var [type]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
104
   */
105
  public $config;
106
107
  /**
108
   * [public description]
109
   * @var [type]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
110
   */
111
  public $authPreempted = false;
112
113
  /**
114
   * [PACKAGE description]
115
   * @var string
116
   */
117
  const PACKAGE = "francis94c/ci-rest";
118
119
  /**
120
   * [RATE_LIMIT description]
121
   * @var string
122
   */
123
  const RATE_LIMIT = "RateLimit";
124
125
  /**
126
   * [AUTH_GRAVITY description]
127
   * @var integer
128
   */
129
  const AUTH_GRAVITY = 0b100;
130
  const AUTH_PASSIVE = 0b010;
131
  const AUTH_FINAL   = 0b001;
132
133
  /**
134
   * [__construct This is the part of the code that takes care of all
135
   * authentiations. allowing you to focus on building wonderful things at REST.
136
   * pun intended ;-)]
137
   * @param array|null $params Initialization parameters from the Slint system.
138
   *                           There's no use for this arg yet.
139
   */
140
  function __construct(?array $params=null)
141
  {
142
    $this->ci =& get_instance();
0 ignored issues
show
Bug introduced by
The function get_instance was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

142
    $this->ci =& /** @scrutinizer ignore-call */ get_instance();
Loading history...
143
144
    if ($this->ci->input->is_cli_request()) return;
145
146
    // Load Config If Exists.
147
    //$this->ci->config->load('rest', true, true);
148
    if (is_file(APPPATH . 'config/rest.php')) {
0 ignored issues
show
Bug introduced by
The constant APPPATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
149
      include APPPATH . 'config/rest.php';
150
    }
151
152
    $this->config = $config;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $config seems to be never defined.
Loading history...
153
154
    // Load Database.
155
    $this->ci->load->database();
156
157
    // load URL Helper
158
    $this->ci->load->helper('url');
159
160
    // Load REST Helper.
161
    $this->ci->load->splint(self::PACKAGE, '%rest');
162
163
    // Load Model.
164
    $this->ci->load->splint(self::PACKAGE, '*RESTModel', 'rest_model');
165
    $this->rest_model =& $this->ci->rest_model;
0 ignored issues
show
Bug Best Practice introduced by
The property rest_model does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
166
167
    $this->rest_model->init([
168
      'users_table'           => $config['basic_auth']['users_table'] ?? null,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $config seems to never exist and therefore isset should always be false.
Loading history...
169
      'users_id_column'       => $config['basic_auth']['id_column'] ?? null,
170
      'users_username_column' => $config['basic_auth']['username_column'] ?? null,
171
      'users_email_column'    => $config['basic_auth']['email_column'] ?? null,
172
      'users_password_column' => $config['basic_auth']['password_column'] ?? null,
173
      'api_key_table'         => $config['api_key_auth']['api_key_table'] ?? null,
174
      'api_key_column'        => $config['api_key_auth']['api_key_column'] ?? null,
175
      'api_key_limit_column'  => $config['api_key_auth']['api_key_limit_column'] ?? null
176
    ]);
177
178
    // Load Variable(s) from Config.
179
    $this->allowedIps = $config['allowed_ips'] ?? ['127.0.0.1', '[::1]'];
180
    $this->apiKeyHeader = $config['api_key_header'] ?? 'X-API-KEY';
181
    $this->api_key_limit_column = $config['api_key_auth']['api_key_limit_column'] ?? null;
182
    $this->api_key_column = $config['api_key_auth']['api_key_column'] ?? null;
183
    $this->limit_api = $config['api_limiter']['api_limiter'] ?? false;
184
    $this->per_hour = $config['api_limiter']['per_hour'] ?? 100;
185
    $this->ip_per_hour = $config['api_limiter']['ip_per_hour'] ?? 50;
186
    $this->show_header = $config['api_limiter']['show_header'] ?? null;
187
    $this->whitelist = $config['api_limiter']['whitelist'] ?? [];
188
    $this->header_prefix = $config['api_limiter']['header_prefix'] ?? 'X-RateLimit-';
189
190
    // Limit Only?
191
    //if ($this->config['api_limiter']['api_limit_only'] ?? false) {
192
      //return;
193
    //}
194
195
    // Authenticate
196
    $this->authenticate();
197
198
    // Generic Rate Limiter.
199
    if ($this->limit_api && !$this->checked_rate_limit &&
200
    ($config['api_limiter']['limit_by_ip'] ?? false)) {
201
      $this->api_rest_limit_by_ip_address();
202
    }
203
204
    log_message('debug', 'REST Request Authenticated and REST Library Initialized.');
0 ignored issues
show
Bug introduced by
The function log_message was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

204
    /** @scrutinizer ignore-call */ 
205
    log_message('debug', 'REST Request Authenticated and REST Library Initialized.');
Loading history...
205
  }
206
207
  /**
208
   * [authenticate description]
209
   * @date 2020-01-30
210
   */
211
  private function authenticate():void
212
  {
213
    $auths = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $auths is dead and can be removed.
Loading history...
214
    $auths = $this->config['auth'] ?? null;
215
    if ($auths) $auths = is_array($auths) ? $auths : [$auths];
216
217
    if (!$auths) return; // No authentication(s) to carry out.
218
219
    /**
220
     * $this->process_auth() terminates the script if authentication fails
221
     * It will call the callable in the rest.php config file under
222
     * response_callbacks which matches the necesarry RESTResponse constant
223
     * before exiting. Which callable is called in any situation is documented
224
     * in README.md
225
     */
226
227
    foreach ($auths as $key => $auth) {
228
      if ($this->authPreempted) break;
229
      if (is_numeric($key)) {
230
        $this->process_auth($auth, self::AUTH_GRAVITY);
231
      } else {
232
        $this->process_auth($key, $auth);
233
      }
234
    }
235
  }
236
237
  /**
238
   * [process_auth description]
239
   * @date  2020-04-07
240
   * @param string     $auth  [description]
241
   * @param int        $flags [description]
242
   */
243
  private function process_auth(string &$auth, int $flags):void
244
  {
245
    switch ($auth) {
246
      case RESTAuth::IP: $this->ip_auth($flags); break;
247
      case RESTAuth::BASIC: $this->basic_auth($flags); break;
248
      case RESTAuth::API_KEY: $this->api_key_auth($flags); break;
249
      case RESTAuth::OAUTH2: $this->bearer_auth(RESTAuth::OAUTH2, $flags); break;
250
      case RESTAuth::BEARER: $this->bearer_auth(RESTAuth::BEARER, $flags); break;
251
      case RESTAuth::SECRET: $this->bearer_auth(RESTAuth::SECRET, $flags); break;
252
      default: $this->custom_auth($auth, $flags);
0 ignored issues
show
Unused Code introduced by
The call to REST::custom_auth() has too many arguments starting with $flags. ( Ignorable by Annotation )

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

252
      default: $this->/** @scrutinizer ignore-call */ custom_auth($auth, $flags);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
253
    }
254
  }
255
256
  /**
257
   * [auth_proceed description]
258
   * @date   2020-04-07
259
   * @param  bool       $success [description]
260
   * @param  int        $flags   [description]
261
   * @return bool                [description]
262
   */
263
  private function auth_proceed(bool $success, int $flags):bool
264
  {
265
    if ($flags & self::AUTH_GRAVITY) return $success;
266
    if ($success) {
267
      if ($flags & self::AUTH_FINAL) {
268
        $this->authPreempted = true;
269
        return true;
270
      }
0 ignored issues
show
Bug Best Practice introduced by
The function implicitly returns null when the if condition on line 267 is false. This is incompatible with the type-hinted return boolean. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
271
    } else {
272
      return $flags & self::AUTH_PASSIVE ? true : false;
273
    }
274
  }
275
276
  /**
277
   * [ip_auth description]
278
   * @date  2020-04-07
279
   * @param int        $flags [description]
280
   */
281
  private function ip_auth(int $flags):void
282
  {
283
    if (!$this->auth_proceed(in_array($this->ci->input->ip_address(), $this->allowedIps), $flags)) {
284
      $this->handle_response(RESTResponse::UN_AUTHORIZED, RESTAuth::IP); // Exits.
285
    }
286
  }
287
288
  /**
289
   * [bearer_auth description]
290
   * @date  2020-04-07
291
   * @param string     $auth  [description]
292
   * @param int        $flags [description]
293
   */
294
  private function bearer_auth(string $auth, int $flags):void
295
  {
296
    $authorization = $this->get_authorization_header();
297
    $shouldProceed = $this->auth_proceed(false, $flags);
298
    if ($authorization == null || substr_count($authorization, ' ') != 1 && !$shouldProceed) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $authorization of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
299
      $this->handle_response(RESTResponse::BAD_REQUEST, $auth, 'Bad Request'); // Exits.
300
    }
301
    $token = explode(" ", $authorization);
302
    if ($token[0] != $auth && !$shouldProceed) {
303
      $this->handle_response(RESTResponse::BAD_REQUEST, $auth, 'Bad Request'); // Exits.
304
    }
305
    $this->token = $token[1];
306
    // Call Up Custom Implemented Bearer/Token Authorization.
307
    // Callback Check.
308
    if (!isset($this->config['auth_callbacks'][$auth])) {
309
      $this->handle_response(RESTResponse::NOT_IMPLEMENTED, $auth); // Exits.
310
    }
311
    // Authorization.
312
    if (!$this->auth_proceed($this->config['auth_callbacks'][$auth]($this, $this->token), $flags)) {
313
      $this->handle_response(RESTResponse::UN_AUTHORIZED, $auth); // Exits.
314
    }
315
  }
316
317
  /**
318
   * [basic_auth description]
319
   * @date  2020-04-07
320
   * @param int        $flags [description]
321
   */
322
  private function basic_auth(int $flags):void
323
  {
324
    $username = $_SERVER['PHP_AUTH_USER'] ?? null;
325
    $password = $_SERVER['PHP_AUTH_PW'] ?? null;
326
    if (!$this->auth_proceed(!$username || !$password, $flags)) $this->handle_response(RESTResponse::BAD_REQUEST, RESTAuth::BASIC); // Exits.
327
    if (!$this->auth_proceed($this->rest_model->basicAuth($this, $username, $password), $flags)) $this->handle_response(RESTResponse::UN_AUTHORIZED, RESTAuth::BASIC); // Exits.
328
  }
329
  /**
330
   * [api_key_auth description]
331
   */
332
  private function api_key_auth(int $flags=self::AUTH_GRAVITY):void
333
  {
334
    if (uri_string() == '')  return;
0 ignored issues
show
Bug introduced by
The function uri_string was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

334
    if (/** @scrutinizer ignore-call */ uri_string() == '')  return;
Loading history...
335
    $shouldProceed = $this->auth_proceed(false, $flags);
336
337
    if (!$this->ci->input->get_request_header($this->apiKeyHeader, true) && !$shouldProceed) {
338
    // if (!isset($_SERVER['HTTP_' . str_replace("-", "_", $this->apiKeyHeader)])) {
339
      $this->handle_response(RESTResponse::BAD_REQUEST, RESTAuth::API_KEY); // Exits.
340
    }
341
342
    $apiKey = $this->rest_model->getAPIKeyData(
343
      $this->ci->input->get_request_header($this->apiKeyHeader, true)
344
    );
345
346
    if ($apiKey == null && !$shouldProceed) {
347
      $this->handle_response(RESTResponse::UN_AUTHORIZED, RESTAuth::API_KEY); // Exits.
348
    }
349
350
    $this->apiKey = $apiKey;
351
352
    if (!$this->auth_proceed(true, $flags)) return;
353
354
    // ==== API KEY Auth Passed ==== //
355
356
    if ($this->limit_api && $this->api_key_limit_column != null && $apiKey->{$this->api_key_limit_column} == 1) {
357
      $this->limitAPIKey($apiKey->{$this->api_key_column});
358
    }
359
360
    $this->checked_rate_limit = true; // Ignore Limit By IP.
361
  }
362
363
  /**
364
   * [limitAPIKey description]
365
   * @date  2020-04-08
366
   * @param string     $apiKey [description]
367
   */
368
  public function limitAPIKey(string $apiKey):void
369
  {
370
    // Trunctate Rate Limit Data.
371
    $this->rest_model->truncateRatelimitData();
372
    // Check Whitelist.
373
    if (in_array($this->ci->input->ip_address(), $this->whitelist)) {
374
      $this->checked_rate_limit = true; // Ignore Limit By IP.
375
      return;
376
    }
377
    // Should we acyually Limit?
378
    if ($this->per_hour > 0) {
379
      $client = hash('md5', $this->ci->input->ip_address() . "%" . $apiKey);
380
      $limitData = $this->rest_model->getLimitData($client, '_api_keyed_user');
381
      if ($limitData == null) {
382
        $limitData = [];
383
        $limitData['count'] = 0;
384
        $limitData['reset_epoch'] = gmdate('d M Y H:i:s', time() + (60 * 60));
385
        $limitData['start'] = date('d M Y H:i:s');
386
      }
387
      if ($this->per_hour - $limitData['count'] > 0) {
388
        if (!$this->rest_model->insertLimitData($client, '_api_keyed_user')) {
389
          $this->handle_response(RESTResponse::INTERNAL_SERVER_ERROR, self::RATE_LIMIT); // Exits.
390
        }
391
        ++$limitData['count'];
392
        if ($this->show_header) {
393
          header($this->header_prefix.'Limit: '.$this->per_hour);
394
          header($this->header_prefix.'Remaining: '.($this->per_hour - $limitData['count']));
395
          header($this->header_prefix.'Reset: '.strtotime($limitData['reset_epoch']));
396
        }
397
      } else {
398
        header('Retry-After: '.(strtotime($limitData['reset_epoch']) - strtotime(gmdate('d M Y H:i:s'))));
399
        $this->handle_response(RESTResponse::TOO_MANY_REQUESTS, self::RATE_LIMIT); // Exits.
400
      }
401
    }
402
  }
403
404
  /**
405
   * [api_rest_limit_by_ip_address description]
406
   * TODO: Implement.
407
   */
408
  private function api_rest_limit_by_ip_address():void
409
  {
410
    // Trunctate Rate Limit Data.
411
    $this->rest_model->truncateRatelimitData();
412
    // Check Whitelist.
413
    if (in_array($this->ci->input->ip_address(), $this->whitelist)) return;
414
    // Should we acyually Limit?
415
    if ($this->ip_per_hour > 0) {
416
      $client = hash('md5', $this->ci->input->ip_address());
417
      $limitData = $this->rest_model->getLimitData($client, '_ip_address');
418
      if ($limitData == null) {
419
        $limitData = [];
420
        $limitData['count'] = 0;
421
        $limitData['reset_epoch'] = gmdate('d M Y H:i:s', time() + (60 * 60));
422
        $limitData['start'] = date('d M Y H:i:s');
423
      }
424
      if ($this->ip_per_hour - $limitData['count'] > 0) {
425
        if (!$this->rest_model->insertLimitData($client, '_ip_address')) {
426
          $this->handle_response(RESTResponse::INTERNAL_SERVER_ERROR, self::RATE_LIMIT); // Exits.
427
        }
428
        ++$limitData['count'];
429
        if ($this->show_header) {
430
          header($this->header_prefix.'Limit: '.$this->ip_per_hour);
431
          header($this->header_prefix.'Remaining: '.($this->ip_per_hour - $limitData['count']));
432
          header($this->header_prefix.'Reset: '.strtotime($limitData['reset_epoch']));
433
        }
434
      } else {
435
        header('Retry-After: '.(strtotime($limitData['reset_epoch']) - strtotime(gmdate('d M Y H:i:s'))));
436
        $this->handle_response(RESTResponse::TOO_MANY_REQUESTS, self::RATE_LIMIT); // Exits.
437
      }
438
    }
439
  }
440
  /**
441
   * [custom_auth description]
442
   * @param string $auth [description]
443
   */
444
  private function custom_auth(string &$auth):void
445
  {
446
    // Header Check.
447
    if (!isset($_SERVER[$auth])) {
448
      $this->handle_response(RESTResponse::BAD_REQUEST, $auth);
449
    }
450
    // Callback Check.
451
    if (!isset($this->config['auth_callbacks'][$auth])) {
452
      $this->handle_response(RESTResponse::NOT_IMPLEMENTED, $auth); // Exits.
453
    }
454
    // Authentication.
455
    if (!$this->config['auth_callbacks'][$auth]($this, $this->ci->security->xss_clean($_SERVER[$auth]))) {
456
      $this->handle_response(RESTResponse::UN_AUTHORIZED, $auth); // Exits.
457
    }
458
  }
459
  /**
460
   * [get_authorization_header description]
461
   * @return [type] [description]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
462
   */
463
  private function get_authorization_header():?string
464
  {
465
    if (isset($_SERVER['Authorization'])) {
466
      return trim($_SERVER["Authorization"]);
467
    } else if (isset($_SERVER['HTTP_AUTHORIZATION'])) { //Nginx or fast CGI
468
      return trim($_SERVER["HTTP_AUTHORIZATION"]);
469
    } elseif (function_exists('apache_request_headers')) {
470
      $requestHeaders = apache_request_headers();
471
472
      // Avoid Surprises.
473
      $requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders));
0 ignored issues
show
Bug introduced by
It seems like $requestHeaders can also be of type false; however, parameter $input of array_keys() 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

473
      $requestHeaders = array_combine(array_map('ucwords', array_keys(/** @scrutinizer ignore-type */ $requestHeaders)), array_values($requestHeaders));
Loading history...
Bug introduced by
It seems like $requestHeaders can also be of type false; however, parameter $input of array_values() 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

473
      $requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values(/** @scrutinizer ignore-type */ $requestHeaders));
Loading history...
474
475
      if (isset($requestHeaders['Authorization'])) {
476
        return trim($requestHeaders['Authorization']);
477
      }
478
    }
479
    return null;
480
  }
481
482
  /**
483
   * [handle_response description]
484
   * @param int $code [description]
485
   */
486
  private function handle_response(int $code, $auth=null, ?string $errorReason=null):void
487
  {
488
    http_response_code($code);
489
    header("Content-Type: application/json");
490
    if (isset($this->config['response_callbacks'][$code])) {
491
      $this->config['response_callbacks'][$code]($auth, $errorReason);
492
    }
493
    if (ENVIRONMENT != 'testing') exit($code);
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...
Bug introduced by
The constant ENVIRONMENT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
494
    throw new Exception("Error $code in $auth", $code);
495
  }
496
}
497
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...
498