Passed
Push — master ( 76e0d3...261fee )
by Vince
01:33
created

server   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 438
Duplicated Lines 0 %

Importance

Changes 14
Bugs 0 Features 1
Metric Value
eloc 129
c 14
b 0
f 1
dl 0
loc 438
rs 8.64
wmc 47

16 Methods

Rating   Name   Duplication   Size   Complexity  
A setOptions() 0 8 2
A requestType() 0 5 1
A setDependencies() 0 21 5
A setResponse() 0 21 5
A isMockTest() 0 9 3
C route() 0 90 10
A response() 0 22 3
A getOptions() 0 3 1
A getResponse() 0 3 1
A coredata() 0 10 2
A getRequestType() 0 3 1
A DB() 0 3 1
A __construct() 0 16 5
A rateLimit() 0 5 1
A authenticate() 0 38 5
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->setDependencies();
118
    }
119
120
    /**
121
     * [setDependencies Setup all dependent classes]
122
     */
123
    private function setDependencies()
124
    {
125
        $options = $this->getOptions();
126
127
        if (is_null($this->header)) {
128
            $this->header = new headers\header;
129
            $this->header->setOptions($options);
130
        }
131
132
        if (is_null($this->keys)) {
133
            $this->keys = new keys\key;
134
        }
135
136
        if (is_null($this->endpoints)) {
137
            $this->endpoints = new endpoints\map;
138
            $this->endpoints->setOptions($options);
139
        }
140
141
        if (is_null($this->auth)) {
142
            $this->auth = new auth\authorise($options);
143
            $this->auth->header = $this->header;
144
        }
145
    }
146
147
    /**
148
     * [options Responsible API options]
149
     * @param array $options
150
     */
151
    public function setOptions($options)
152
    {
153
        if (!is_null($this->options)) {
0 ignored issues
show
introduced by
The condition is_null($this->options) is always false.
Loading history...
154
            array_merge($this->options, $options);
155
            return;
156
        }
157
158
        $this->options = $options;
159
    }
160
161
    /**
162
     * [getOptions Get the stored Responsible API options]
163
     * @return array
164
     */
165
    public function getOptions():array
166
    {
167
        return $this->options;
168
    }
169
170
    /**
171
     * [DB Get the database instance]
172
     * @return object
173
     */
174
    public function DB()
175
    {
176
        return $this->DB;
177
    }
178
179
    /**
180
     * [requestType]
181
     * @var string $type
182
     * @return self
183
     */
184
    public function requestType($type)
185
    {
186
        $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

186
        $this->header->/** @scrutinizer ignore-call */ 
187
                       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...
187
        $this->header->requestMethod();
188
        return $this;
189
    }
190
191
    /**
192
     * [getRequestType]
193
     * @return string
194
     */
195
    public function getRequestType()
196
    {
197
        return $this->header->getRequestType();
198
    }
199
200
    /**
201
     * [setResponse Append the Responsible API response]
202
     * @param string|array $key
203
     * @param array|object|null $response
204
     */
205
    public function setResponse($key, $response)
206
    {
207
        $this->RESPONSE = [
208
            'headerStatus' => $this->header->getHeaderStatus(),
209
            '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

209
            '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...
210
            'access_token' => $this->auth->getJWTObject('token'),
211
            'refresh_token' => $this->auth->getJWTObject('refresh'),
212
        ];
213
214
        if (isset($this->RESPONSE['response'][$key])) {
215
            $this->RESPONSE['response'][$key][] = $response;
216
            return;
217
        }
218
        if (is_null($key) || $key == '') {
219
            if( !is_null($response) ) {
220
                $this->RESPONSE['response'] = $response;
221
            }
222
            return;
223
        }
224
225
        $this->RESPONSE['response'][$key] = $response;
226
    }
227
228
    /**
229
     * [getResponse Get the Responsible API output response]
230
     * @return array
231
     */
232
    private function getResponse()
233
    {
234
        return $this->RESPONSE;
235
    }
236
237
    /**
238
     * [rate Set the API rate limit]
239
     * @param  integer $limit [The request limit]
240
     * @param  string|integer $rate  [The request window]
241
     * @return self
242
     */
243
    public function rateLimit($limit = null, $rate = null)
244
    {
245
        $this->limiter = new throttle\limiter($limit, $rate);
246
        $this->limiter->setOptions($this->getOptions());
247
        return $this;
248
    }
249
250
    /**
251
     * [authenticate Parse the requests to Responsible API]
252
     *
253
     * 1. Authorise the requests JWT
254
     * 2. Throttle the requests
255
     *
256
     * @return self
257
     */
258
    public function authenticate()
259
    {
260
        $options = $this->getOptions();
261
        $route = (isset($options['route']) && !empty($options['route']) ) ? $options['route'] : '';
262
263
        $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

263
        $this->endpoints->/** @scrutinizer ignore-call */ 
264
                          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...
264
        $this->endpoints->register();
265
        
266
        $router = new route\router();
267
        $router->baseApiRoot(dirname(__DIR__));
268
269
        $this->router = $router->route($route);
0 ignored issues
show
Documentation Bug introduced by
It seems like $router->route($route) of type array is incompatible with the declared type null|object of property $router.

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...
270
        $endpoint = $this->endpoints->isEndpoint($router->getApi(), $router->getPath());
271
272
        if(isset($endpoint->model['scope'])) {
273
            $_REQUEST['scope'] = $endpoint->model['scope'];
274
            $this->header->setData($_REQUEST);
275
        }
276
277
        /**
278
         * Authenticate the JWT
279
         */
280
        $this->auth->authorise();
281
282
        /**
283
         * Call the rate limiter then throttle
284
         */
285
        if (!isset($this->limiter)) {
286
            $this->rateLimit();
287
        }
288
        
289
        $this->limiter
290
            ->setAccount($this->auth->user())
0 ignored issues
show
Bug introduced by
The method setAccount() 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

290
            ->/** @scrutinizer ignore-call */ 
291
              setAccount($this->auth->user())

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...
291
            ->setupOptions()
292
            ->throttleRequest()
293
        ;
294
295
        return $this;
296
    }
