QtView::sanitizeHtml()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
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 3.0.0
13
 */
14
15
namespace Quantum\View;
16
17
use Quantum\Libraries\Database\Exceptions\DatabaseException;
18
use Quantum\Libraries\Session\Exceptions\SessionException;
19
use Quantum\Libraries\Asset\Exceptions\AssetException;
20
use Quantum\Libraries\ResourceCache\ViewCache;
21
use Quantum\Config\Exceptions\ConfigException;
22
use Quantum\View\Exceptions\ViewException;
23
use Quantum\App\Exceptions\BaseException;
24
use Quantum\Libraries\Asset\AssetManager;
25
use Quantum\Di\Exceptions\DiException;
26
use Quantum\Renderer\Renderer;
27
use Quantum\Debugger\Debugger;
28
use ReflectionException;
29
use Psr\Log\LogLevel;
30
31
/**
32
 * Class QtView
33
 * @package Quantum\View
34
 */
35
class QtView
36
{
37
    /**
38
     * @var Renderer
39
     */
40
    private $renderer;
41
42
    /**
43
     * @var AssetManager
44
     */
45
    private $assetManager;
46
47
    /**
48
     * @var Debugger
49
     */
50
    private $debugger;
51
52
    /**
53
     * @var ViewCache
54
     */
55
    private $viewCache;
56
57
    /**
58
     * Layout file
59
     * @var string
60
     */
61
    private $layoutFile = null;
62
63
    /**
64
     * Rendered view
65
     * @var string
66
     */
67
    private $viewContent;
68
69
    /**
70
     * Assets to be included
71
     * @var array
72
     */
73
    private $assets = [];
74
75
    /**
76
     * View params
77
     * @var array
78
     */
79
    private $params = [];
80
81
    /**
82
     * @param Renderer $renderer
83
     * @param AssetManager $assetManager
84
     * @param Debugger $debugger
85
     * @param ViewCache $viewCache
86
     */
87
    public function __construct(
88
        Renderer $renderer,
89
        AssetManager $assetManager,
90
        Debugger $debugger,
91
        ViewCache $viewCache
92
    ) {
93
        $this->renderer = $renderer;
94
        $this->assetManager = $assetManager;
95
        $this->debugger = $debugger;
96
        $this->viewCache = $viewCache;
97
    }
98
99
    /**
100
     * Sets a layout
101
     * @param string|null $layoutFile
102
     * @param array $assets
103
     */
104
    public function setLayout(?string $layoutFile, array $assets = [])
105
    {
106
        $this->layoutFile = $layoutFile;
107
108
        if ($assets !== []) {
109
            $this->assets = array_merge($this->assets, $assets);
110
        }
111
    }
112
113
    /**
114
     * Gets the layout
115
     * @return string|null
116
     */
117
    public function getLayout(): ?string
118
    {
119
        return $this->layoutFile;
120
    }
121
122
    /**
123
     * Sets view parameter
124
     * @param string $key
125
     * @param mixed $value
126
     */
127
    public function setParam(string $key, $value)
128
    {
129
        $this->params[$key] = $value;
130
    }
131
132
    /**
133
     * @param string $key
134
     * @param $value
135
     * @return void
136
     */
137
    public function setRawParam(string $key, $value)
138
    {
139
        $this->params[$key] = new RawParam($value);
140
    }
141
142
    /**
143
     * Gets the view parameter
144
     * @param string $key
145
     * @return mixed|null
146
     */
147
    public function getParam(string $key)
148
    {
149
        $param = $this->params[$key] ?? null;
150
151
        if ($param instanceof RawParam) {
152
            return $param->getValue();
153
        }
154
155
        return $param;
156
    }
157
158
    /**
159
     * Sets multiple view parameters
160
     * @param array $params
161
     */
162
    public function setParams(array $params)
163
    {
164
        foreach ($params as $key => $value) {
165
            $this->setParam($key, $value);
166
        }
167
    }
168
169
    /**
170
     * Gets all view parameters
171
     * @return array
172
     */
173
    public function getParams(): array
174
    {
175
        $params = [];
176
177
        foreach ($this->params as $key => $param) {
178
            $params[$key] = ($param instanceof RawParam) ? $param->getValue() : $param;
179
        }
180
181
        return $params;
182
    }
183
184
    /**
185
     * Flushes the view params
186
     */
187
    public function flushParams()
188
    {
189
        $this->params = [];
190
    }
191
192
    /**
193
     * Renders the view.
194
     * @param string $viewFile
195
     * @param array $params
196
     * @return string|null
197
     * @throws AssetException
198
     * @throws BaseException
199
     * @throws ConfigException
200
     * @throws DatabaseException
201
     * @throws DiException
202
     * @throws ReflectionException
203
     * @throws SessionException
204
     * @throws ViewException
205
     */
206
    public function render(string $viewFile, array $params = []): ?string
207
    {
208
        if (!$this->layoutFile) {
209
            throw ViewException::noLayoutSet();
210
        }
211
212
        if ($params !== []) {
213
            $this->params = array_merge($this->params, $params);
214
        }
215
216
        $this->viewContent = $this->renderFile($viewFile);
217
218
        if ($this->assets !== []) {
219
            $this->assetManager->register($this->assets);
220
        }
221
222
        if ($this->debugger->isEnabled()) {
223
            $this->updateDebugger($viewFile);
224
        }
225
226
        $layoutContent = $this->renderFile($this->layoutFile);
227
228
        if ($this->viewCache->isEnabled()) {
229
            $layoutContent = $this->viewCache
230
                ->set(route_uri(), $layoutContent)
231
                ->get(route_uri());
232
        }
233
234
        return $layoutContent;
235
    }
236
237
    /**
238
     * Renders partial view.
239
     * @param string $viewFile
240
     * @param array $params
241
     * @return string
242
     */
243
    public function renderPartial(string $viewFile, array $params = []): string
244
    {
245
        if ($params !== []) {
246
            $this->params = array_merge($this->params, $params);
247
        }
248
249
        return $this->renderFile($viewFile);
250
    }
251
252
    /**
253
     * Gets the rendered view.
254
     * @return string|null
255
     * @throws ViewException
256
     */
257
    public function getView(): ?string
258
    {
259
        if ($this->viewContent === null) {
260
            throw ViewException::viewNotRendered();
261
        }
262
263
        return $this->viewContent;
264
    }
265
266
    /**
267
     * Renders the view
268
     * @param string $viewFile
269
     * @return string
270
     */
271
    private function renderFile(string $viewFile): string
272
    {
273
        $filteredParams = $this->xssFilter($this->params);
274
275
        return $this->renderer->render($viewFile, $filteredParams);
276
    }
277
278
    /**
279
     * XSS Filter
280
     * @param mixed $params
281
     * @return mixed
282
     */
283
    private function xssFilter($params)
284
    {
285
        if ($params instanceof RawParam) {
286
            return $params->getValue();
287
        }
288
289
        if (is_string($params)) {
290
            return $this->sanitizeHtml($params);
291
        }
292
293
        if (is_array($params)) {
294
            foreach ($params as $key => $value) {
295
                $params[$key] = $this->xssFilter($value);
296
            }
297
298
            return $params;
299
        }
300
301
        if (is_object($params)) {
302
            foreach (get_object_vars($params) as $property => $value) {
303
                $params->$property = $this->xssFilter($value);
304
            }
305
306
            return $params;
307
        }
308
309
        return $params;
310
    }
311
312
    /**
313
     * @param string $value
314
     * @return string
315
     */
316
    private function sanitizeHtml(string $value): string
317
    {
318
        return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
319
    }
320
321
    /**
322
     * @param string $viewFile
323
     * @return void
324
     */
325
    private function updateDebugger(string $viewFile)
326
    {
327
        $routesCell = $this->debugger->getStoreCell(Debugger::ROUTES);
328
        $currentData = current($routesCell)[LogLevel::INFO] ?? [];
329
        $additionalData = ['View' => current_module() . '/Views/' . $viewFile];
330
        $mergedData = array_merge($currentData, $additionalData);
331
        $this->debugger->clearStoreCell(Debugger::ROUTES);
332
        $this->debugger->addToStoreCell(Debugger::ROUTES, LogLevel::INFO, $mergedData);
333
    }
334
}
335