Completed
Push — master ( 929e4f...acab5f )
by Anton
07:41
created

Meta::setParam()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

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