Completed
Pull Request — master (#422)
by Anton
04:34
created

Controller::assign()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 4
ccs 0
cts 3
cp 0
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
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
     * One of HTML, JSON or empty string
91
     *
92
     * @var string
93
     */
94
    protected $render = 'HTML';
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 714
    public function __construct($module, $controller, array $params = [])
106
    {
107
        // initial default helper path
108 714
        $this->addHelperPath(__DIR__ . '/Helper/');
109
110 714
        $this->setModule($module);
111 714
        $this->setController($controller);
112 714
        $this->setParams($params);
113
114 714
        $this->template = $controller . '.phtml';
115 714
        $this->key = "data.$module.$controller." . md5(http_build_query($params));
116 714
    }
117
118
    /**
119
     * @return string
120
     */
121
    public function getModule(): string
122
    {
123
        return $this->module;
124
    }
125
126
    /**
127
     * @param string $module
128
     */
129 714
    protected function setModule(string $module)
130
    {
131 714
        $this->module = $module;
132 714
    }
133
134
    /**
135
     * @return string
136
     */
137
    public function getController(): string
138
    {
139
        return $this->controller;
140
    }
141
142
    /**
143
     * @param string $controller
144
     */
145 714
    protected function setController(string $controller)
146
    {
147 714
        $this->controller = $controller;
148 714
    }
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 714
    protected function setParams(array $params)
162
    {
163 714
        $this->params = $params;
164 714
    }
165
166
    /**
167
     * Run controller logic
168
     *
169
     * @return Data
170
     * @throws ComponentException
171
     * @throws ControllerException
172
     */
173 13
    public function run(): Data
174
    {
175 13
        if (!$this->loadData()) {
176 13
            $this->process();
177
178 11
            $this->saveData();
179
        }
180 11
        return $this->data;
181
    }
182
183
    /**
184
     * Controller run
185
     *
186
     * @return Data
187
     * @throws ComponentException
188
     * @throws ControllerException
189
     */
190 13
    protected function process(): Data
191
    {
192
        // initial variables for use inside controller
193 13
        $module = $this->module;
194 13
        $controller = $this->controller;
195 13
        $params = $this->params;
196
197
        /**
198
         * @var \closure $controllerClosure
199
         */
200 13
        $controllerClosure = include $this->getFile();
201
202 13
        if (!is_callable($controllerClosure)) {
203
            throw new ControllerException("Controller is not callable '{$module}/{$controller}'");
204
        }
205
206
        // process params
207 13
        $params = $this->getMeta()->params($params);
208
209
        // call Closure or Controller
210 13
        $result = $controllerClosure(...$params);
211
212
        // switch statement for result of Closure run
213
        switch (true) {
214 11
            case ($result === false):
215
                // return "false" is equal to disable view and layout
216 5
                $this->disableLayout();
217 5
                $this->disableView();
218 5
                break;
219 6
            case is_string($result):
220
                // return string variable is equal to change view template
221
                $this->template = $result;
222
                break;
223 6
            case is_array($result):
224
                // return associative array is equal to setup view data
225 3
                $this->getData()->setFromArray($result);
226 3
                break;
227 3
            case ($result instanceof Controller):
228
                $this->getData()->setFromArray($result->getData()->toArray());
229
                break;
230
        }
231
232 11
        return $this->getData();
233
    }
234
235
    /**
236
     * Setup controller file
237
     *
238
     * @return void
239
     * @throws ControllerException
240
     */
241 714
    protected function findFile()
242
    {
243 714
        $path = Application::getInstance()->getPath();
244 714
        $file = "$path/modules/{$this->module}/controllers/{$this->controller}.php";
245
246 714
        if (!file_exists($file)) {
247 3
            throw new ControllerException("Controller file not found '{$this->module}/{$this->controller}'", 404);
248
        }
249
250 714
        $this->file = $file;
251 714
    }
252
253
    /**
254
     * Get controller file path
255
     *
256
     * @return string
257
     * @throws ControllerException
258
     */
259 714
    protected function getFile(): string
260
    {
261 714
        if (!$this->file) {
262 714
            $this->findFile();
263
        }
264 714
        return $this->file;
265
    }
266
267
    /**
268
     * Retrieve reflection for anonymous function
269
     *
270
     * @return void
271
     * @throws ComponentException
272
     * @throws ControllerException
273
     */
274 714
    protected function initMeta()
275
    {
276
        // cache for reflection data
277 714
        $cacheKey = "meta.{$this->module}.{$this->controller}";
278
279 714
        if (!$meta = Cache::get($cacheKey)) {
280 714
            $meta = new Meta($this->getFile());
281 714
            $meta->process();
282
283 714
            Cache::set(
284 714
                $cacheKey,
285 714
                $meta,
286 714
                Cache::TTL_NO_EXPIRY,
287 714
                ['system', 'meta']
288
            );
289
        }
290 714
        $this->meta = $meta;
291 714
    }
292
293
    /**
294
     * Get meta information
295
     *
296
     * @return Meta
297
     * @throws ControllerException
298
     * @throws ComponentException
299
     */
300 714
    public function getMeta(): Meta
301
    {
302 714
        if (!$this->meta) {
303 714
            $this->initMeta();
304
        }
305 714
        return $this->meta;
306
    }
307
308
    /**
309
     * Assign key/value pair to Data object
310
     *
311
     * @param  string $key
312
     * @param  mixed  $value
313
     *
314
     * @return void
315
     */
316
    public function assign($key, $value)
317
    {
318
        $this->getData()->set($key, $value);
319
    }
320
321
    /**
322
     * Get controller Data container
323
     *
324
     * @return Data
325
     */
326 11
    public function getData(): Data
327
    {
328 11
        if (!$this->data) {
329 11
            $this->data = new Data();
330
        }
331 11
        return $this->data;
332
    }
333
334
    /**
335
     * Load Data from cache
336
     *
337
     * @return bool
338
     * @throws ComponentException
339
     */
340 13
    private function loadData(): bool
341
    {
342 13
        $cacheTime = $this->getMeta()->getCache();
343
344 13
        if ($cacheTime && $cached = Cache::get($this->key)) {
345
            $this->data = $cached;
346
            return true;
347
        }
348 13
        return false;
349
    }
350
351
    /**
352
     * Save Data to cache
353
     *
354
     * @return bool
355
     * @throws ComponentException
356
     */
357 11
    private function saveData(): bool
358
    {
359 11
        if ($cacheTime = $this->getMeta()->getCache()) {
360
            return Cache::set(
361
                $this->key,
362
                $this->getData(),
363
                $cacheTime,
364
                ['system', 'data']
365
            );
366
        }
367 11
        return false;
368
    }
369
370
    /**
371
     * Specify data which should be serialized to JSON
372
     *
373
     * @return Data
374
     */
375
    public function jsonSerialize()
376
    {
377
        return $this->getData();
378
    }
379
380
    /**
381
     * Magic cast to string
382
     *
383
     * @return string
384
     */
385 2
    public function __toString()
386
    {
387 2
        if (!$this->template) {
388
            return '';
389
        }
390
391
        try {
392
            // $view for use in closure
393 2
            $view = new View();
394
395 2
            $path = Application::getInstance()->getPath();
396
397
            // setup additional helper path
398 2
            $view->addHelperPath($path . '/layouts/helpers');
399
400
            // setup additional partial path
401 2
            $view->addPartialPath($path . '/layouts/partial');
402
403
            // setup default path
404 2
            $view->setPath($path . '/modules/' . $this->module . '/views');
405
406
            // setup template
407 2
            $view->setTemplate($this->template);
408
409
            // setup data
410 2
            $view->setFromArray($this->getData()->toArray());
411 2
            return $view->render();
412
        } catch (\Exception $e) {
413
            // save log
414
            Logger::error($e->getMessage());
415
            return '';
416
        }
417
    }
418
}
419