1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of Blitz PHP framework. |
5
|
|
|
* |
6
|
|
|
* (c) 2022 Dimitri Sitchet Tomkeu <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view |
9
|
|
|
* the LICENSE file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace BlitzPHP\View; |
13
|
|
|
|
14
|
|
|
use BlitzPHP\Container\Services; |
15
|
|
|
use BlitzPHP\Exceptions\ConfigException; |
16
|
|
|
use BlitzPHP\Validation\ErrorBag; |
17
|
|
|
use BlitzPHP\View\Adapters\BladeAdapter; |
18
|
|
|
use BlitzPHP\View\Adapters\LatteAdapter; |
19
|
|
|
use BlitzPHP\View\Adapters\NativeAdapter; |
20
|
|
|
use BlitzPHP\View\Adapters\PlatesAdapter; |
21
|
|
|
use BlitzPHP\View\Adapters\SmartyAdapter; |
22
|
|
|
use BlitzPHP\View\Adapters\TwigAdapter; |
23
|
|
|
|
24
|
|
|
class View |
25
|
|
|
{ |
26
|
|
|
/** |
27
|
|
|
* Views configuration |
28
|
|
|
* |
29
|
|
|
* @var array |
30
|
|
|
*/ |
31
|
|
|
protected $config; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @var RendererInterface |
35
|
|
|
*/ |
36
|
|
|
private $adapter; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* Liste des adapters pris en comptes |
40
|
|
|
* |
41
|
|
|
* @var array |
42
|
|
|
*/ |
43
|
|
|
public static $validAdapters = [ |
44
|
|
|
'native' => NativeAdapter::class, |
45
|
|
|
'blade' => BladeAdapter::class, |
46
|
|
|
'latte' => LatteAdapter::class, |
47
|
|
|
'plates' => PlatesAdapter::class, |
48
|
|
|
'smarty' => SmartyAdapter::class, |
49
|
|
|
'twig' => TwigAdapter::class, |
50
|
|
|
]; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Options de la vue |
54
|
|
|
* |
55
|
|
|
* @var array |
56
|
|
|
*/ |
57
|
|
|
private $options = []; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* La vue à rendre |
61
|
|
|
* |
62
|
|
|
* @var string |
63
|
|
|
*/ |
64
|
|
|
private $view; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Données partagées à toutes les vues |
68
|
|
|
* |
69
|
|
|
* @var array |
70
|
|
|
*/ |
71
|
|
|
private static $shared = []; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Constructeur |
75
|
|
|
*/ |
76
|
|
|
public function __construct() |
77
|
|
|
{ |
78
|
|
|
$this->config = config('view'); |
|
|
|
|
79
|
|
|
|
80
|
|
|
$this->setAdapter($this->config['active_adapter'] ?? 'native'); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
public function __toString() |
84
|
|
|
{ |
85
|
|
|
return $this->get(); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* Defini les données partagées entre plusieurs vues |
90
|
|
|
*/ |
91
|
|
|
public static function share(array|string $key, mixed $value = null): void |
92
|
|
|
{ |
93
|
|
|
if (is_string($key)) { |
|
|
|
|
94
|
|
|
$key = [$key => $value]; |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
static::$shared = array_merge(static::$shared, $key); |
|
|
|
|
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Recupere et retourne le code html de la vue créée |
102
|
|
|
* |
103
|
|
|
* @param bool|string $compress |
104
|
|
|
*/ |
105
|
|
|
public function get($compress = 'auto'): string |
106
|
|
|
{ |
107
|
|
|
$output = $this->adapter->render($this->view, $this->options); |
108
|
|
|
|
109
|
|
|
return $this->compressView($output, $compress); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* Affiche la vue generee au navigateur |
114
|
|
|
*/ |
115
|
|
|
public function render(): void |
116
|
|
|
{ |
117
|
|
|
$compress = $this->config['compress_output'] ?? 'auto'; |
118
|
|
|
|
119
|
|
|
echo $this->get($compress); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Modifier les options d'affichage |
124
|
|
|
*/ |
125
|
|
|
public function setOptions(?array $options = []): self |
126
|
|
|
{ |
127
|
|
|
$this->options = (array) $options; |
128
|
|
|
|
129
|
|
|
return $this; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Définir la vue à afficher |
134
|
|
|
*/ |
135
|
|
|
public function display(string $view): self |
136
|
|
|
{ |
137
|
|
|
$this->view = $view; |
138
|
|
|
|
139
|
|
|
return $this; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* Définit plusieurs éléments de données de vue à la fois. |
144
|
|
|
*/ |
145
|
|
|
public function addData(array $data = [], ?string $context = null): self |
146
|
|
|
{ |
147
|
|
|
unset($data['errors']); |
148
|
|
|
|
149
|
|
|
$data = array_merge(static::$shared, $data); |
|
|
|
|
150
|
|
|
|
151
|
|
|
$this->adapter->addData($data, $context); |
152
|
|
|
|
153
|
|
|
if (! array_key_exists('errors', $this->getData())) { |
154
|
|
|
$this->setValidationErrors(); |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
return $this; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* Définit plusieurs éléments de données de vue à la fois. |
162
|
|
|
*/ |
163
|
|
|
public function with(array|string $key, mixed $value = null, ?string $context = null): self |
164
|
|
|
{ |
165
|
|
|
if (is_array($key)) { |
|
|
|
|
166
|
|
|
$context = $value; |
167
|
|
|
} else { |
168
|
|
|
$key = [$key => $value]; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
return $this->addData($key, $context); |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* Définit une seule donnée de vue. |
176
|
|
|
* |
177
|
|
|
* @param mixed|null $value |
178
|
|
|
*/ |
179
|
|
|
public function setVar(string $name, $value = null, ?string $context = null): self |
180
|
|
|
{ |
181
|
|
|
$this->adapter->setVar($name, $value, $context); |
182
|
|
|
|
183
|
|
|
return $this; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* Remplacer toutes les données de vue par de nouvelles données |
188
|
|
|
*/ |
189
|
|
|
public function setData(array $data, ?string $context = null): self |
190
|
|
|
{ |
191
|
|
|
unset($data['errors']); |
192
|
|
|
|
193
|
|
|
$data = array_merge(static::$shared, $data); |
|
|
|
|
194
|
|
|
|
195
|
|
|
$this->adapter->setData($data, $context); |
196
|
|
|
|
197
|
|
|
if (! array_key_exists('errors', $this->getData())) { |
198
|
|
|
$this->setValidationErrors(); |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
return $this; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* Remplacer toutes les données de vue par de nouvelles données |
206
|
|
|
*/ |
207
|
|
|
public function getData(): array |
208
|
|
|
{ |
209
|
|
|
return $this->adapter->getData(); |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* Supprime toutes les données de vue du système. |
214
|
|
|
*/ |
215
|
|
|
public function resetData(): self |
216
|
|
|
{ |
217
|
|
|
$this->adapter->resetData(); |
218
|
|
|
|
219
|
|
|
return $this; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* Definit le layout a utiliser par les vues |
224
|
|
|
*/ |
225
|
|
|
public function setLayout(string $layout): self |
226
|
|
|
{ |
227
|
|
|
$this->adapter->setLayout($layout); |
228
|
|
|
|
229
|
|
|
return $this; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* Defini l'adapteur à utiliser |
234
|
|
|
*/ |
235
|
|
|
public function setAdapter(string $adapter, array $config = []): self |
236
|
|
|
{ |
237
|
|
|
if (! array_key_exists($adapter, self::$validAdapters)) { |
238
|
|
|
$adapter = 'native'; |
239
|
|
|
} |
240
|
|
|
if (empty($this->config['adapters']) || ! is_array($this->config['adapters'])) { |
241
|
|
|
$this->config['adapters'] = []; |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
$config = array_merge($this->config['adapters'][$adapter] ?? [], $config); |
245
|
|
|
if (empty($config)) { |
246
|
|
|
throw ConfigException::viewAdapterConfigNotFound($adapter); |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
$debug = $this->config['debug'] ?? 'auto'; |
250
|
|
|
if ($debug === 'auto') { |
251
|
|
|
$debug = on_dev(); |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
$this->adapter = new self::$validAdapters[$adapter]( |
255
|
|
|
$config, |
256
|
|
|
Services::locator(), |
257
|
|
|
$debug |
258
|
|
|
); |
259
|
|
|
|
260
|
|
|
return $this; |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* Renvoie les données de performances qui ont pu être collectées |
265
|
|
|
* lors de l'exécution. Utilisé principalement dans la barre d'outils de débogage. |
266
|
|
|
*/ |
267
|
|
|
public function getPerformanceData(): array |
268
|
|
|
{ |
269
|
|
|
return $this->adapter->getPerformanceData(); |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* Compresse le code html d'une vue |
274
|
|
|
* |
275
|
|
|
* @param bool|string $compress |
276
|
|
|
*/ |
277
|
|
|
protected function compressView(string $output, $compress = 'auto'): string |
278
|
|
|
{ |
279
|
|
|
if ($compress === 'auto') { |
280
|
|
|
$compress = is_online(); |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
return true === $compress ? trim(preg_replace('/\s+/', ' ', $output)) : $output; |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
/** |
287
|
|
|
* Defini les erreurs de validation pour la vue |
288
|
|
|
*/ |
289
|
|
|
private function setValidationErrors() |
290
|
|
|
{ |
291
|
|
|
$errors = []; |
292
|
|
|
|
293
|
|
|
if (null !== $e = session()->getFlashdata('errors')) { |
294
|
|
|
if (is_array($e)) { |
|
|
|
|
295
|
|
|
$errors = array_merge($errors, $e); |
296
|
|
|
} else { |
297
|
|
|
$errors['error'] = $e; |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
session()->unmarkFlashdata('errors'); |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
if (null !== $e = session()->getFlashdata('error')) { |
304
|
|
|
if (is_array($e)) { |
|
|
|
|
305
|
|
|
$errors = array_merge($errors, $e); |
306
|
|
|
} else { |
307
|
|
|
$errors['error'] = $e; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
session()->unmarkFlashdata('error'); |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
$this->adapter->addData(['errors' => new ErrorBag($errors)]); |
314
|
|
|
} |
315
|
|
|
} |
316
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.