Passed
Push — master ( 1466fb...e4c3a9 )
by Vince
01:32
created

server   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 419
Duplicated Lines 0 %

Importance

Changes 13
Bugs 0 Features 1
Metric Value
eloc 123
c 13
b 0
f 1
dl 0
loc 419
rs 9.0399
wmc 42

15 Methods

Rating   Name   Duplication   Size   Complexity  
A setOptions() 0 8 2
A requestType() 0 5 1
A setResponse() 0 21 5
A getOptions() 0 3 1
A getResponse() 0 3 1
A getRequestType() 0 3 1
A DB() 0 3 1
A __construct() 0 24 5
A rateLimit() 0 4 1
A authenticate() 0 39 5
A isMockTest() 0 9 3
C route() 0 90 10
A response() 0 22 3
A coredata() 0 10 2
A getRouter() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like server 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 server, and based on these observations, apply Extract Interface, too.

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
11
 *
12
 * @author Vince scarpa <[email protected]>
13
 *
14
 */
15
namespace responsible\core;
16
17
use responsible\core\auth;
18
use responsible\core\configuration;
19
use responsible\core\connect;
20
use responsible\core\endpoints;
21
use responsible\core\exception;
22
use responsible\core\headers;
23
use responsible\core\keys;
24
use responsible\core\request;
25
use responsible\core\route;
26
use responsible\core\throttle;
27
28
class server
29
{
30
    /**
31
     * [$config]
32
     * @var object
33
     */
34
    protected $config;
35
36
    /**
37
     * [$options Variable store for the Responsible API options set]
38
     * @var array
39
     */
40
    private $options = null;
41
42
    /**
43
     * [$DB Database PDO connector]
44
     * @var object
45
     */
46
    protected $DB = null;
47
48
    /**
49
     * [$grant_access If grant type is set then allow system scope override]
50
     * @var boolean
51
     */
52
    protected $grantAccess = false;
53
54
    /**
55
     * [$RESPONSE]
56
     * @var array
57
     */
58
    protected $RESPONSE = array();
59
60
    /**
61
     * [$header Header class object]
62
     * @var object|null
63
     */
64
    protected $header = null;
65
66
    /**
67
     * [$endpoints Endpoints class object]
68
     * @var object|null
69
     */
70
    protected $endpoints = null;
71
72
    /**
73
     * [$keys Keys class object]
74
     * @var object|null
75
     */
76
    protected $keys = null;
77
78
    /**
79
     * [$auth Auth class object]
80
     * @var object|null
81
     */
82
    protected $auth = null;
83
84
    /**
85
     * [$limiter Limiter class object]
86
     * @var object|null
87
     */
88
    protected $limiter = null;
89
90
    /**
91
     * [$router Router class object]
92
     * @var object|null
93
     */
94
    protected $router = null;
95
96
    /**
97
     * [__construct]
98
     * @param array  $config 
99
     *        environment variables
100
     * @param boolean $db
101
     */
102
    public function __construct(array $config = [], array $options = [], $db = false)
103
    {
104
        $this->setOptions($options);
105
106
        if ($db && !$this->isMockTest()) {
107
            if (empty($config)) {
108
                $config = new configuration\config;
109
                $config->responsibleDefault();
110
                $config = $config->getConfig();
111
            }
112
            if (is_null($this->DB)) {
113
                $this->DB = new connect\DB($config['DB_HOST'], $config['DB_NAME'], $config['DB_USER'], $config['DB_PASSWORD']);
114
            }
115
        }
116
117
        $this->header = new headers\header;
118
        $this->header->setOptions($options);
119
120
        $this->keys = new keys\key;
121
        $this->endpoints = new endpoints\map;
122
        $this->endpoints->setOptions($options);
123
124
        $this->auth = new auth\authorise($options);
125
        $this->auth->header = $this->header;
126
    }
127
128
    /**
129
     * [options Responsible API options]
130
     * @param array $options
131
     */
132
    public function setOptions($options)
133
    {
134
        if (!is_null($this->options)) {
0 ignored issues
show
introduced by
The condition is_null($this->options) is always false.
Loading history...
135
            array_merge($this->options, $options);
136
            return;
137
        }
138
139
        $this->options = $options;
140
    }
141
142
    /**
143
     * [getOptions Get the stored Responsible API options]
144
     * @return array
145
     */
146
    public function getOptions():array
147
    {
148
        return $this->options;
149
    }
150
151
    /**
152
     * [DB Get the database instance]
153
     * @return object
154
     */
155
    public function DB()
156
    {
157
        return $this->DB;
158
    }
159
160
    /**
161
     * [requestType]
162
     * @var string $type
163
     * @return self
164
     */
165
    public function requestType($type)
166
    {
167
        $this->header->requestType($type);
0 ignored issues
show
Bug introduced by
The method requestType() does not exist on null. ( Ignorable by Annotation )

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

167
        $this->header->/** @scrutinizer ignore-call */ 
168
                       requestType($type);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
168
        $this->header->requestMethod();
169
        return $this;
170
    }
171
172
    /**
173
     * [getRequestType]
174
     * @return string
175
     */
176
    public function getRequestType()
177
    {
178
        return $this->header->getRequestType();
179
    }
180
181
    /**
182
     * [setResponse Append the Responsible API response]
183
     * @param string|array $key
184
     * @param array|null $response
185
     */
186
    public function setResponse($key, $response)
187
    {
188
        $this->RESPONSE = [
189
            'headerStatus' => $this->header->getHeaderStatus(),
190
            'expires_in' => $this->auth->getJWTObject('expiresIn'),
0 ignored issues
show
Bug introduced by
The method getJWTObject() does not exist on null. ( Ignorable by Annotation )

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

190
            'expires_in' => $this->auth->/** @scrutinizer ignore-call */ getJWTObject('expiresIn'),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
191
            'access_token' => $this->auth->getJWTObject('token'),
192
            'refresh_token' => $this->auth->getJWTObject('refresh'),
193
        ];
194
195
        if (isset($this->RESPONSE['response'][$key])) {
196
            $this->RESPONSE['response'][$key][] = $response;
197
            return;
198
        }
199
        if (is_null($key) || $key == '') {
200
            if( !is_null($response) ) {
201
                $this->RESPONSE['response'] = $response;
202
            }
203
            return;
204
        }
205
206
        $this->RESPONSE['response'][$key] = $response;
207
    }
208
209
    /**
210
     * [getResponse Get the Responsible API output response]
211
     * @return array
212
     */
213
    private function getResponse()
214
    {
215
        return $this->RESPONSE;
216
    }
217
218
    /**
219
     * [rate Set the API rate limit]
220
     * @param  integer $limit [The request limit]
221
     * @param  string|integer $rate  [The request window]
222
     * @return self
223
     */
224
    public function rateLimit($limit = null, $rate = null)
225
    {
226
        $this->limiter = new throttle\limiter($limit, $rate);
227
        return $this;
228
    }
229
230
    /**
231
     * [authenticate Parse the requests to Responsible API]
232
     *
233
     * 1. Authorise the requests JWT
234
     * 2. Throttle the requests
235
     *
236
     * @return self
237
     */
238
    public function authenticate()
239
    {
240
        $options = $this->getOptions();
241
        $route = (isset($options['route']) && !empty($options['route']) ) ? $options['route'] : '';
242
243
        $this->endpoints->baseApiRoot(dirname(__DIR__));
0 ignored issues
show
Bug introduced by
The method baseApiRoot() does not exist on null. ( Ignorable by Annotation )

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

243
        $this->endpoints->/** @scrutinizer ignore-call */ 
244
                          baseApiRoot(dirname(__DIR__));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
244
        $this->endpoints->register();
245
        
246
        $router = new route\router();
247
        $router->baseApiRoot(dirname(__DIR__));
248
249
        $this->router = $router->route($route);
250
        $endpoint = $this->endpoints->isEndpoint($router->getApi(), $router->getPath());
251
252
        if(isset($endpoint->model['scope'])) {
253
            $_REQUEST['scope'] = $endpoint->model['scope'];
254
            $this->header->setData($_REQUEST);
255
        }
256
257
        /**
258
         * Authenticate the JWT
259
         */
260
        $this->auth->authorise();
261
262
        /**
263
         * Call the rate limiter then throttle
264
         */
265
        if (!isset($this->limiter)) {
266
            $this->rateLimit();
267
        }
268
        
269
        $this->limiter
270
            ->options($this->getOptions())
0 ignored issues
show
Bug introduced by
The method options() does not exist on null. ( Ignorable by Annotation )

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

270
            ->/** @scrutinizer ignore-call */ 
271
              options($this->getOptions())

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
271
            ->setAccount($this->auth->user())
272
            ->setupOptions()
273
            ->throttleRequest()
274
        ;
275
276
        return $this;
277
    }
278
279
    /**
280
     * [route Build the Responsible router]
281
     *
282
     * 1. Endpoints registry
283
     * 2. Build router
284
     * 3. Try run middleware
285
     *
286
     * @return self
287
     */
288
    public function route($route)
289
    {
290
        /**
291
         * Register endpoints
292
         */
293
        $this->endpoints->baseApiRoot(dirname(__DIR__));
294
        $this->endpoints->register();
295
296
        /**
297
         * Initialise the router
298
         */
299
        $router = new route\router();
300
        $router->baseApiRoot(dirname(__DIR__));
301
        $this->router = $router->route($route);
302
        $this->router->options = $this->getOptions();
303
        $this->router->auth = $this->auth->user();
304
        $this->router->limiter = $this->limiter->getThrottle();
305
306
        /**
307
         * Endpoint tiers must be larger than 1
308
         */
309
        if ($router->getSize() < 2) {
310
            (new exception\errorException)->error('NOT_FOUND');
311
        }
312
313
        /**
314
         * Check if the requested endpoint is allowed
315
         */
316
        if (!$this->router->endpoint =
317
            $this->endpoints->isEndpoint($router->getApi(), $router->getPath())
318
        ) {
319
            (new exception\errorException)->error('BAD_REQUEST');
320
        }
321
322
        $this->router->endpoint->header = [
323
            'method' => $this->header->getServerMethod(),
324
            'status' => $this->header->getHeaderStatus(),
325
            'body' => $this->header->getMethod(),
326
        ];
327
328
        /**
329
         * Check if theres a payload sent
330
         */
331
        if(isset($_REQUEST['payload'])) {
332
            $router->setRequestBody($_REQUEST['payload']);
333
        }
334
        // print_r($_REQUEST);
335
        /*if(isset($_POST) && !empty($_POST)) {
336
            $router->setPostBody($_POST);
337
        }*/
338
        $this->router->payload = $router->getRequestBody();
339
340
        /**
341
         * Check the access scope
342
         */
343
        if( !isset($this->router->endpoint->model['scope']) ) {
344
            $this->router->endpoint->model['scope'] = 'private';
345
        }
346
347
        if( isset($this->header->getMethod()->data['scope']) && 
348
            ($this->header->getMethod()->data['scope'] == 'anonymous')
349
        ) {
350
            $this->router->endpoint->model['scope'] = 'anonymous';
351
        }
352
353
        $router->setScope($this->router->endpoint->model['scope']);
354
        
355
        if (!$this->auth->isGrantType()) {
356
            if (!$router->systemAccess($this->auth->user())) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $router->systemAccess($this->auth->user()) of type null|true is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
357
                $this->header->unauthorised();
358
            }
359
        }
360
361
        /**
362
         * Try run the requests
363
         */
364
        if ($router->getScope() !== 'system') {
365
            $response = $router->run();
366
367
        } else {
368
            /*$response = [
369
                'system' => $router->getApi(),
370
            ];*/
371
372
            $response = $router->run();
373
        }
374
375
        $this->setResponse('', $response);
376
377
        return $this;
378
    }
