Meta   D
last analyzed

Complexity

Total Complexity 59

Size/Duplication

Total Lines 424
Duplicated Lines 0 %

Test Coverage

Coverage 81.34%

Importance

Changes 0
Metric Value
eloc 120
dl 0
loc 424
ccs 109
cts 134
cp 0.8134
rs 4.08
c 0
b 0
f 0
wmc 59

22 Methods

Rating   Name   Duplication   Size   Complexity  
A setCache() 0 3 1
B prepareRoutePattern() 0 25 8
A setMethod() 0 3 1
A setParam() 0 11 2
A setRoute() 0 3 1
A setPrivilege() 0 3 1
A setAcl() 0 3 1
A __construct() 0 3 1
A initRoute() 0 4 2
A getFile() 0 3 1
A getPrivilege() 0 3 1
B prepareCache() 0 19 7
A getAcl() 0 3 2
A __set_state() 0 7 2
A getRoute() 0 3 2
B process() 0 35 7
A setAccept() 0 13 2
A getAccept() 0 3 2
A getCache() 0 3 1
A getParams() 0 3 1
A getMethod() 0 3 2
B params() 0 35 11

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
/**
4
 * Bluz Framework Component
5
 *
6
 * @copyright Bluz PHP Team
7
 * @link      https://github.com/bluzphp/framework
8
 */
9
10
declare(strict_types=1);
11
12
namespace Bluz\Controller;
13
14
use Bluz\Common\Exception\ComponentException;
15
use Bluz\Common\Options;
16
use Bluz\Proxy\Request;
17
use Closure;
18
use ReflectionException;
19
20
/**
21
 * Meta information from reflection of the function
22
 *
23
 * @package  Bluz\Controller
24
 * @author   Anton Shevchuk
25
 */
