Controller::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

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