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

Controller   A

Complexity

Total Complexity 37

Size/Duplication

Total Lines 392
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 83.33%

Importance

Changes 0
Metric Value
dl 0
loc 392
ccs 100
cts 120
cp 0.8333
rs 9.44
c 0
b 0
f 0
wmc 37
lcom 1
cbo 9

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 1
A getModule() 0 4 1
A setModule() 0 4 1
A getController() 0 4 1
A setController() 0 4 1
A getParams() 0 4 1
A setParams() 0 4 1
A getTemplate() 0 4 1
A setTemplate() 0 4 1
A run() 0 8 2
B process() 0 45 6
A findFile() 0 11 2
A getFile() 0 7 2
A initMeta() 0 18 2
A getMeta() 0 7 2
A assign() 0 4 1
A getData() 0 7 2
A loadData() 0 10 3
A saveData() 0 12 2
A jsonSerialize() 0 4 1
A __toString() 0 33 3
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\Application\Application;
14
use Bluz\Auth\IdentityInterface;
15
use Bluz\Common\Exception\CommonException;
16
use Bluz\Common\Exception\ComponentException;
17
use Bluz\Common\Helper;
18
use Bluz\Proxy\Cache;
19
use Bluz\Proxy\Logger;
20
use Bluz\Response\ResponseTrait;
21
use Bluz\View\View;
22
23
/**
24
 * Statement
25
 *
26
 * @package  Bluz\Controller
27
 * @author   Anton Shevchuk
28
 *
29
 * @method void attachment(string $file)
30
 * @method void checkHttpAccept()
31
 * @method void checkHttpMethod()
32
 * @method void checkPrivilege()
33
 * @method void denied()
34
 * @method void disableLayout()
35
 * @method void disableView()
36
 * @method Controller dispatch(string $module, string $controller, array $params = [])
37
 * @method void redirect(string $url)
38
 * @method void redirectTo(string $module, string $controller, array $params = [])
39
 * @method void reload()
40
 * @method bool isAllowed($privilege)
41
 * @method void useJson()
42
 * @method void useLayout($layout)
43
 * @method IdentityInterface user()
44
 */
