Passed
Pull Request — master (#43)
by Arman
03:54
created

QtView::renderDebugBar()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 5
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
/**
4
 * Quantum PHP Framework
5
 *
6
 * An open source software development framework for PHP
7
 *
8
 * @package Quantum
9
 * @author Arman Ag. <[email protected]>
10
 * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
11
 * @link http://quantum.softberg.org/
12
 * @since 2.5.0
13
 */
14
15
namespace Quantum\Mvc;
16
17
use Quantum\Libraries\Storage\FileSystem;
18
use Quantum\Exceptions\ViewException;
19
use Quantum\Factory\ViewFactory;
20
use Quantum\Hooks\HookManager;
21
use Quantum\Di\Di;
22
use Error;
23
24
/**
25
 * Class QtView
26
 * @package Quantum\Mvc
27
 */
28
class QtView
29
{
30
31
    /**
32
     * Layout file
33
     * @var string
34
     */
35
    private $layout = null;
36
37
    /**
38
     * Rendered view
39
     * @var string
40
     */
41
    private $view = null;
42
43
    /**
44
     * View data
45
     * @var array
46
     */
47
    private $data = [];
48
49
    /**
50
     * QtView constructor.
51
     * @throws \Quantum\Exceptions\ViewException
52
     */
53
    public function __construct()
54
    {
55
        if (get_caller_class() != ViewFactory::class) {
56
            throw ViewException::directInstantiation(ViewFactory::class);
57
        }
58
    }
59
60
    /**
61
     * Sets a layout
62
     * @param string $layout
63
     */
64
    public function setLayout(string $layout)
65
    {
66
        $this->layout = $layout;
67
    }
68
69
    /**
70
     * Gets the layout
71
     * @return string|null
72
     */
73
    public function getLayout(): ?string
74
    {
75
        return $this->layout;
76
    }
77
78
    /**
79
     * Sets view parameter
80
     * @param string $key
81
     * @param mixed $value
82
     */
83
    public function setParam(string $key, $value)
84
    {
85
        $this->data[$key] = $value;
86
    }
87
88
    /**
89
     * Gets the view parameter
90
     * @param string $key
91
     * @return mixed|null
92
     */
93
    public function getParam(string $key)
94
    {
95
        return $this->data[$key] ?? null;
96
    }
97
98
    /**
99
     * Sets multiple view parameters
100
     * @param array $params
101
     */
102
    public function setParams(array $params)
103
    {
104
        foreach ($params as $key => $value) {
105
            $this->setParam($key, $value);
106
        }
107
    }
108
109
    /**
110
     * Gets all view parameters
111
     * @return array
112
     */
113
    public function getParams(): array
114
    {
115
        return $this->data;
116
    }
117
118
    /**
119
     * @param string $view
120
     * @param array $params
121
     * @return string|null
122
     * @throws \Quantum\Exceptions\DiException
123
     * @throws \Quantum\Exceptions\HookException
124
     * @throws \Quantum\Exceptions\ViewException
125
     * @throws \ReflectionException
126
     */
127
    public function render(string $view, array $params = []): ?string
128
    {
129
        if (!$this->layout) {
130
            throw ViewException::noLayoutSet();
131
        }
132
133
        if (!empty($params)) {
134
            $this->data = array_merge($this->data, $params);
135
        }
136
137
        $this->view = $this->renderFile($view);
138
139
        if (filter_var(config()->get('debug'), FILTER_VALIDATE_BOOLEAN)) {
140
            HookManager::call('updateDebuggerStore', ['view' => $view]);
141
        }
142
143
        return $this->renderFile($this->layout);
144
    }
145
146
    /**
147
     * Renders partial view
148
     * @param string $view
149
     * @param array $params
150
     * @return string|null
151
     * @throws \Quantum\Exceptions\DiException
152
     * @throws \Quantum\Exceptions\HookException
153
     * @throws \Quantum\Exceptions\ViewException
154
     * @throws \ReflectionException
155
     */
156
    public function renderPartial(string $view, array $params = []): ?string
157
    {
158
        if (!empty($params)) {
159
            $this->data = array_merge($this->data, $params);
160
        }
161
162
        return $this->renderFile($view);
163
    }
164
165
    /**
166
     * Gets the rendered view
167
     * @return string
168
     */
169
    public function getView(): string
170
    {
171
        return $this->view;
172
    }
173
174
    /**
175
     * Finds a given file
176
     * @param string $file
177
     * @return string
178
     * @throws \Quantum\Exceptions\DiException
179
     * @throws \Quantum\Exceptions\ViewException
180
     * @throws \ReflectionException
181
     */
182
    private function findFile(string $file): string
183
    {
184
        $fs = Di::get(FileSystem::class);
185
186
        $filePath = modules_dir() . DS . current_module() . DS . 'Views' . DS . $file . '.php';
187
188
        if (!$fs->exists($filePath)) {
189
            $filePath = base_dir() . DS . 'base' . DS . 'views' . DS . $file . '.php';
190
            if (!$fs->exists($filePath)) {
191
                throw ViewException::fileNotFound($file);
192
            }
193
        }
194
195
        return $filePath;
196
    }
197
198
    /**
199
     * Renders the view
200
     * @param string $view
201
     * @return mixed|string
202
     * @throws \Quantum\Exceptions\DiException
203
     * @throws \Quantum\Exceptions\HookException
204
     * @throws \Quantum\Exceptions\ViewException
205
     * @throws \ReflectionException
206
     */
207
    private function renderFile(string $view)
208
    {
209
        $params = $this->xssFilter($this->data);
210
211
        $templateEngine = config()->get('template_engine');
212
213
        if ($templateEngine) {
214
            $engineName = key($templateEngine);
215
            $engineConfigs = $templateEngine[$engineName];
216
217
            return HookManager::call('templateRenderer', [
218
                'configs' => $engineConfigs,
219
                'view' => $view,
220
                'params' => $params
221
            ]);
222
        } else {
223
            return $this->defaultRenderer($view, $params);
224
        }
225
    }
226
227
    /**
228
     * Default Renderer
229
     * @param string $view
230
     * @param array $params
231
     * @return string|null
232
     * @throws \Quantum\Exceptions\DiException
233
     * @throws \Quantum\Exceptions\ViewException
234
     * @throws \ReflectionException
235
     */
236
    private function defaultRenderer(string $view, array $params = []): ?string
237
    {
238
        try {
239
            ob_start();
240
            ob_implicit_flush(0);
241
242
            if (!empty($params)) {
243
                extract($params, EXTR_OVERWRITE);
244
            }
245
246
            require $this->findFile($view);
247
248
            return ob_get_clean();
249
        } catch (Error $e) {
250
            ob_clean();
251
            exit($e->getMessage());
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
252
        }
253
    }
254
255
    /**
256
     * XSS Filter
257
     * @param mixed $data
258
     * @return mixed
259
     */
260
    private function xssFilter($data)
261
    {
262
        if (is_string($data)) {
263
            $this->cleaner($data);
264
            $data = [$data];
265
        } else {
266
            array_walk_recursive($data, [$this, 'cleaner']);
267
        }
268
269
        return $data;
270
    }
271
272
    /**
273
     * Cleaner
274
     * @param mixed $value
275
     */
276
    private function cleaner(&$value)
277
    {
278
        if (is_object($value)) {
279
            $this->xssFilter($value);
280
        } else {
281
            $value = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
282
        }
283
    }
284
285
}
286