Completed
Pull Request — master (#409)
by Anton
05:45
created

Meta   D

Complexity

Total Complexity 60

Size/Duplication

Total Lines 417
Duplicated Lines 0 %

Coupling/Cohesion

Components 5
Dependencies 2

Test Coverage

Coverage 76.87%

Importance

Changes 0
Metric Value
dl 0
loc 417
ccs 103
cts 134
cp 0.7687
rs 4.2857
c 0
b 0
f 0
wmc 60
lcom 5
cbo 2

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A __set_state() 0 8 2
C process() 0 41 8
A getFile() 0 4 1
A getCache() 0 4 1
A setCache() 0 4 1
B prepareCache() 0 21 7
A getAccept() 0 4 2
A getAcl() 0 4 2
A setAcl() 0 4 1
A getMethod() 0 4 2
A setMethod() 0 4 1
A getParams() 0 4 1
A setParam() 0 12 2
A getPrivilege() 0 4 1
A setPrivilege() 0 4 1
A getRoute() 0 4 2
A setRoute() 0 4 1
A initRoute() 0 6 2
C prepareRoutePattern() 0 26 8
C params() 0 36 11
A setAccept() 0 15 2

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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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