Completed
Push — master ( 624f04...3c7473 )
by Anton
13s queued 11s
created

Meta::getFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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