Completed
Pull Request — master (#411)
by Anton
06:32
created

Meta::prepareRoutePattern()   C

Complexity

Conditions 8
Paths 8

Size

Total Lines 26
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 8

Importance

Changes 0
Metric Value
cc 8
eloc 20
nc 8
nop 1
dl 0
loc 26
ccs 18
cts 18
cp 1
crap 8
rs 5.3846
c 0
b 0
f 0
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 690
    public function __construct($file)
78
    {
79 690
        $this->file = $file;
80 690
    }
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 690
    public function process()
104
    {
105
        /** @var \Closure|object $closure */
106 690
        $closure = include $this->file;
107
108 690
        if (!is_callable($closure)) {
109 1
            throw new ComponentException("There is no callable structure in file `{$this->file}`");
110
        }
111
112 690
        $reflection = new \ReflectionFunction($closure);
113
114
        // check and normalize params by doc comment
115 690
        $docComment = $reflection->getDocComment();
116
117
        // get all options by one regular expression
118 690
        if (preg_match_all('/\s*\*\s*\@([a-z0-9-_]+)\s+(.*).*\s+/i', $docComment, $matches)) {
119 690
            foreach ($matches[1] as $i => $key) {
120 690
                $this->setOption($key, trim($matches[2][$i]));
121
            }
122
        }
123
124
        // init routes
125 690
        $this->initRoute();
126
127
        // parameters available for Closure only
128 690
        if ($reflection instanceof \ReflectionFunction) {
129
            // get params and convert it to simple array
130 690
            $reflectionParams = $reflection->getParameters();
131
            // setup params and optional params
132 690
            foreach ($reflectionParams as $param) {
133 688
                $name = $param->getName();
134
                // if some function params is missed in description
135 688
                if (!isset($this->params[$name])) {
136 1
                    $this->params[$name] = null;
137
                }
138 688
                if ($param->isOptional()) {
139 688
                    $this->values[$name] = $param->getDefaultValue();
140
                }
141
            }
142
        }
143 690
    }
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 3
                $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 * 86400;
241 2
            case 'hour':
242 2
            case 'hours':
243
                return $num * 3600;
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 count($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 count($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 count($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 688
    public function getParams()
330
    {
331 688
        return $this->params;
332
    }
333
334
    /**
335
     * Set param types
336
     *
337
     * @param  string $param
338
     * @return void
339
     */
340 688
    public function setParam($param)
341
    {
342
        // prepare params data
343
        // setup param types
344 688
        if (strpos($param, '$') === false) {
345
            return;
346
        }
347
348 688
        list($type, $key) = preg_split('/[ $]+/', $param);
349
350 688
        $this->params[$key] = trim($type);
351 688
    }
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 688
    public function getRoute()
380
    {
381 688
        return count($this->route)?$this->route:null;
382
    }
383
384
    /**
385
     * Set Route
386
     *
387
     * @param  string $route
388
     * @return void
389
     */
390 688
    public function setRoute($route)
391
    {
392 688
        $this->route[$route] = null;
393 688
    }
394
395
    /**
396
     * Init Route
397
     *
398
     * @return void
399
     */
400 690
    protected function initRoute()
401
    {
402 690
        foreach ($this->route as $route => &$pattern) {
403 688
            $pattern = $this->prepareRoutePattern($route);
404
        }
405 690
    }
406
407
    /**
408
     * Prepare Route pattern
409
     *
410
     * @param  string $route
411
     * @return string
412
     */
413 688
    protected function prepareRoutePattern($route)
414
    {
415 688
        $pattern = str_replace('/', '\/', $route);
416
417 688
        foreach ($this->getParams() as $param => $type) {
418
            switch ($type) {
419 688
                case 'int':
420 688
                case 'integer':
421 688
                    $pattern = str_replace("{\$$param}", "(?P<$param>[0-9]+)", $pattern);
422 688
                    break;
423 688
                case 'float':
424 688
                    $pattern = str_replace("{\$$param}", "(?P<$param>[0-9.,]+)", $pattern);
425 688
                    break;
426 688
                case 'string':
427 688
                case 'module':
428 688
                case 'controller':
429 688
                    $pattern = str_replace(
430 688
                        "{\$$param}",
431 688
                        "(?P<$param>[a-zA-Z0-9-_.]+)",
432
                        $pattern
433
                    );
434 688
                    break;
435
            }
436
        }
437 688
        return '/^' . $pattern . '/i';
438
    }
439
}
440