Completed
Pull Request — master (#363)
by Anton
05:32
created

Reflection::getRoute()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

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