map   F
last analyzed

Complexity

Total Complexity 66

Size/Duplication

Total Lines 446
Duplicated Lines 0 %

Importance

Changes 16
Bugs 0 Features 0
Metric Value
eloc 204
c 16
b 0
f 0
dl 0
loc 446
rs 3.12
wmc 66

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 2 1
B isSystemEndpoint() 0 37 7
C register() 0 83 15
D isEndpoint() 0 181 32
A uriCheckSize() 0 5 1
A filterParts() 0 15 5
A isWildcardEndpoint() 0 12 2
A getClassModel() 0 16 3

How to fix   Complexity   

Complex Class

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

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\endpoints
12
 *
13
 * @author Vince scarpa <[email protected]>
14
 *
15
 */
16
17
namespace responsible\core\endpoints;
18
19
use responsible\core\endpoints;
20
use responsible\core\exception;
21
use responsible\core\route;
22
use responsible\core\interfaces;
23
24
class map extends route\router implements interfaces\optionsInterface
25
{
26
    use \responsible\core\traits\optionsTrait;
27
28
    /**
29
     * [$BASE_ENDPOINTS]
30
     * @var array
31
     */
32
    private $BASE_ENDPOINTS = array();
33
34
    /**
35
     * [$BASE_ENDPOINTS]
36
     * @var array
37
     */
38
    private $NAMESPACE_ENDPOINTS = array();
39
40
    /**
41
     * [$registry]
42
     * @var array
43
     */
44
    private $registry = array();
45
46
    /**
47
     * [$middleWareClass Holds middleware class object]
48
     * @var object
49
     */
50
    private static $middleWareClass;
51
52
    /**
53
     * [$SYSTEM_ENDPOINTS Reserved system Endpoints]
54
     * @var array
55
     */
56
    public const SYSTEM_ENDPOINTS = [
57
        'token' => '/token/access_token',
58
        'user' => [
59
            '/user/create',
60
            '/user/load',
61
        ],
62
    ];
63
64
    /**
65
     * [__construct Silence...]
66
     */
67
    public function __construct()
68
    {
69
    }
70
71
    /**
72
     * [register Scan and register endpoints defined in services]
73
     * @return array
74
     */
75
    public function register()
76
    {
77
        $options = $this->options;
78
79
        /**
80
         * Check if a custom directory was set in the Responsible API options
81
         */
82
        if (
83
            (isset($options['classRoute']) && !empty($options['classRoute'])) &&
84
            (isset($options['classRoute']['directory']) && isset($options['classRoute']['namespace']))
85
        ) {
86
            $customService = $this->options['classRoute'];
87
            $directory = $customService['directory'];
88
            $middleware = $customService['namespace'];
89
        } else {
90
            $middleware = 'responsible';
91
92
            $endpoint = str_replace(
93
                array('core', '/', '\\'),
94
                array('service', DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR),
95
                __NAMESPACE__
96
            );
97
98
            $directory = $this->route()->base['root'] . '/' . str_replace('responsible/', '', $endpoint);
99
        }
100
101
        if (!is_dir($directory)) {
102
            (new exception\errorException())
103
                ->setOptions($this->getOptions())
104
                ->message('Directory Error:: responsible\service needs to exist. See documentation on setting up a service.')
105
                ->error('NOT_EXTENDED');
106
        }
107
108
        $scanned = '';
109
        $scanDir = scandir($directory);
110
111
        if (!empty($scanDir)) {
112
            $scanned = array_values(
113
                array_diff(
114
                    $scanDir,
115
                    array('..', '.', '.DS_Store')
116
                )
117
            );
118
        }
119
120
        if (empty($scanned)) {
121
            (new exception\errorException())
122
                ->setOptions($this->getOptions())
123
                ->message('Class Error:: responsible\service\endpoints needs at least 1 class file. See documentation on setting up a service.')
124
                ->error('NOT_EXTENDED');
125
        }
126
127
        foreach ($scanned as $e => $point) {
128
            if (substr($point, -4) == '.php') {
129
                $point = str_replace('.php', '', $point);
130
131
                $this->BASE_ENDPOINTS[] = $point;
132
133
                $endpoint = str_replace('core', 'service', __NAMESPACE__) . '\\' . $point;
134
135
                if ($middleware !== 'responsible') {
136
                    $endpoint = $middleware . '\\service\\endpoints\\' . $point;
137
                }
138
139
                $child = $endpoint;
140
141
                $this->NAMESPACE_ENDPOINTS[$point] = $endpoint;
142
143
                if (class_exists($child) && method_exists($child, 'middleware')) {
144
                    self::$middleWareClass = new $child();
145
                    $this->registry[$point] = self::$middleWareClass->middleware();
146
                } elseif (class_exists($child) && method_exists($child, 'register')) {
147
                    self::$middleWareClass = new $child();
148
                    $this->registry[$point] = self::$middleWareClass->register();
149
                } else {
150
                    (new exception\errorException())
151
                        ->setOptions($this->getOptions())
152
                        ->message("Class Error:: class {$child} needs to exist. See documentation on setting up a service.")
153
                        ->error('NOT_EXTENDED');
154
                }
155
            }
156
        }
157
        return $this->registry;
158
    }
159
160
    /**
161
     * [isSystemEndpoint Check if the endpoint request is a ResponsibleAPI reserved endpoint]
162
     * @param  string  $api
163
     * @param  string  $endpoint
164
     * @return object|null
165
     */
166
    private function isSystemEndpoint($api, $endpoint)
167
    {
168
        if (
169
            isset(self::SYSTEM_ENDPOINTS[$api]) &&
170
            (
171
                in_array($endpoint, self::SYSTEM_ENDPOINTS) ||
172
                array_search($endpoint, self::SYSTEM_ENDPOINTS[$api]) !== false
173
            )
174
        ) {
175
            $endpointSettings = [];
176
            $methodCreate = explode('/', $endpoint);
177
            $methodCreate = array_values(array_filter($methodCreate));
178
            $method = '';
179
180
            foreach ($methodCreate as $i => $parts) {
181
                if (preg_match_all('#_#', $parts)) {
182
                    $parts = str_replace('_', '', lcfirst(ucwords($parts, '_')));
183
                }
184
                if ($i > 0) {
185
                    $method .= ucfirst($parts);
186
                } else {
187
                    $method .= $parts;
188
                }
189
            }
190
191
            $endpointSettings['model'] = array(
192
                'scope' => 'system',
193
                'namespace' => 'responsible\core\endpoints\system',
194
                'class' => 'system',
195
                'method' => $method,
196
                'arguments' => '',
197
            );
198
199
            return (object) $endpointSettings;
200
        }
201
202
        return null;
203
    }
204
205
    /**
206
     * [isEndpoint Check the requested endpoint, scope and tier parts]
207
     * @param  string  $api
208
     * @param  string  $endpoint
209
     * @return object|null
210
     */
211
    public function isEndpoint($api, $endpoint)
212
    {
213
        /**
214
         * Return if it's a system endpoint
215
         */
216
        if (null !== ($endpointSettings = $this->isSystemEndpoint($api, $endpoint))) {
217
            return $endpointSettings;
218
        }
219
220
        /**
221
         * Check if the endpoint is a RouterInterface
222
         */
223
        $endpoint = htmlspecialchars($endpoint, ENT_QUOTES, 'UTF-8');
224
        $index = array_search($api, $this->BASE_ENDPOINTS);
225
226
        if ($index !== false) {
227
            if (isset($this->registry[$api])) {
228
                $endpointSettings = array(
229
                    'path' => $endpoint,
230
                    'model' => array(
231
                        'namespace' => $this->NAMESPACE_ENDPOINTS[$api],
232
                        'class' => $this->BASE_ENDPOINTS[$index],
233
                        'method' => basename($endpoint),
234
                        'scope' => 'private',
235
                    ),
236
                );
237
238
                /**
239
                 * Nothing dynamic, found an exact match
240
                 * @var array
241
                 */
242
                if (array_search($endpoint, $this->registry[$api]) !== false) {
243
                    if (method_exists($this->NAMESPACE_ENDPOINTS[$api], 'scope')) {
244
                        $classScope = (new $this->NAMESPACE_ENDPOINTS[$api]())->scope();
245
                        $position = array_search($endpoint, $this->registry[$api]);
246
247
                        if (is_array($classScope) && isset($classScope[$position])) {
248
                            $endpointSettings['model']['scope'] = $classScope[$position];
249
                        } else {
250
                            if (!is_array($classScope)) {
251
                                $endpointSettings['model']['scope'] = $classScope;
252
                            }
253
                        }
254
                    }
255
                    return (object) $endpointSettings;
256
                }
257
258
                /**
259
                 * Check for exact match in middleware routes
260
                 */
261
                foreach ($this->registry[$api] as $i => $path) {
262
                    if ($path instanceof \responsible\core\endpoints\RouteMiddlewareInterface) {
263
                        $middlewareRoute = $path->getRoute();
264
                        $scope = $path->getScope();
265
266
                        if ($this->isWildcardEndpoint($middlewareRoute, $endpoint)) {
267
                            // If the endpoint is a wildcard endpoint, we can return it directly
268
                            if ($scope == 'public') {
269
                                $scope = 'anonymous';
270
                            }
271
                            $endpointSettings['model']['scope'] = $scope;
272
                            $endpointSettings['model']['middleware'] = $path;
273
                            $endpointSettings['model']['method'] = '';
274
                            return (object) $endpointSettings;
275
                        }
276
277
                        if ($middlewareRoute === $endpoint) {
278
                            $scope = $path->getScope();
279
                            if (isset($_SERVER['REQUEST_METHOD'])) {
280
                                $serverMethod = $_SERVER['REQUEST_METHOD'];
281
                                if ($path->getVerb() !== $serverMethod) {
282
                                    continue;
283
                                }
284
                            }
285
                            if ($scope == 'public') {
286
                                $scope = 'anonymous';
287
                            }
288
                            $endpointSettings['model']['scope'] = $scope;
289
                            $endpointSettings['model']['middleware'] = $path;
290
                            return (object) $endpointSettings;
291
                        }
292
                    }
293
                }
294
295
                /**
296
                 * Check for dynamic uri eg: {asset_id}
297
                 * Dynamic uri's must be wrapped in {} for a true return
298
                 */
299
                foreach ($this->registry[$api] as $i => $path) {
300
                    $endpointRegister = $path;
301
                    $methodArgs = [];
302
303
                    $routeInstance = null;
304
                    if ($path instanceof \responsible\core\endpoints\RouteMiddlewareInterface) {
305
                        $routeInstance = $path;
306
                        $endpointRegister = $path = $routeInstance->getRoute();
307
                        $scope = $routeInstance->getScope();
0 ignored issues
show
Unused Code introduced by
The assignment to $scope is dead and can be removed.
Loading history...
308
309
                        if (isset($_SERVER['REQUEST_METHOD'])) {
310
                            $serverMethod = $_SERVER['REQUEST_METHOD'];
311
                            if ($routeInstance->getVerb() !== $serverMethod) {
312
                                continue;
313
                            }
314
                        } else {
315
                            // If no request method is set then continue to force a bad request
316
                            continue;
317
                        }
318
                    }
319
320
                    /**
321
                     * If comparing the two sizes are not equal
322
                     * then no use continuing through the loop
323
                     */
324
                    if (!$this->uriCheckSize($endpointRegister, $endpoint)) {
325
                        continue;
326
                    }
327
328
                    /**
329
                     * This replacment will create a pattern to use as a match all expression
330
                     * @var [string]
331
                     */
332
                    $endpointRegister = preg_replace('@/{(.*?)}@', '/(.*?)', $endpointRegister);
333
334
                    if (preg_match_all('@^' . $endpointRegister . '$@i', $endpoint, $dynamicParts)) {
335
                        $endpointFilter = $this->filterParts($endpoint, $dynamicParts);
336
                        $model = $this->getClassModel($path);
337
338
                        /**
339
                         * Find the dynamic parts and set them as argument key(s)
340
                         * then combine them with the endpoint request and set the request parts
341
                         * as argument value(s)
342
                         */
343
                        if (preg_match_all("/(?<={).*?(?=})/", $path, $registerParts)) {
344
                            if (isset($registerParts[0][0])) {
345
                                $registerParts = $registerParts[0];
346
347
                                if (sizeof($endpointFilter) == sizeof($registerParts)) {
348
                                    $methodArgs = array_combine($registerParts, $endpointFilter);
349
                                }
350
                            }
351
                        }
352
353
                        $scope = 'private';
354
                        if ($routeInstance instanceof \responsible\core\endpoints\RouteMiddlewareInterface) {
355
                            $scope = $routeInstance->getScope();
356
                            if ($scope == 'public') {
357
                                $scope = 'anonymous';
358
                            }
359
                        }
360
361
                        if (method_exists($this->NAMESPACE_ENDPOINTS[$api], 'scope')) {
362
                            $classScope = (new $this->NAMESPACE_ENDPOINTS[$api]())->scope();
363
                            $position = array_search($path, $this->registry[$api]);
364
365
                            if (is_array($classScope) && isset($classScope[$position])) {
366
                                $scope = $classScope[$position];
367
                            } else {
368
                                if (!is_array($classScope)) {
369
                                    $scope = $classScope;
370
                                }
371
                            }
372
                        }
373
374
                        $endpointSettings['model'] = array(
375
                            'scope' => $scope,
376
                            'namespace' => $this->NAMESPACE_ENDPOINTS[$api],
377
                            'class' => $model['class'],
378
                            'method' => $model['method'],
379
                            'arguments' => $methodArgs,
380
                            'middleware' => $routeInstance,
381
                        );
382
383
                        return (object) $endpointSettings;
384
                    } else {
385
                        continue;
386
                    }
387
                }
388
            }
389
        }
390
391
        return;
392
    }
393
394
    /**
395
     * [isWildcardEndpoint Check if the requested route matches a wildcard endpoint]
396
     * @param  string  $registeredRoute
397
     * @param  string  $requestedRoute
398
     * @return bool|null
399
     */
400
    private function isWildcardEndpoint($registeredRoute, $requestedRoute)
401
    {
402
        // Check if the registered route ends with '*'
403
        if (substr($registeredRoute, -1) === '*') {
404
            // Remove the trailing '*' and trim any trailing slash
405
            $prefix = rtrim(substr($registeredRoute, 0, -1), '/');
406
            // Also trim any trailing slash from requested route for comparison
407
            $requestedTrimmed = rtrim($requestedRoute, '/');
408
            // Check if requested route starts with the prefix
409
            return strpos($requestedTrimmed, $prefix) === 0;
410
        }
411
        return null;
412
    }
413
414
    /**
415
     * [filterParts Prepare routed parts]
416
     * @return array
417
     */
418
    private function filterParts($uri, $parts)
419
    {
420
        $filter = array();
421
422
        foreach ($parts as $p => $part) {
423
            if (is_array($part)) {
424
                foreach ($part as $i => $parti) {
425
                    if ($parti !== $uri) {
426
                        $filter[] = $parti;
427
                    }
428
                }
429
            }
430
        }
431
432
        return $filter;
433
    }
434
435
    /**
436
     * [uriCheckSize]
437
     *
438
     * Compare the current request endpoint with the registered endpoint
439
     * only return the same tier sizes
440
     *
441
     * @return boolean
442
     */
443
    private function uriCheckSize($endpointRegister, $endpoint)
444
    {
445
        $registerExplode = explode('/', $endpointRegister);
446
        $endpointExplode = explode('/', $endpoint);
447
        return (sizeof($registerExplode) === sizeof($endpointExplode));
448
    }
449
450
    /**
451
     * [getClassModel Class, Method]
452
     * @return array
453
     */
454
    private function getClassModel($request_path)
455
    {
456
        $cm = explode('/', $request_path);
457
458
        if (!empty($cm) && sizeof($cm) >= 2) {
459
            $cm = array_values(array_filter($cm));
460
461
            return array(
462
                'class' => $cm[0],
463
                'method' => $cm[1] ?? '',
464
            );
465
        }
466
467
        return [
468
            'class' => '',
469
            'method' => '',
470
        ];
471
    }
472
}
473