Completed
Pull Request — master (#423)
by Anton
07:53
created

Controller   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 385
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 75.83%

Importance

Changes 0
Metric Value
dl 0
loc 385
ccs 91
cts 120
cp 0.7583
rs 8.6
c 0
b 0
f 0
wmc 37
lcom 1
cbo 9

21 Methods

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