Completed
Push — master ( 0ea9e7...aef2ed )
by Anton
14s
created

Reflection::__set_state()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 8
ccs 0
cts 5
cp 0
crap 6
rs 9.4285
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
 * Reflection
19
 *
20
 * @package  Bluz\Controller
21
 * @author   Anton Shevchuk
22
 */
23
class Reflection
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 639
    public function __construct($file)
78
    {
79 639
        $this->file = $file;
80 639
    }
81
82
    /**
83
     * Set state required for working with var_export (used inside PHP File cache)
84
     *
85
     * @param  $array
86
     * @return Reflection
87
     */
88
    public static function __set_state($array)
89
    {
90
        $instance = new Reflection($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 639
    public function process()
104
    {
105
        /** @var \Closure|object $closure */
106 639
        $closure = include $this->file;
107
108 639
        if (!is_callable($closure)) {
109 1
            throw new ComponentException("There is no callable structure in file `{$this->file}`");
110
        }
111
112 639
        $reflection = new \ReflectionFunction($closure);
113
114
        // check and normalize params by doc comment
115 639
        $docComment = $reflection->getDocComment();
116
117
        // get all options by one regular expression
118 639
        if (preg_match_all('/\s*\*\s*\@([a-z0-9-_]+)\s+(.*).*\s+/i', $docComment, $matches)) {
119 639
            foreach ($matches[1] as $i => $key) {
120 639
                $this->setOption($key, trim($matches[2][$i]));
121
            }
122
        }
123
124
        // init routes
125 639
        $this->initRoute();
126
127
        // parameters available for Closure only
128 639
        if ($reflection instanceof \ReflectionFunction) {
129
            // get params and convert it to simple array
130 639
            $reflectionParams = $reflection->getParameters();
131
            // setup params and optional params
132 639
            foreach ($reflectionParams as $param) {
133 637
                $name = $param->getName();
134
                // if some function params is missed in description
135 637
                if (!isset($this->params[$name])) {
136 1
                    $this->params[$name] = null;
137
                }
138 637
                if ($param->isOptional()) {
139 637
                    $this->values[$name] = $param->getDefaultValue();
140
                }
141
            }
142
        }
143 639
    }
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 3
                        $params[] = (string)$requestParams[$param];
174 3
                        break;
175
                    case 'array':
176
                        $params[] = (array)$requestParams[$param];
177
                        break;
178
                    default:
179
                        $params[] = $requestParams[$param];
180
                        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
232 2
        if ($pos = strpos($cache, ' ')) {
233 2
            $time = substr($cache, $pos);
234
        } else {
235
            $time = 'min';
236
        }
237
238
        switch ($time) {
239 2
            case 'day':
240 2
            case 'days':
241
                return $num * 60 * 60 *24;
242 2
            case 'hour':
243 2
            case 'hours':
244
                return $num * 60 * 60;
245 2
            case 'min':
246
            default:
247 2
                return $num * 60;
248
        }
249
    }
250
251
    /**
252
     * Get accepted type
253
     *
254
     * @return array|null
255
     */
256
    public function getAccept()
257
    {
258
        return sizeof($this->accept)?$this->accept:null;
259
    }
260
261
    /**
262
     * Set accepted types
263
     *
264
     * @param  string $accept
265
     * @return void
266
     */
267
    public function setAccept($accept)
268
    {
269
        // allow accept map
270
        $acceptMap = [
271
            'ANY'  => Request::TYPE_ANY,
272
            'HTML' => Request::TYPE_HTML,
273
            'JSON' => Request::TYPE_JSON
274
        ];
275
276
        $accept = strtoupper($accept);
277
278
        if (isset($acceptMap[$accept])) {
279
            $this->accept[] = $acceptMap[$accept];
280
        }
281
    }
282
    
283
    /**
284
     * Get Acl privileges
285
     *
286
     * @return array|null
287
     */
288 1
    public function getAcl()
289
    {
290 1
        return sizeof($this->acl)?$this->acl:null;
291
    }
292
293
    /**
294
     * Set Acl privileges
295
     *
296
     * @param  string $acl
297
     * @return void
298
     */
299 2
    public function setAcl($acl)
300
    {
301 2
        $this->acl[] = $acl;
302 2
    }
303
304
    /**
305
     * Get HTTP Method
306
     *
307
     * @return array|null
308
     */
309 9
    public function getMethod()
310
    {
311 9
        return sizeof($this->method)?$this->method:null;
312
    }
313
314
    /**
315
     * Set HTTP Method
316
     *
317
     * @param  string $method
318
     * @return void
319
     */
320 2
    public function setMethod($method)
321
    {
322 2
        $this->method[] = strtoupper($method);
323 2
    }
324
325
    /**
326
     * Get all params
327
     *
328
     * @return array
329
     */
330 637
    public function getParams()
331
    {
332 637
        return $this->params;
333
    }
334
335
    /**
336
     * Set param types
337
     *
338
     * @param  string $param
339
     * @return void
340
     */
341 637
    public function setParam($param)
342
    {
343
        // prepare params data
344
        // setup param types
345 637
        if (strpos($param, '$') === false) {
346
            return;
347
        }
348
349 637
        list($type, $key) = preg_split('/[ $]+/', $param);
350
351 637
        $this->params[$key] = trim($type);
352 637
    }
353
354
    /**
355
     * Get Privilege fo ACL
356
     *
357
     * @return string
358
     */
359 9
    public function getPrivilege()
360
    {
361 9
        return $this->privilege;
362
    }
363
364
    /**
365
     * Set Privilege fo ACL allow only one privilege
366
     *
367
     * @param  string $privilege
368
     * @return void
369
     */
370 2
    public function setPrivilege($privilege)
371
    {
372 2
        $this->privilege = $privilege;
373 2
    }
374
375
    /**
376
     * Get Route
377
     *
378
     * @return array|null
379
     */
380 637
    public function getRoute()
381
    {
382 637
        return sizeof($this->route)?$this->route:null;
383
    }
384
385
    /**
386
     * Set Route
387
     *
388
     * @param  string $route
389
     * @return void
390
     */
391 637
    public function setRoute($route)
392
    {
393 637
        $this->route[$route] = null;
394 637
    }
395
396
    /**
397
     * Init Route
398
     *
399
     * @return void
400
     */
401 639
    protected function initRoute()
402
    {
403 639
        foreach ($this->route as $route => &$pattern) {
404 637
            $pattern = $this->prepareRoutePattern($route);
405
        }
406 639
    }
407
408
    /**
409
     * Prepare Route pattern
410
     *
411
     * @param  string $route
412
     * @return string
413
     */
414 637
    protected function prepareRoutePattern($route)
415
    {
416 637
        $pattern = str_replace('/', '\/', $route);
417
418 637
        foreach ($this->getParams() as $param => $type) {
419
            switch ($type) {
420 637
                case 'int':
421 637
                case 'integer':
422 637
                    $pattern = str_replace("{\$" . $param . "}", "(?P<$param>[0-9]+)", $pattern);
423 637
                    break;
424 637
                case 'float':
425 637
                    $pattern = str_replace("{\$" . $param . "}", "(?P<$param>[0-9.,]+)", $pattern);
426 637
                    break;
427 637
                case 'string':
428 637
                case 'module':
429 637
                case 'controller':
430 637
                    $pattern = str_replace(
431 637
                        "{\$" . $param . "}",
432 637
                        "(?P<$param>[a-zA-Z0-9-_.]+)",
433
                        $pattern
434
                    );
435 637
                    break;
436
            }
437
        }
438 637
        return '/^' . $pattern . '/i';
439
    }
440
}
441