26
class Meta
27
{
28
    use Options;
29
30
    /**
31
     * @var string full path to file
32
     */
33
    protected $file;
34
35
    /**
36
     * @var integer cache TTL
37
     */
38
    protected $cache = 0;
39
40
    /**
41
     * @var array list of Accept
42
     */
43
    protected $accept = [];
44
45
    /**
46
     * @var array list of Acl
47
     */
48
    protected $acl = [];
49
50
    /**
51
     * @var array list of HTTP methods
52
     */
53
    protected $method = [];
54
55
    /**
56
     * @var array described params
57
     */
58
    protected $params = [];
59
60
    /**
61
     * @var string privilege
62
     */
63
    protected $privilege;
64
65
    /**
66
     * @var array routers
67
     */
68
    protected $route = [];
69
70
    /**
71
     * @var array default values of params
72
     */
73
    protected $values = [];
74
75
    /**
76
     * Constructor of Reflection
77
     *
78
     * @param string $file
79
     */
80 587
    public function __construct($file)
81
    {
82 587
        $this->file = $file;
83 587
    }
84
85
    /**
86
     * Set state required for working with var_export (used inside PHP File cache)
87
     *
88
     * @param  array $array
89
     *
90
     * @return Meta
91
     */
92
    public static function __set_state($array)
93
    {
94
        $instance = new Meta($array['file']);
95
        foreach ($array as $key => $value) {
96
            $instance->{$key} = $value;
97
        }
98
        return $instance;
99
    }
100
101
    /**
102
     * Process to get reflection from file
103
     *
104
     * @return void
105
     * @throws ComponentException
106
     * @throws ReflectionException
107
     */
108 587
    public function process(): void
109
    {
110
        /** @var Closure|object $closure */
111 587
        $closure = include $this->file;
112
113 587
        if (!is_callable($closure)) {
114 1
            throw new ComponentException("There is no callable structure in file `{$this->file}`");
115
        }
116
117 587
        $reflection = new \ReflectionFunction($closure);
118
119
        // check and normalize params by doc comment
120 587
        $docComment = $reflection->getDocComment();
121
122
        // get all options by one regular expression
123 587
        if (preg_match_all('/\s*\*\s*\@([a-z0-9-_]+)\s+(.*).*\s+/i', $docComment, $matches)) {
124 587
            foreach ($matches[1] as $i => $key) {
125 587
                $this->setOption($key, trim($matches[2][$i]));
126
            }
127
        }
128
129
        // init routes
130 587
        $this->initRoute();
131
132
        // get params and convert it to simple array
133 587
        $reflectionParams = $reflection->getParameters();
134
        // setup params and optional params
135 587
        foreach ($reflectionParams as $param) {
136 587
            $name = $param->getName();
137
            // if some function params is missed in description
138 587
            if (!isset($this->params[$name])) {
139 587
                $this->params[$name] = null;
140
            }
141 587
            if ($param->isOptional()) {
142 587
                $this->values[$name] = $param->getDefaultValue();
143
            }
144
        }
145 587
    }
146
147
    /**
148
     * Process request params
149
     *
150
     *  - type conversion
151
     *  - set default value
152
     *
153
     * @param array $requestParams
154
     *
155
     * @return array
156
     */
157 40
    public function params(array $requestParams): array
158
    {
159
        // apply type and default value for request params
160 40
        $params = [];
161 40
        foreach ($this->params as $param => $type) {
162 6
            if (isset($requestParams[$param])) {
163 6
                switch ($type) {
164 6
                    case 'bool':
165 6
                    case 'boolean':
166
                        $params[] = (bool)$requestParams[$param];
167
                        break;
168 6
                    case 'int':
169 6
                    case 'integer':
170 3
                        $params[] = (int)$requestParams[$param];
171 3
                        break;
172 6
                    case 'float':
173
                        $params[] = (float)$requestParams[$param];
174
                        break;
175 6
                    case 'string':
176
                        $params[] = (string)$requestParams[$param];
177
                        break;
178 6
                    case 'array':
179
                        $params[] = (array)$requestParams[$param];
180
                        break;
181
                    default:
182 6
                        $params[] = $requestParams[$param];
183 6
                        break;
184
                }
185 2
            } elseif (isset($this->values[$param])) {
186
                $params[] = $this->values[$param];
187
            } else {
188 2
                $params[] = null;
189
            }
190
        }
191 40
        return $params;
192
    }
193
194
    /**
195
     * Get path to file
196
     *
197
     * @return string
198
     */
199
    public function getFile(): string
200
    {
201
        return $this->file;
202
    }
203
204
    /**
205
     * Get Cache TTL
206
     *
207
     * @return integer
208
     */
209 41
    public function getCache(): int
210
    {
211 41
        return $this->cache;
212
    }
213
214
    /**
215
     * Set Cache TTL
216
     *
217
     * @param string $ttl
218
     *
219
     * @return void
220
     */
221 2
    public function setCache(string $ttl): void
222
    {
223 2
        $this->cache = $this->prepareCache($ttl);
224 2
    }
225
226
    /**
227
     * Prepare Cache
228
     *
229
     * @param string $cache
230
     *
231
     * @return integer
232
     */
233 2
    protected function prepareCache(string $cache): int
234
    {
235 2
        $num = (int)$cache;
236 2
        $time = 'min';
237
238 2
        if ($pos = strpos($cache, ' ')) {
239 2
            $time = substr($cache, $pos);
240
        }
241
242 2
        switch ($time) {
243 2
            case 'day':
244 2
            case 'days':
245
                return $num * 86400;
246 2
            case 'hour':
247 2
            case 'hours':
248
                return $num * 3600;
249 2
            case 'min':
250
            default:
251 2
                return $num * 60;
252
        }
253
    }
254
255
    /**
256
     * Get accepted type
257
     *
258
     * @return array|null
259
     */
260 40
    public function getAccept(): ?array
261
    {
262 40
        return count($this->accept) ? $this->accept : null;
263
    }
264
265
    /**
266
     * Set accepted types
267
     *
268
     * @param string $accept
269
     *
270
     * @return void
271
     */
272
    public function setAccept(string $accept): void
273
    {
274
        // allow accept map
275
        $acceptMap = [
276
            'ANY' => Request::TYPE_ANY,
277
            'HTML' => Request::TYPE_HTML,
278
            'JSON' => Request::TYPE_JSON
279
        ];
280
281
        $accept = strtoupper($accept);
282
283
        if (isset($acceptMap[$accept])) {
284
            $this->accept[] = $acceptMap[$accept];
285
        }
286
    }
287
288
    /**
289
     * Get Acl privileges
290
     *
291
     * @return array|null
292
     */
293 1
    public function getAcl(): ?array
294
    {
295 1
        return count($this->acl) ? $this->acl : null;
296
    }
297
298
    /**
299
     * Set Acl privileges
300
     *
301
     * @param string $acl
302
     *
303
     * @return void
304
     */
305 2
    public function setAcl(string $acl): void
306
    {
307 2
        $this->acl[] = $acl;
308 2
    }
309
310
    /**
311
     * Get HTTP Method
312
     *
313
     * @return array|null
314
     */
315 41
    public function getMethod(): ?array
316
    {
317 41
        return count($this->method) ? $this->method : null;
318
    }
319
320
    /**
321
     * Set HTTP Method
322
     *
323
     * @param string $method
324
     *
325
     * @return void
326
     */
327 2
    public function setMethod(string $method): void
328
    {
329 2
        $this->method[] = strtoupper($method);
330 2
    }
331
332
    /**
333
     * Get all params
334
     *
335
     * @return array
336
     */
337 587
    public function getParams(): array
338
    {
339 587
        return $this->params;
340
    }
341
342
    /**
343
     * Set param types
344
     *
345
     * @param string $param
346
     *
347
     * @return void
348
     */
349 587
    public function setParam(string $param): void
350
    {
351
        // prepare params data
352
        // setup param types
353 587
        if (strpos($param, '$') === false) {
354
            return;
355
        }
356
357 587
        [$type, $key] = preg_split('/[ $]+/', $param);
358
359 587
        $this->params[$key] = trim($type);
360 587
    }
361
362
    /**
363
     * Get Privilege fo ACL
364
     *
365
     * @return string|null
366
     */
367 41
    public function getPrivilege(): ?string
368
    {
369 41
        return $this->privilege;
370
    }
371
372
    /**
373
     * Set Privilege fo ACL allow only one privilege
374
     *
375
     * @param string $privilege
376
     *
377
     * @return void
378
     */
379 2
    public function setPrivilege(string $privilege): void
380
    {
381 2
        $this->privilege = $privilege;
382 2
    }
383
384
    /**
385
     * Get Route
386
     *
387
     * @return array|null
388
     */
389 587
    public function getRoute(): ?array
390
    {
391 587
        return count($this->route) ? $this->route : null;
392
    }
393
394
    /**
395
     * Set Route
396
     *
397
     * @param string $route
398
     *
399
     * @return void
400
     */
401 587
    public function setRoute(string $route): void
402
    {
403 587
        $this->route[$route] = null;
404 587
    }
405
406
    /**
407
     * Init Route
408
     *
409
     * @return void
410
     */
411 587
    protected function initRoute(): void
412
    {
413 587
        foreach ($this->route as $route => &$pattern) {
414 587
            $pattern = $this->prepareRoutePattern($route);
415
        }
416 587
    }
417
418
    /**
419
     * Prepare Route pattern
420
     *
421
     * @param string $route
422
     *
423
     * @return string
424
     */
425 587
    protected function prepareRoutePattern(string $route): string
426
    {
427 587
        $pattern = str_replace('/', '\/', $route);
428
429 587
        foreach ($this->getParams() as $param => $type) {
430 587
            switch ($type) {
431 587
                case 'int':
432 587
                case 'integer':
433 587
                    $pattern = str_replace("{\$$param}", "(?P<$param>[0-9]+)", $pattern);
434 587
                    break;
435 587
                case 'float':
436 587
                    $pattern = str_replace("{\$$param}", "(?P<$param>[0-9.,]+)", $pattern);
437 587
                    break;
438 587
                case 'string':
439 587
                case 'module':
440 587
                case 'controller':
441 587
                    $pattern = str_replace(
442 587
                        "{\$$param}",
443 587
                        "(?P<$param>[a-zA-Z0-9-_.]+)",
444
                        $pattern
445
                    );
446 587
                    break;
447
            }
448
        }
449 587
        return '/^' . $pattern . '/i';
450
    }
451
}
452