Passed
Push — master ( 89971b...1a5c63 )
by Vince
01:47
created

map::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 1
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 0
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 1
rs 10
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
namespace responsible\core\endpoints;
17
18
use responsible\core\endpoints;
19
use responsible\core\exception;
20
use responsible\core\route;
21
use responsible\core\interfaces;
22
23
class map extends route\router implements interfaces\optionsInterface
24
{
25
    use \responsible\core\traits\optionsTrait;
26
27
    /**
28
     * [$BASE_ENDPOINTS]
29
     * @var array
30
     */
31
    private $BASE_ENDPOINTS = array();
32
33
    /**
34
     * [$BASE_ENDPOINTS]
35
     * @var array
36
     */
37
    private $NAMESPACE_ENDPOINTS = array();
38
39
    /**
40
     * [$registry]
41
     * @var array
42
     */
43
    private $registry = array();
44
45
    /**
46
     * [$middleWareClass Holds middleware class object]
47
     * @var object
48
     */
49
    private static $middleWareClass;
50
51
    /**
52
     * [$SYSTEM_ENDPOINTS Reserved system Endpoints]
53
     * @var array
54
     */
55
    const SYSTEM_ENDPOINTS = [
56
        'token' => '/token/access_token',
57
        'user' => [
58
            '/user/create',
59
            '/user/load',
60
        ],
61
    ];
62
63
    /**
64
     * [__construct Silence...]
65
     */
66
    public function __construct() {}
67
68
    /**
69
     * [register Scan and register endpoints defined in services]
70
     * @return array
71
     */
72
    public function register()
73
    {
74
        $options = $this->options;
75
76
        /**
77
         * Check if a custom directory was set in the Responsible API options
78
         */
79
        if( (isset($options['classRoute']) && !empty($options['classRoute'])) && 
80
            (isset($options['classRoute']['directory']) && isset($options['classRoute']['namespace']))
81
        ) {
82
            $customService = $this->options['classRoute'];
83
            $directory = $customService['directory'];
84
            $middleware = $customService['namespace'];
85
86
        }else {
87
            $middleware = 'responsible';
88
89
            $endpoint = str_replace(
90
                array('core', '/', '\\'),
91
                array('service', DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR),
92
                __NAMESPACE__
93
            );
94
95
            $directory = $this->route()->base['root'] . '/' . str_replace('responsible/', '', $endpoint);
96
        }
97
98
        if (!is_dir($directory)) {
99
            (new exception\errorException)
100
                ->message('Directory Error:: responsible\service needs to exist. See documentation on setting up a service.')
101
                ->error('NOT_EXTENDED');
102
        }
103
104
        $scanned = '';
105
        $scanDir = scandir($directory);
106
107
        if (!empty($scanDir)) { 
108
            $scanned = array_values(
109
                array_diff(
110
                    $scanDir,
111
                    array('..', '.', '.DS_Store')
112
                )
113
            );
114
        }
115
116
        if (empty($scanned)) {
117
            (new exception\errorException)
118
                ->message('Class Error:: responsible\service\endpoints needs at least 1 class file. See documentation on setting up a service.')
119
                ->error('NOT_EXTENDED');
120
        }
121
122
        foreach ($scanned as $e => $point) {
123
124
            if (substr($point, -4) == '.php') {
125
                $point = str_replace('.php', '', $point);
126
127
                $this->BASE_ENDPOINTS[] = $point;
128
129
                $endpoint = str_replace('core', 'service', __NAMESPACE__) . '\\' . $point;
130
131
                if ($middleware !== 'responsible') {
132
                    $endpoint = $middleware . '\\service\\endpoints\\' . $point;
133
                }
134
135
                $child = $endpoint;
136
137
                $this->NAMESPACE_ENDPOINTS[$point] = $endpoint;
138
139
                if (class_exists($child)) {
140
                    self::$middleWareClass = new $child;
141
                    $this->registry[$point] = self::$middleWareClass->register();
142
                }else{
143
                    (new exception\errorException)
144
                        ->message("Class Error:: class {$child} needs to exist. See documentation on setting up a service.")
145
                        ->error('NOT_EXTENDED');
146
                }
147
            }
148
        }
149
150
        return $this->registry;
151
    }
152
153
    /**
154
     * [isSystemEndpoint Check if the endpoint request is a ResponsibleAPI reserved endpoint]
155
     * @param  string  $api
156
     * @param  string  $endpoint
157
     * @return object|null
158
     */
159
    private function isSystemEndpoint($api, $endpoint)
160
    {
161
        if (isset(self::SYSTEM_ENDPOINTS[$api]) &&
162
            (
163
                in_array($endpoint, self::SYSTEM_ENDPOINTS) ||
164
                array_search($endpoint, self::SYSTEM_ENDPOINTS[$api]) !== false
165
            )
166
        ) {
167
            $endpointSettings = [];
168
            $methodCreate = explode('/', $endpoint);
169
            $methodCreate = array_values(array_filter($methodCreate));
170
            $method = '';
171
172
            foreach ($methodCreate as $i => $parts) {
173
                if (preg_match_all('#_#', $parts)) {
174
                    $parts = str_replace('_', '', lcfirst(ucwords($parts, '_')));
175
                }
176
                if ($i > 0) {
177
                    $method .= ucfirst($parts);
178
                } else {
179
                    $method .= $parts;
180
                }
181
            }
182
183
            $endpointSettings['model'] = array(
184
                'scope' => 'system',
185
                'namespace' => 'responsible\core\endpoints\system',
186
                'class' => 'system',
187
                'method' => $method,
188
                'arguments' => '',
189
            );
190
191
            return (object) $endpointSettings;
192
        }
193
194
        return null;
195
    }
196
197
    /**
198
     * [isEndpoint Check the requested endpoint, scope and tier parts]
199
     * @param  string  $api
200
     * @param  string  $endpoint
201
     * @return object|null
202
     */
203
    public function isEndpoint($api, $endpoint)
204
    {
205
        /**
206
         * Return if it's a system endpoint
207
         */
208
        if (null !== ($endpointSettings = $this->isSystemEndpoint($api, $endpoint))) {
209
            return $endpointSettings;
210
        }
211
212
        $endpoint = htmlspecialchars($endpoint, ENT_QUOTES, 'UTF-8');
213
        $index = array_search($api, $this->BASE_ENDPOINTS);
214
215
        if ($index !== false) {
216
            if (isset($this->registry[$api])) {
217
                $endpointSettings = array(
218
                    'path' => $endpoint,
219
                    'model' => array(
220
                        'namespace' => $this->NAMESPACE_ENDPOINTS[$api],
221
                        'class' => $this->BASE_ENDPOINTS[$index],
222
                        'method' => basename($endpoint),
223
                        'scope' => 'private',
224
                    ),
225
                );
226
227
                /**
228
                 * Nothing dynamic, found an exact match
229
                 * @var array
230
                 */
231
                if (array_search($endpoint, $this->registry[$api]) !== false) {
232
                    if( method_exists($this->NAMESPACE_ENDPOINTS[$api], 'scope') ) {
233
                        $classScope = (new $this->NAMESPACE_ENDPOINTS[$api])->scope();
234
                        $position = array_search($endpoint, $this->registry[$api]);
235
                        
236
                        if( is_array($classScope) && isset($classScope[$position]) ) {
237
                            $endpointSettings['model']['scope'] = $classScope[$position];
238
239
                        }else{
240
241
                            if( !is_array($classScope) ) {
242
                                $endpointSettings['model']['scope'] = $classScope;
243
                            }
244
                        }
245
                    }
246
                    return (object) $endpointSettings;
247
                }
248
249
                /**
250
                 * Check for dynamic uri eg: {asset_id}
251
                 * Dynamic uri's must be wrapped in {} for a true return
252
                 */
253
                foreach ($this->registry[$api] as $i => $path) {
254
                    $endpointRegister = $path;
255
                    $methodArgs = [];
256
257
                    /**
258
                     * If comparing the two sizes are not equal
259
                     * then no use continuing through the loop
260
                     */
261
                    if (!$this->uriCheckSize($endpointRegister, $endpoint)) {
262
                        continue;
263
                    }
264
265
                    /**
266
                     * This replacment will create a pattern to use as a match all expression
267
                     * @var [string]
268
                     */
269
                    $endpointRegister = preg_replace('@/{(.*?)}@', '/(.*?)', $endpointRegister);
270
271
                    if (preg_match_all('@^' . $endpointRegister . '$@i', $endpoint, $dynamicParts)) {
272
                        $endpointFilter = $this->filterParts($endpoint, $dynamicParts);
273
                        $model = $this->getClassModel($path);
274
275
                        /**
276
                         * Find the dynamic parts and set them as argument key(s)
277
                         * then combine them with the endpoint request and set the request parts
278
                         * as argument value(s)
279
                         */
280
                        if (preg_match_all("/(?<={).*?(?=})/", $path, $registerParts)) {
281
                            if (isset($registerParts[0][0])) {
282
                                $registerParts = $registerParts[0];
283
284
                                if (sizeof($endpointFilter) == sizeof($registerParts)) {
285
                                    $methodArgs = array_combine($registerParts, $endpointFilter);
286
                                }
287
                            }
288
                        }
289
290
                        $scope = 'private';
291
292
                        if( method_exists($this->NAMESPACE_ENDPOINTS[$api], 'scope') ) {
293
                            $classScope = (new $this->NAMESPACE_ENDPOINTS[$api])->scope();
294
                            $position = array_search($path, $this->registry[$api]);
295
                            
296
                            if( is_array($classScope) && isset($classScope[$position]) ) {
297
                                $scope = $classScope[$position];
298
299
                            }else{
300
301
                                if( !is_array($classScope) ) {
302
                                    $scope = $classScope;
303
                                }
304
                            }
305
                        }
306
307
                        $endpointSettings['model'] = array(
308
                            'scope' => $scope,
309
                            'namespace' => $this->NAMESPACE_ENDPOINTS[$api],
310
                            'class' => $model['class'],
311
                            'method' => $model['method'],
312
                            'arguments' => $methodArgs,
313
                        );
314
315
                        return (object) $endpointSettings;
316
                    } else {
317
                        continue;
318
                    }
319
                }
320
            }
321
        }
322
323
        return;
324
    }
325
326
    /**
327
     * [filterParts Prepare routed parts]
328
     * @return array
329
     */
330
    private function filterParts($uri, $parts)
331
    {
332
        $filter = array();
333
334
        foreach ($parts as $p => $part) {
335
            if (is_array($part)) {
336
                foreach ($part as $i => $parti) {
337
                    if ($parti !== $uri) {
338
                        $filter[] = $parti;
339
                    }
340
                }
341
            }
342
        }
343
344
        return $filter;
345
    }
346
347
    /**
348
     * [uriCheckSize]
349
     *
350
     * Compare the current request endpoint with the registered endpoint
351
     * only return the same tier sizes
352
     *
353
     * @return boolean
354
     */
355
    private function uriCheckSize($endpointRegister, $endpoint)
356
    {
357
        $registerExplode = explode('/', $endpointRegister);
358
        $endpointExplode = explode('/', $endpoint);
359
        return (sizeof($registerExplode) === sizeof($endpointExplode));
360
    }
361
362
    /**
363
     * [getClassModel Class, Method]
364
     * @return array
365
     */
366
    private function getClassModel($request_path)
367
    {
368
        $cm = explode('/', $request_path);
369
370
        if (!empty($cm) && sizeof($cm) >= 2) {
371
            $cm = array_values(array_filter($cm));
372
373
            return array(
374
                'class' => $cm[0],
375
                'method' => $cm[1],
376
            );
377
        }
378
379
        return;
380
    }
381
}
382