Passed
Pull Request — master (#182)
by Arman
03:14
created

QtView::renderPartial()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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