297
298
    /**
299
     * [route Build the Responsible router]
300
     *
301
     * 1. Endpoints registry
302
     * 2. Build router
303
     * 3. Try run middleware
304
     *
305
     * @return self
306
     */
307
    public function route($route)
308
    {
309
        /**
310
         * Register endpoints
311
         */
312
        $this->endpoints->baseApiRoot(dirname(__DIR__));
313
        $this->endpoints->register();
314
315
        /**
316
         * Initialise the router
317
         */
318
        $router = new route\router();
319
        $router->baseApiRoot(dirname(__DIR__));
320
        $this->router = $router->route($route);
0 ignored issues
show
Documentation Bug introduced by
It seems like $router->route($route) of type array is incompatible with the declared type null|object of property $router.

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...
321
        $this->router->options = $this->getOptions();
322
        $this->router->auth = $this->auth->user();
323
        $this->router->limiter = $this->limiter->getThrottle();
324
325
        /**
326
         * Endpoint tiers must be larger than 1
327
         */
328
        if ($router->getSize() < 2) {
329
            (new exception\errorException)->error('NOT_FOUND');
330
        }
331
332
        /**
333
         * Check if the requested endpoint is allowed
334
         */
335
        if (!$this->router->endpoint =
336
            $this->endpoints->isEndpoint($router->getApi(), $router->getPath())
337
        ) {
338
            (new exception\errorException)->error('BAD_REQUEST');
339
        }
340
341
        $this->router->endpoint->header = [
342
            'method' => $this->header->getServerMethod(),
343
            'status' => $this->header->getHeaderStatus(),
344
            'body' => $this->header->getMethod(),
345
        ];
346
347
        /**
348
         * Check if theres a payload sent
349
         */
350
        if(isset($_REQUEST['payload'])) {
351
            $router->setRequestBody($_REQUEST['payload']);
352
        }
353
        // print_r($_REQUEST);
354
        /*if(isset($_POST) && !empty($_POST)) {
355
            $router->setPostBody($_POST);
356
        }*/
357
        $this->router->payload = $router->getRequestBody();
358
359
        /**
360
         * Check the access scope
361
         */
362
        if( !isset($this->router->endpoint->model['scope']) ) {
363
            $this->router->endpoint->model['scope'] = 'private';
364
        }
365
366
        if( isset($this->header->getMethod()->data['scope']) && 
367
            ($this->header->getMethod()->data['scope'] == 'anonymous')
368
        ) {
369
            $this->router->endpoint->model['scope'] = 'anonymous';
370
        }
371
372
        $router->setScope($this->router->endpoint->model['scope']);
373
        
374
        if (!$this->auth->isGrantType()) {
375
            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...
376
                $this->header->unauthorised();
377
            }
378
        }
379
380
        /**
381
         * Try run the requests
382
         */
383
        if ($router->getScope() !== 'system') {
384
            $response = $router->run();
385
386
        } else {
387
            /*$response = [
388
                'system' => $router->getApi(),
389
            ];*/
390
391
            $response = $router->run();
392
        }
393
394
        $this->setResponse('', $response);
395
396
        return $this;
397
    }
398
399
    /**
400
     * [getRouter Get the details of the Responsible API router]
401
     * @return array
402
     */
403
    public function getRouter()
404
    {
405
        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...
406
    }
407
408
    /**
409
     * [coredata Get the core data response]
410
     * @return object
411
     */
412
    public function coredata()
413
    {
414
        /**
415
         * Set the core data response
416
         * Used for debugging
417
         */
418
        foreach ($this->router as $key => $value) {
419
            $this->setResponse($key, $value);
420
        }
421
        return $this;
422
    }
423
424
    /**
425
     * [response Finnal response output]
426
     * @return array|object
427
     */
428
    public function response($debug = '')
429
    {
430
        /**
431
         * Output bebug functions
432
         */
433
        if (!empty($debug)) {
434
            if (method_exists($this, $debug)) {
435
                call_user_func(array($this, $debug));
436
            }
437
        }
438
439
        /**
440
         * Set the Responsible headers
441
         */
442
        $this->header->requestType($this->getRequestType());
443
        $this->header->setHeaders();
444
445
        /**
446
         * Output the response if any
447
         */
448
        return (new request\application($this->getRequestType()))
0 ignored issues
show
Bug Best Practice introduced by
The expression return new responsible\c...a($this->getResponse()) returns the type void which is incompatible with the documented return type array|object.
Loading history...
Bug introduced by
Are you sure the usage of new responsible\core\req...a($this->getResponse()) targeting responsible\core\request\application::data() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
449
            ->data($this->getResponse());
450
    }
451
452
    /**
453
     * isMockTest
454
     *     Check if there's a mook server request
455
     * @return boolean
456
     */
457
    public function isMockTest():bool
458
    {
459
        if (isset($this->options['mock']) && 
460
            $this->options['mock'] == 'mock:3$_\7ucJ#D4,Yy=qzwY{&E+Mk_h,7L8:key'
461
        ) {
462
            return true;
463
        }
464
465
        return false;
466
    }
467
}
468