379
380
    /**
381
     * [getRouter Get the details of the Responsible API router]
382
     * @return array
383
     */
384
    public function getRouter()
385
    {
386
        return $this->router;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->router also could return the type object which is incompatible with the documented return type array.
Loading history...
387
    }
388
389
    /**
390
     * [coredata Get the core data response]
391
     * @return object
392
     */
393
    public function coredata()
394
    {
395
        /**
396
         * Set the core data response
397
         * Used for debugging
398
         */
399
        foreach ($this->router as $key => $value) {
400
            $this->setResponse($key, $value);
401
        }
402
        return $this;
403
    }
404
405
    /**
406
     * [response Finnal response output]
407
     * @return array|object
408
     */
409
    public function response($debug = '')
410
    {
411
        /**
412
         * Output bebug functions
413
         */
414
        if (!empty($debug)) {
415
            if (method_exists($this, $debug)) {
416
                call_user_func(array($this, $debug));
417
            }
418
        }
419
420
        /**
421
         * Set the Responsible headers
422
         */
423
        $this->header->requestType($this->getRequestType());
424
        $this->header->setHeaders();
425
426
        /**
427
         * Output the response if any
428
         */
429
        return (new request\application($this->getRequestType()))
430
            ->data($this->getResponse());
431
    }
432
433
    /**
434
     * isMockTest
435
     *     Check if there's a mook server request
436
     * @return boolean
437
     */
438
    public function isMockTest():bool
439
    {
440
        if (isset($this->options['mock']) && 
441
            $this->options['mock'] == 'mock:3$_\7ucJ#D4,Yy=qzwY{&E+Mk_h,7L8:key'
442
        ) {
443
            return true;
444
        }
445
446
        return false;
447
    }
448
}
449