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\Http; |
||||
13 | |||||
14 | use ArrayAccess; |
||||
15 | use BlitzPHP\Contracts\Support\Arrayable; |
||||
16 | use BlitzPHP\Exceptions\ValidationException; |
||||
17 | use BlitzPHP\Http\Concerns\InteractsWithContentTypes; |
||||
18 | use BlitzPHP\Http\Concerns\InteractsWithFlashData; |
||||
19 | use BlitzPHP\Http\Concerns\InteractsWithInput; |
||||
20 | use BlitzPHP\Session\Store; |
||||
21 | use BlitzPHP\Utilities\Iterable\Arr; |
||||
22 | use BlitzPHP\Utilities\String\Text; |
||||
23 | use BlitzPHP\Validation\DataValidation; |
||||
24 | use BlitzPHP\Validation\Validation; |
||||
25 | use BlitzPHP\Validation\Validator; |
||||
26 | use Dimtrovich\Validation\Exceptions\ValidationException as DimtrovichValidationException; |
||||
27 | use Dimtrovich\Validation\ValidatedInput; |
||||
28 | use InvalidArgumentException; |
||||
29 | |||||
30 | class Request extends ServerRequest implements Arrayable, ArrayAccess |
||||
31 | { |
||||
32 | use InteractsWithContentTypes; |
||||
33 | use InteractsWithInput; |
||||
34 | use InteractsWithFlashData; |
||||
35 | |||||
36 | /** |
||||
37 | * Validation des donnees de la requete |
||||
38 | * |
||||
39 | * @param array|class-string<DataValidation> $rules |
||||
0 ignored issues
–
show
Documentation
Bug
introduced
by
![]() |
|||||
40 | * @param array $messages Si $rules est une chaine (representant) la classe de validation, |
||||
41 | * alors, $messages est consideré comme un tableau d'attribut à passer à la classe de validation. |
||||
42 | * Ceci peut par exemple être utilisé par spécifier l'ID à ignorer pour la règle `unique`. |
||||
43 | */ |
||||
44 | public function validate(array|string $rules, array $messages = []): ValidatedInput |
||||
45 | { |
||||
46 | try { |
||||
47 | return $this->validation($rules, $messages)->safe(); |
||||
48 | } catch (DimtrovichValidationException $e) { |
||||
49 | $th = new ValidationException($e->getMessage()); |
||||
50 | $th->setErrors($e->getErrors()); |
||||
51 | |||||
52 | throw $th; |
||||
53 | } |
||||
54 | } |
||||
55 | |||||
56 | /** |
||||
57 | * Cree un validateur avec les donnees de la requete actuelle |
||||
58 | * |
||||
59 | * @param array|class-string<DataValidation> $rules |
||||
0 ignored issues
–
show
|
|||||
60 | * @param array $messages Si $rules est une chaine (representant) la classe de validation, |
||||
61 | * alors, $messages est consideré comme un tableau d'attribut à passer à la classe de validation. |
||||
62 | * Ceci peut par exemple être utilisé par spécifier l'ID à ignorer pour la règle `unique`. |
||||
63 | */ |
||||
64 | public function validation(array|string $rules, array $messages = []): Validation |
||||
65 | { |
||||
66 | if (is_string($rules)) { |
||||
0 ignored issues
–
show
|
|||||
67 | if (! class_exists($rules) || ! is_subclass_of($rules, DataValidation::class)) { |
||||
68 | throw new InvalidArgumentException(); |
||||
69 | } |
||||
70 | |||||
71 | /** @var DataValidation $validation */ |
||||
72 | $validation = service('container')->make($rules); |
||||
73 | |||||
74 | return $validation->process($this, $messages); |
||||
75 | } |
||||
76 | |||||
77 | return Validator::make($this->all(), $rules, $messages); |
||||
0 ignored issues
–
show
|
|||||
78 | } |
||||
79 | |||||
80 | /** |
||||
81 | * Obtenez la méthode de requête. |
||||
82 | */ |
||||
83 | public function method(): string |
||||
84 | { |
||||
85 | 2 | return $this->getMethod(); |
|||
86 | } |
||||
87 | |||||
88 | /** |
||||
89 | * Obtenez l'URL racine de l'application. |
||||
90 | */ |
||||
91 | public function root(): string |
||||
92 | { |
||||
93 | 4 | return rtrim(site_url(), '/'); |
|||
94 | } |
||||
95 | |||||
96 | /** |
||||
97 | * Renvoie l'URL racine à partir de laquelle cette requête est exécutée. |
||||
98 | * |
||||
99 | * L'URL de base ne se termine jamais par un /. |
||||
100 | * |
||||
101 | * Ceci est similaire à getBasePath(), sauf qu'il inclut également le |
||||
102 | * nom de fichier du script (par exemple index.php) s'il existe. |
||||
103 | * |
||||
104 | * @return string L'URL brute (c'est-à-dire non décodée en url) |
||||
105 | */ |
||||
106 | public function getBaseUrl(): string |
||||
107 | { |
||||
108 | return trim(config()->get('app.base_url'), '/'); |
||||
109 | } |
||||
110 | |||||
111 | /** |
||||
112 | * Obtient le schéma et l'hôte HTTP. |
||||
113 | * |
||||
114 | * Si l'URL a été appelée avec une authentification de base, l'utilisateur et |
||||
115 | * le mot de passe ne sont pas ajoutés à la chaîne générée. |
||||
116 | */ |
||||
117 | public function getSchemeAndHttpHost(): string |
||||
118 | { |
||||
119 | return $this->getScheme() . '://' . $this->getHttpHost(); |
||||
120 | } |
||||
121 | |||||
122 | /** |
||||
123 | * Renvoie l'hôte HTTP demandé. |
||||
124 | * |
||||
125 | * Le nom du port sera ajouté à l'hôte s'il n'est pas standard. |
||||
126 | */ |
||||
127 | public function getHttpHost(): string |
||||
128 | { |
||||
129 | $scheme = $this->getScheme(); |
||||
130 | $port = $this->getPort(); |
||||
131 | |||||
132 | if (('http' === $scheme && 80 === $port) || ('https' === $scheme && 443 === $port)) { |
||||
133 | return $this->getHost(); |
||||
134 | } |
||||
135 | |||||
136 | return $this->getHost() . ':' . $port; |
||||
137 | } |
||||
138 | |||||
139 | /** |
||||
140 | * Obtenez l'URL (pas de chaîne de requête) pour la demande. |
||||
141 | */ |
||||
142 | public function url(): string |
||||
143 | { |
||||
144 | 2 | return rtrim(preg_replace('/\?.*/', '', (string) $this->getUri()), '/'); |
|||
145 | } |
||||
146 | |||||
147 | /** |
||||
148 | * Obtenez l'URL complète de la demande. |
||||
149 | */ |
||||
150 | public function fullUrl(): string |
||||
151 | { |
||||
152 | if (($query = $this->getEnv('QUERY_STRING')) !== null && ($query = $this->getEnv('QUERY_STRING')) !== '') { |
||||
0 ignored issues
–
show
|
|||||
153 | 2 | return $this->url() . '?' . $query; |
|||
154 | } |
||||
155 | |||||
156 | 2 | return $this->url(); |
|||
157 | } |
||||
158 | |||||
159 | /** |
||||
160 | * Obtenez l'URL complète de la demande avec les paramètres de chaîne de requête ajoutés. |
||||
161 | */ |
||||
162 | public function fullUrlWithQuery(array $query): string |
||||
163 | { |
||||
164 | $question = '?'; |
||||
165 | |||||
166 | return count($this->query()) > 0 |
||||
167 | ? $this->url() . $question . Arr::query(array_merge($this->query(), $query)) |
||||
168 | : $this->fullUrl() . $question . Arr::query($query); |
||||
169 | } |
||||
170 | |||||
171 | /** |
||||
172 | * Obtenez l'URL complète de la requête sans les paramètres de chaîne de requête donnés. |
||||
173 | */ |
||||
174 | public function fullUrlWithoutQuery(array|string $keys): string |
||||
175 | { |
||||
176 | $query = Arr::except($this->query(), $keys); |
||||
177 | |||||
178 | return $query !== [] |
||||
179 | ? $this->url() . '?' . Arr::query($query) |
||||
180 | : $this->url(); |
||||
181 | } |
||||
182 | |||||
183 | /** |
||||
184 | * Obtenez les informations de chemin actuelles pour la demande. |
||||
185 | */ |
||||
186 | public function path(): string |
||||
187 | { |
||||
188 | 2 | return $this->getPath(); |
|||
189 | } |
||||
190 | |||||
191 | /** |
||||
192 | * Obtenez les informations de chemin décodées actuelles pour la demande. |
||||
193 | */ |
||||
194 | public function decodedPath(): string |
||||
195 | { |
||||
196 | return rawurldecode(trim($this->path(), '/')); |
||||
197 | } |
||||
198 | |||||
199 | /** |
||||
200 | * Obtenir un segment de l'URI (index basé sur 1). |
||||
201 | */ |
||||
202 | public function segment(int $index, ?string $default = null): ?string |
||||
203 | { |
||||
204 | return Arr::get($this->segments(), $index - 1, $default); |
||||
205 | } |
||||
206 | |||||
207 | /** |
||||
208 | * Obtenez tous les segments pour le chemin de la demande. |
||||
209 | */ |
||||
210 | public function segments(): array |
||||
211 | { |
||||
212 | $segments = explode('/', $this->decodedPath()); |
||||
213 | |||||
214 | return array_values(array_filter($segments, static fn ($value) => $value !== '')); |
||||
215 | } |
||||
216 | |||||
217 | /** |
||||
218 | * Déterminez si l’URI de la demande actuelle correspond à un modèle. |
||||
219 | */ |
||||
220 | public function pathIs(...$patterns): bool |
||||
221 | { |
||||
222 | $path = $this->decodedPath(); |
||||
223 | |||||
224 | return collect($patterns)->contains(static fn ($pattern) => Text::is($pattern, $path)); |
||||
225 | } |
||||
226 | |||||
227 | /** |
||||
228 | * Déterminez si le nom de la route correspond à un modèle donné. |
||||
229 | * |
||||
230 | * @param mixed ...$patterns |
||||
231 | */ |
||||
232 | public function routeIs(...$patterns): bool |
||||
0 ignored issues
–
show
The parameter
$patterns is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||
233 | { |
||||
234 | return false; |
||||
235 | // return $this->route() && $this->route()->named(...$patterns); |
||||
236 | } |
||||
237 | |||||
238 | /** |
||||
239 | * Verifier si la methode de la requete actuelle est l'une envoyee en parametre |
||||
240 | */ |
||||
241 | public function isMethod(array|string $methods): bool |
||||
242 | { |
||||
243 | foreach ((array) $methods as $method) { |
||||
244 | if (strtolower($method) === strtolower($this->method())) { |
||||
245 | return true; |
||||
246 | } |
||||
247 | } |
||||
248 | |||||
249 | return false; |
||||
250 | } |
||||
251 | |||||
252 | /** |
||||
253 | * Déterminez si l'URL de requête et la chaîne de requête actuelles correspondent à un modèle. |
||||
254 | * |
||||
255 | * @param mixed ...$patterns |
||||
256 | */ |
||||
257 | public function fullUrlIs(...$patterns): bool |
||||
258 | { |
||||
259 | $url = $this->fullUrl(); |
||||
260 | |||||
261 | return collect($patterns)->contains(static fn ($pattern) => Text::is($pattern, $url)); |
||||
262 | } |
||||
263 | |||||
264 | /** |
||||
265 | * Obtenez l'hôte HTTP demandé. |
||||
266 | */ |
||||
267 | public function httpHost(): ?string |
||||
268 | { |
||||
269 | return $this->host(); |
||||
270 | } |
||||
271 | |||||
272 | /** |
||||
273 | * Déterminez si la demande est le résultat d'un appel AJAX. |
||||
274 | */ |
||||
275 | public function ajax(): bool |
||||
276 | { |
||||
277 | 2 | return $this->is('ajax'); |
|||
0 ignored issues
–
show
'ajax' of type string is incompatible with the type BlitzPHP\Http\list expected by parameter $type of BlitzPHP\Http\ServerRequest::is() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
278 | } |
||||
279 | |||||
280 | /** |
||||
281 | * Déterminez si la demande est le résultat d'un appel PJAX. |
||||
282 | */ |
||||
283 | public function pjax(): bool |
||||
284 | { |
||||
285 | return $this->header('X-PJAX') === true; |
||||
286 | } |
||||
287 | |||||
288 | /** |
||||
289 | * Déterminez si la demande est le résultat d'un appel de prélecture. |
||||
290 | */ |
||||
291 | public function prefetch(): bool |
||||
292 | { |
||||
293 | return strcasecmp($this->server('HTTP_X_MOZ', ''), 'prefetch') === 0 |
||||
0 ignored issues
–
show
It seems like
$this->server('HTTP_X_MOZ', '') can also be of type array and null ; however, parameter $string1 of strcasecmp() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
294 | || strcasecmp($this->header('Purpose', ''), 'prefetch') === 0; |
||||
295 | } |
||||
296 | |||||
297 | /** |
||||
298 | * Déterminez si la demande est via HTTPS. |
||||
299 | */ |
||||
300 | public function secure(): bool |
||||
301 | { |
||||
302 | return $this->is('ssl'); |
||||
0 ignored issues
–
show
'ssl' of type string is incompatible with the type BlitzPHP\Http\list expected by parameter $type of BlitzPHP\Http\ServerRequest::is() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
303 | } |
||||
304 | |||||
305 | /** |
||||
306 | * Obtenez l'adresse IP du client. |
||||
307 | */ |
||||
308 | public function ip(): ?string |
||||
309 | { |
||||
310 | return $this->clientIp(); |
||||
311 | } |
||||
312 | |||||
313 | /** |
||||
314 | * Obtenez l'agent utilisateur client. |
||||
315 | */ |
||||
316 | public function userAgent(): ?string |
||||
317 | { |
||||
318 | return $this->header('User-Agent'); |
||||
319 | } |
||||
320 | |||||
321 | /** |
||||
322 | * Fusionne la nouvelle entrée dans le tableau d'entrée de la requête actuelle. |
||||
323 | */ |
||||
324 | public function merge(array $input): self |
||||
325 | { |
||||
326 | $this->data = array_merge($this->data, $input); |
||||
0 ignored issues
–
show
It seems like
$this->data can also be of type null and object ; however, parameter $arrays of array_merge() does only seem to accept array , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
327 | |||||
328 | return $this; |
||||
329 | } |
||||
330 | |||||
331 | /** |
||||
332 | * Fusionne la nouvelle entrée dans l'entrée de la requête, mais uniquement lorsque cette clé est absente de la requête. |
||||
333 | */ |
||||
334 | public function mergeIfMissing(array $input): self |
||||
335 | { |
||||
336 | return $this->merge(collect($input)->filter(fn ($value, $key) => $this->missing($key))->toArray()); |
||||
337 | } |
||||
338 | |||||
339 | /** |
||||
340 | * Remplacez l'entrée de la requête en cours. |
||||
341 | */ |
||||
342 | public function replace(array $input): self |
||||
343 | { |
||||
344 | $this->data = $input; |
||||
345 | |||||
346 | return $this; |
||||
347 | } |
||||
348 | |||||
349 | /** |
||||
350 | * {@inheritDoc} |
||||
351 | */ |
||||
352 | public function hasSession(): bool |
||||
353 | { |
||||
354 | return null !== $this->session; |
||||
355 | } |
||||
356 | |||||
357 | /** |
||||
358 | * Définissez l'instance de session sur la demande. |
||||
359 | */ |
||||
360 | public function setSession(Store $session): void |
||||
361 | { |
||||
362 | $this->session = $session; |
||||
363 | } |
||||
364 | |||||
365 | /** |
||||
366 | * Obtenez toutes les entrées et tous les fichiers de la requête. |
||||
367 | */ |
||||
368 | public function toArray(): array |
||||
369 | { |
||||
370 | return $this->all(); |
||||
371 | } |
||||
372 | |||||
373 | public function getScheme(): string |
||||
374 | { |
||||
375 | 4 | return $this->getUri()->getScheme(); |
|||
376 | } |
||||
377 | |||||
378 | public function getHost(): string |
||||
379 | { |
||||
380 | return $this->getUri()->getHost(); |
||||
381 | } |
||||
382 | |||||
383 | public function getPort(): int |
||||
384 | { |
||||
385 | return $this->getUri()->getPort() ?? 80; |
||||
386 | } |
||||
387 | |||||
388 | /** |
||||
389 | * {@inheritDoc} |
||||
390 | * |
||||
391 | * @param string $offset |
||||
392 | */ |
||||
393 | public function offsetExists($offset): bool |
||||
394 | { |
||||
395 | return Arr::has($this->all(), $offset); |
||||
396 | } |
||||
397 | |||||
398 | /** |
||||
399 | * {@inheritDoc} |
||||
400 | * |
||||
401 | * @param string $offset |
||||
402 | */ |
||||
403 | public function offsetGet($offset): mixed |
||||
404 | { |
||||
405 | return $this->__get($offset); |
||||
406 | } |
||||
407 | |||||
408 | /** |
||||
409 | * {@inheritDoc} |
||||
410 | * |
||||
411 | * @param string $offset |
||||
412 | */ |
||||
413 | public function offsetSet($offset, $value): void |
||||
414 | { |
||||
415 | $this->data[$offset] = $value; |
||||
416 | } |
||||
417 | |||||
418 | /** |
||||
419 | * {@inheritDoc} |
||||
420 | * |
||||
421 | * @param string $offset |
||||
422 | */ |
||||
423 | public function offsetUnset($offset): void |
||||
424 | { |
||||
425 | unset($this->data[$offset]); |
||||
426 | } |
||||
427 | |||||
428 | /** |
||||
429 | * Vérifiez si un élément d'entrée est défini sur la demande. |
||||
430 | */ |
||||
431 | public function __isset(string $key): bool |
||||
432 | { |
||||
433 | return null !== $this->__get($key); |
||||
434 | } |
||||
435 | |||||
436 | /** |
||||
437 | * Obtenez un élément d'entrée à partir de la requête. |
||||
438 | */ |
||||
439 | public function __get(string $key): mixed |
||||
440 | { |
||||
441 | return Arr::get($this->all(), $key, null); |
||||
442 | } |
||||
443 | } |
||||
444 |