server::route()   C
last analyzed

Complexity

Conditions 11
Paths 192

Size

Total Lines 99
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 11
Bugs 0 Features 1
Metric Value
cc 11
eloc 45
c 11
b 0
f 1
nc 192
nop 1
dl 0
loc 99
rs 6.55

How to fix   Long Method    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
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
12
 *
13
 * @author Vince scarpa <[email protected]>
14
 *
15
 */
16
17
namespace responsible\core;
18
19
use responsible\core\auth;
20
use responsible\core\configuration;
21
use responsible\core\connect;
22
use responsible\core\endpoints;
23
use responsible\core\exception;
24
use responsible\core\headers;
25
use responsible\core\keys;
26
use responsible\core\request;
27
use responsible\core\route;
28
use responsible\core\throttle;
29
30
class server
31
{
32
    /**
33
     * [$config]
34
     * @var object
35
     */
36
    protected $config;
37
38
    /**
39
     * [$options Variable store for the Responsible API options set]
40
     * @var array|null
41
     */
42
    private $options = null;
43
44
    /**
45
     * [$DB Database PDO connector]
46
     * @var object|null
47
     */
48
    protected $DB = null;
49
50
    /**
51
     * [$grant_access If grant type is set then allow system scope override]
52
     * @var boolean
53
     */
54
    protected $grantAccess = false;
55
56
    /**
57
     * [$RESPONSE]
58
     * @var array
59
     */
60
    protected $RESPONSE = array();
61
62
    /**
63
     * [$header Header class object]
64
     * @var object
65
     */
66
    protected $header;
67
68
    /**
69
     * [$endpoints Endpoints class object]
70
     * @var object
71
     */
72
    protected $endpoints;
73
74
    /**
75
     * [$keys Keys class object]
76
     * @var object
77
     */
78
    protected $keys;
79
80
    /**
81
     * [$auth Auth class object]
82
     * @var object
83
     */
84
    protected $auth;
85
86
    /**
87
     * [$limiter Limiter class object]
88
     * @var object
89
     */
90
    protected $limiter;
91
92
    /**
93
     * [$router Router object]
94
     * @var object
95
     */
96
    protected $router;
97
98
    /**
99
     * [$routerClass Router class object]
100
     * @var object
101
     */
102
    protected $routerClass;
103
104
    /**
105
     * [$renderError]
106
     * @var boolean
107
     */
108
    protected $renderError = false;
109
110
    /**
111
     * [__construct]
112
     * @param array  $config
113
     *        environment variables
114
     * @param boolean $db
115
     */
116
    public function __construct(array $config = [], array $options = [], $db = false)
117
    {
118
        $this->setOptions($options);
119
120
        // @codeCoverageIgnoreStart
121
        if ($db && !$this->isMockTest()) {
122
            if (empty($config)) {
123
                $config = $this->getConfig();
124
            }
125
            if (is_null($this->DB)) {
126
                $this->DB = new connect\DB(
127
                    $config['DB_HOST'],
128
                    $config['DB_NAME'],
129
                    $config['DB_USER'],
130
                    $config['DB_PASSWORD']
131
                );
132
            }
133
        }
134
        // @codeCoverageIgnoreEnd
135
136
        $this->setDependencies();
137
    }
138
139
    /**
140
     * [getConfig]
141
     * @return array
142
     */
143
    public function getConfig()
144
    {
145
        $config = new configuration\config();
146
        $config->responsibleDefault();
147
        $config = $config->getConfig();
148
149
        return $config;
150
    }
151
152
    /**
153
     * [setDependencies Setup all dependent classes]
154
     */
155
    private function setDependencies()
156
    {
157
        $options = $this->getOptions();
158
159
        if (is_null($this->header)) {
160
            $this->header = new headers\header();
161
            $this->header->setOptions($options);
162
163
            if (empty((array)$this->header->getMethod())) {
164
                $this->header->requestMethod();
165
            }
166
        }
167
168
        if (is_null($this->keys)) {
169
            $this->keys = new keys\key();
170
        }
171
172
        if (is_null($this->endpoints)) {
173
            $this->endpoints = new endpoints\map();
174
            $this->endpoints->setOptions($options);
175
        }
176
177
        if (is_null($this->auth)) {
178
            $this->auth = new auth\authorise($options);
179
            $this->auth->header = $this->header;
180
        }
181
    }
182
183
    /**
184
     * [getInstance Get a child dependency of sever class]
185
     * @param  [type] $class
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...
186
     * @return object|null
187
     */
188
    public function getInstance($class)
189
    {
190
        if (property_exists($this, $class)) {
191
            if (!is_null($this->{$class})) {
192
                return $this->{$class};
193
            }
194
        }
195
        return null;
196
    }
197
198
    /**
199
     * [options Responsible API options]
200
     * @param array $options
201
     */
202
    public function setOptions($options)
203
    {
204
        if (!is_null($this->options)) {
205
            array_merge($this->options, $options);
206
            return;
207
        }
208
209
        $this->options = $options;
210
    }
211
212
    /**
213
     * [getOptions Get the stored Responsible API options]
214
     * @return array|null
215
     */
216
    public function getOptions(): ?array
217
    {
218
        return $this->options;
219
    }
220
221
    /**
222
     * [DB Get the database instance]
223
     * @codeCoverageIgnore
224
     * @return object
225
     */
226
    public function DB()
227
    {
228
        return $this->DB;
229
    }
230
231
    /**
232
     * [requestType]
233
     * @var string $type
234
     * @return self
235
     */
236
    public function requestType($type)
237
    {
238
        $this->header->requestType($type);
239
        $this->header->requestMethod();
240
        return $this;
241
    }
242
243
    /**
244
     * [getRequestType]
245
     * @return string
246
     */
247
    public function getRequestType()
248
    {
249
        return $this->header->getRequestType();
250
    }
251
252
    /**
253
     * [setResponse Append the Responsible API response]
254
     * @param string|array $key
255
     * @param array|object|null $response
256
     */
257
    public function setResponse($key, $response)
258
    {
259
        if ($this->getRouter()->responseType == 'restful') {
260
            $this->RESPONSE = $response;
0 ignored issues
show
Documentation Bug introduced by
It seems like $response can also be of type object. However, the property $RESPONSE is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
261
            return;
262
        }
263
264
        $responseHeader = [
265
            'headerStatus' => $this->header->getHeaderStatus(),
266
            'expires_in' => $this->auth->getJWTObject('expiresIn'),
267
            'access_token' => $this->auth->getJWTObject('token'),
268
            'refresh_token' => $this->auth->getJWTObject('refresh'),
269
        ];
270
271
        if (isset($this->RESPONSE['response'][$key])) {
272
            $this->RESPONSE['response'][$key][] = $response;
273
            $this->RESPONSE = array_merge($responseHeader, $this->RESPONSE);
274
            return;
275
        }
276
277
        if (is_null($key) || $key == '') {
278
            if (!is_null($response)) {
279
                $this->RESPONSE['response'] = $response;
280
            }
281
            $this->RESPONSE = array_merge($responseHeader, $this->RESPONSE);
282
            return;
283
        }
284
285
        $this->RESPONSE['response'][$key] = $response;
286
        $this->RESPONSE = array_merge($responseHeader, $this->RESPONSE);
287
    }
288
289
    /**
290
     * [getResponse Get the Responsible API output response]
291
     * @return array
292
     */
293
    public function getResponse()
294
    {
295
        if (isset($this->RESPONSE['response']['response'])) {
296
            $this->RESPONSE['response'] = $this->RESPONSE['response']['response'];
297
        }
298
        return $this->RESPONSE;
299
    }
300
301
    /**
302
     * [rate Set the API rate limit]
303
     * @param  integer $limit [The request limit]
304
     * @param  string|integer $rate  [The request window]
305
     * @return self
306
     */
307
    public function rateLimit($limit = null, $rate = null)
308
    {
309
        $this->limiter = new throttle\limiter($limit, $rate);
310
        $this->limiter->setOptions($this->getOptions());
311
        return $this;
312
    }
313
314
    /**
315
     * [authenticate Parse the requests to Responsible API]
316
     *
317
     * 1. Authorise the requests JWT
318
     * 2. Throttle the requests
319
     *
320
     * @return self
321
     */
322
    public function authenticate()
323
    {
324
        $options = $this->getOptions();
325
        $route = (isset($options['route']) && !empty($options['route']) ) ? $options['route'] : '';
326
327
        $this->endpoints->baseApiRoot(dirname(__DIR__));
328
        $this->endpoints->register();
329
330
        $router = new route\router();
331
        $router->baseApiRoot(dirname(__DIR__));
332
333
        $this->router = $router->route($route);
334
        $endpoint = $this->endpoints->isEndpoint($router->getApi(), $router->getPath());
335
336
        if (isset($endpoint->model['scope'])) {
337
            $_REQUEST['scope'] = $endpoint->model['scope'];
338
            $this->header->setData($_REQUEST);
339
        }
340
341
        /**
342
         * Authenticate the JWT
343
         */
344
        $this->auth->authorise();
345
346
        /**
347
         * Call the rate limiter then throttle
348
         */
349
        if (!isset($this->limiter)) {
350
            $this->rateLimit();
351
        }
352
353
        $this->limiter
354
            ->setAccount($this->auth->user())
355
            ->setupOptions()
356
            ->throttleRequest()
357
        ;
358
359
        return $this;
360
    }
361
362
    /**
363
     * [route Build the Responsible router]
364
     *
365
     * 1. Endpoints registry
366
     * 2. Build router
367
     * 3. Try run middleware
368
     *
369
     * @return self
370
     */
371
    public function route($route)
372
    {
373
        /**
374
         * Register endpoints
375
         */
376
        $this->endpoints->baseApiRoot(dirname(__DIR__));
377
        $this->endpoints->register();
378
379
        /**
380
         * Initialise the router
381
         */
382
        $this->routerClass = new route\router();
383
        $this->routerClass->setOptions($this->getOptions());
384
        $router = $this->routerClass;
385
386
        $router->baseApiRoot(dirname(__DIR__));
387
        $this->router = $router->route($route);
388
        $this->router->options = $this->getOptions();
389
        $this->router->auth = $this->auth->user();
390
        $this->router->limiter = $this->limiter->getThrottle();
391
392
        /**
393
         * Endpoint tiers must be larger than 1
394
         */
395
        if ($router->getSize() < 1) {
396
            (new exception\errorException())
397
                ->setOptions($this->getOptions())
398
                ->error('NOT_FOUND');
399
        }
400
401
        /**
402
         * Check if the requested endpoint is allowed
403
         */
404
        if (
405
            !$this->router->endpoint =
406
            $this->endpoints->isEndpoint($router->getApi(), $router->getPath())
407
        ) {
408
            (new exception\errorException())
409
                ->setOptions($this->getOptions())
410
                ->error('BAD_REQUEST');
411
        }
412
413
        $this->router->endpoint->header = [
414
            'method' => $this->header->getServerMethod(),
415
            'status' => $this->header->getHeaderStatus(),
416
            'body' => $this->header->getMethod(),
417
        ];
418
419
        /**
420
         * Check if theres a request payload sent
421
         */
422
        if (isset($_REQUEST['payload'])) {
423
            $router->setRequestBody($_REQUEST['payload']);
424
        }
425
        $router->setPostBody($this->header->getBody());
426
427
        $this->router->payload = $router->getRequestBody();
428
        $this->router->body = $router->getBody();
429
430
        /**
431
         * Check the access scope
432
         */
433
        if (!isset($this->router->endpoint->model['scope'])) {
434
            $this->router->endpoint->model['scope'] = 'private';
435
        }
436
437
        if (
438
            isset($this->header->getMethod()->data['scope']) &&
439
            ($this->header->getMethod()->data['scope'] == 'anonymous')
440
        ) {
441
            $this->router->endpoint->model['scope'] = 'anonymous';
442
        }
443
444
        $router->setScope($this->router->endpoint->model['scope']);
445
446
        // @codeCoverageIgnoreStart
447
        if (!$this->auth->isGrantType() && !$this->isMockTest()) {
448
            if (!$router->systemAccess($this->auth->user())) {
449
                $this->header->unauthorised();
450
            }
451
        }
452
        // @codeCoverageIgnoreEnd
453
454
        /**
455
         * Try run the requests
456
         */
457
        if ($router->getScope() !== 'system') {
458
            $response = $router->run();
459
        } else {
460
            /*$response = [
461
                'system' => $router->getApi(),
462
            ];*/
463
464
            $response = $router->run();
465
        }
466
467
        $this->setResponse('', $response);
468
469
        return $this;
470
    }
471
472
    /**
473
     * [getRouter Get the details of the Responsible API router]
474
     * @return object
475
     */
476
    public function getRouter()
477
    {
478
        return $this->router;
479
    }
480
481
    /**
482
     * [coredata Get the core data response]
483
     * @return object
484
     */
485
    public function coredata()
486
    {
487
        /**
488
         * Set the core data response
489
         * Used for debugging
490
         */
491
        foreach ($this->router as $key => $value) {
492
            $this->setResponse($key, $value);
493
        }
494
        return $this;
495
    }
496
497
    /**
498
     * [response Finnal response output]
499
     * @return array|object
500
     */
501
    public function response($debug = '')
502
    {
503
        /**
504
         * Output bebug functions
505
         */
506
        if (!empty($debug)) {
507
            if (method_exists($this, $debug)) {
508
                call_user_func(array($this, $debug));
509
            }
510
        }
511
512
        /**
513
         * Set the Responsible headers
514
         */
515
        $corsAllowed = ($this->getOptions()['cors']) ?? false;
516
        $isCorsRequest = ($_SERVER['HTTP_ORIGIN']) ?? false;
517
        $this->header->requestType($this->getRequestType());
518
        $this->header->setHeaders($corsAllowed && $isCorsRequest);
519
520
        /**
521
         * Output the response if any
522
         */
523
        $requestApplication = new request\application($this->getRequestType());
524
        $requestApplication->setOptions($this->getOptions());
525
        return $requestApplication->data($this->getResponse());
526
    }
527
528
    /**
529
     * isMockTest
530
     *     Check if there's a mock test request
531
     * @return boolean
532
     */
533
    public function isMockTest(): bool
534
    {
535
        $config = $this->getConfig();
536
537
        return (isset($this->options['mock']) && $this->options['mock'] == $config['MASTER_KEY']);
538
    }
539
}
540