45
class Controller implements \JsonSerializable
46
{
47
    use Helper;
48
    use ResponseTrait;
49
50
    /**
51
     * @var string
52
     */
53
    protected $module;
54
55
    /**
56
     * @var string
57
     */
58
    protected $controller;
59
60
    /**
61
     * @var array
62
     */
63
    protected $params;
64
65
    /**
66
     * @var string Cache key
67
     */
68
    protected $key;
69
70
    /**
71
     * @var string Template name, by default is equal to controller name
72
     */
73
    protected $template;
74
75
    /**
76
     * @var string
77
     */
78
    protected $file;
79
80
    /**
81
     * @var Meta
82
     */
83
    protected $meta;
84
85
    /**
86
     * @var Data
87
     */
88
    protected $data;
89
90
    /**
91
     * Constructor of Statement
92
     *
93
     * @param string $module
94
     * @param string $controller
95
     * @param array  $params
96
     *
97
     * @throws CommonException
98
     */
99 604
    public function __construct($module, $controller, array $params = [])
100
    {
101
        // initial default helper path
102 604
        $this->addHelperPath(__DIR__ . '/Helper/');
103
104 604
        $this->setModule($module);
105 604
        $this->setController($controller);
106 604
        $this->setParams($params);
107 604
        $this->setTemplate($controller . '.phtml');
108
109 604
        $this->key = "data.$module.$controller." . md5(http_build_query($params));
110 604
    }
111
112
    /**
113
     * @return string
114
     */
115 7
    public function getModule(): string
116
    {
117 7
        return $this->module;
118
    }
119
120
    /**
121
     * @param string $module
122
     */
123 604
    protected function setModule(string $module): void
124
    {
125 604
        $this->module = $module;
126 604
    }
127
128
    /**
129
     * @return string
130
     */
131 7
    public function getController(): string
132
    {
133 7
        return $this->controller;
134
    }
135
136
    /**
137
     * @param string $controller
138
     */
139 604
    protected function setController(string $controller): void
140
    {
141 604
        $this->controller = $controller;
142 604
    }
143
144
    /**
145
     * @return array
146
     */
147
    public function getParams(): array
148
    {
149
        return $this->params;
150
    }
151
152
    /**
153
     * @param array $params
154
     */
155 604
    protected function setParams(array $params): void
156
    {
157 604
        $this->params = $params;
158 604
    }
159
160
    /**
161
     * @return string
162
     */
163 2
    public function getTemplate(): ?string
164
    {
165 2
        return $this->template;
166
    }
167
168
    /**
169
     * @param string $template
170
     */
171 604
    protected function setTemplate(string $template): void
172
    {
173 604
        $this->template = $template;
174 604
    }
175
176
    /**
177
     * Run controller logic
178
     *
179
     * @return Data
180
     * @throws ComponentException
181
     * @throws ControllerException
182
     */
183 40
    public function run(): Data
184
    {
185 40
        if (!$this->loadData()) {
186 40
            $this->process();
187 38
            $this->saveData();
188
        }
189 38
        return $this->data;
190
    }
191
192
    /**
193
     * Controller run
194
     *
195
     * @return Data
196
     * @throws ComponentException
197
     * @throws ControllerException
198
     */
199 40
    protected function process(): Data
200
    {
201
        // initial variables for use inside controller
202 40
        $module = $this->module;
203 40
        $controller = $this->controller;
204 40
        $params = $this->params;
205
206
        /**
207
         * @var \closure $controllerClosure
208
         */
209 40
        $controllerClosure = include $this->getFile();
210
211 40
        if (!\is_callable($controllerClosure)) {
212
            throw new ControllerException("Controller is not callable '{$module}/{$controller}'");
213
        }
214
215
        // process params
216 40
        $params = $this->getMeta()->params($params);
217
218
        // call Closure or Controller
219 40
        $result = $controllerClosure(...$params);
220
221
        // switch statement for result of Closure run
222
        switch (true) {
223 38
            case ($result === false):
224
                // return "false" is equal to disable view and layout
225 20
                $this->disableLayout();
226 20
                $this->disableView();
227 20
                break;
228 19
            case \is_string($result):
229
                // return string variable is equal to change view template
230
                $this->setTemplate($result);
231
                break;
232 19
            case \is_array($result):
233
                // return associative array is equal to setup view data
234 6
                $this->getData()->setFromArray($result);
235 6
                break;
236 13
            case ($result instanceof self):
237
                // return Controller - just extract data from it
238
                $this->getData()->setFromArray($result->getData()->toArray());
239
                break;
240
        }
241
242 38
        return $this->getData();
243
    }
244
245
    /**
246
     * Setup controller file
247
     *
248
     * @return void
249
     * @throws ControllerException
250
     * @throws \ReflectionException
251
     */
252 604
    protected function findFile(): void
253
    {
254 604
        $path = Application::getInstance()->getPath();
255 604
        $file = "$path/modules/{$this->module}/controllers/{$this->controller}.php";
256
257 604
        if (!file_exists($file)) {
258 3
            throw new ControllerException("Controller file not found '{$this->module}/{$this->controller}'", 404);
259
        }
260
261 604
        $this->file = $file;
262 604
    }
263
264
    /**
265
     * Get controller file path
266
     *
267
     * @return string
268
     * @throws ControllerException
269
     * @throws \ReflectionException
270
     */
271 604
    protected function getFile(): string
272
    {
273 604
        if (!$this->file) {
274 604
            $this->findFile();
275
        }
276 604
        return $this->file;
277
    }
278
279
    /**
280
     * Retrieve reflection for anonymous function
281
     *
282
     * @return void
283
     * @throws ComponentException
284
     * @throws ControllerException
285
     * @throws \ReflectionException
286
     */
287 604
    protected function initMeta(): void
288
    {
289
        // cache for reflection data
290 604
        $cacheKey = "meta.{$this->module}.{$this->controller}";
291
292 604
        if (!$meta = Cache::get($cacheKey)) {
293 604
            $meta = new Meta($this->getFile());
294 604
            $meta->process();
295
296 604
            Cache::set(
297 604
                $cacheKey,
298 604
                $meta,
299 604
                Cache::TTL_NO_EXPIRY,
300 604
                ['system', 'meta']
301
            );
302
        }
303 604
        $this->meta = $meta;
304 604
    }
305
306
    /**
307
     * Get meta information
308
     *
309
     * @return Meta
310
     * @throws ControllerException
311
     * @throws ComponentException
312
     * @throws \ReflectionException
313
     */
314 604
    public function getMeta(): Meta
315
    {
316 604
        if (!$this->meta) {
317 604
            $this->initMeta();
318
        }
319 604
        return $this->meta;
320
    }
321
322
    /**
323
     * Assign key/value pair to Data object
324
     *
325
     * @param  string $key
326
     * @param  mixed  $value
327
     *
328
     * @return void
329
     */
330 1
    public function assign($key, $value): void
331
    {
332 1
        $this->getData()->set($key, $value);
333 1
    }
334
335
    /**
336
     * Get controller Data container
337
     *
338
     * @return Data
339
     */
340 38
    public function getData(): Data
341
    {
342 38
        if (!$this->data) {
343 38
            $this->data = new Data();
344
        }
345 38
        return $this->data;
346
    }
347
348
    /**
349
     * Load Data from cache
350
     *
351
     * @return bool
352
     * @throws ComponentException
353
     * @throws ControllerException
354
     * @throws \ReflectionException
355
     */
356 40
    private function loadData(): bool
357
    {
358 40
        $cacheTime = $this->getMeta()->getCache();
359
360 40
        if ($cacheTime && $cached = Cache::get($this->key)) {
361
            $this->data = $cached;
362
            return true;
363
        }
364 40
        return false;
365
    }
366
367
    /**
368
     * Save Data to cache
369
     *
370
     * @return bool
371
     * @throws ComponentException
372
     * @throws ControllerException
373
     * @throws \ReflectionException
374
     */
375 38
    private function saveData(): bool
376
    {
377 38
        if ($cacheTime = $this->getMeta()->getCache()) {
378
            return Cache::set(
379
                $this->key,
380
                $this->getData(),
381
                $cacheTime,
382
                ['system', 'data']
383
            );
384
        }
385 38
        return false;
386
    }
387
388
    /**
389
     * Specify data which should be serialized to JSON
390
     *
391
     * @return Data
392
     */
393
    public function jsonSerialize()
394
    {
395
        return $this->getData();
396
    }
397
398
    /**
399
     * Magic cast to string
400
     *
401
     * @return string
402
     */
403 3
    public function __toString()
404
    {
405 3
        if (!$this->template) {
406
            return '';
407
        }
408
409
        try {
410
            // $view for use in closure
411 3
            $view = new View();
412
413 3
            $path = Application::getInstance()->getPath();
414
415
            // setup additional helper path
416 3
            $view->addHelperPath($path . '/layouts/helpers');
417
418
            // setup additional partial path
419 3
            $view->addPartialPath($path . '/layouts/partial');
420
421
            // setup default path
422 3
            $view->setPath($path . '/modules/' . $this->module . '/views');
423
424
            // setup template
425 3
            $view->setTemplate($this->template);
426
427
            // setup data
428 3
            $view->setFromArray($this->getData()->toArray());
429 3
            return $view->render();
430
        } catch (\Exception $e) {
431
            // save log
432
            Logger::exception($e);
433
            return '';
434
        }
435
    }
436
}
437