blitz-php /
framework
| 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
Loading history...
|
|||||
| 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. Loading history...
|
|||||
| 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
Loading history...
|
|||||
| 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
Loading history...
|
|||||
| 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
Loading history...
|
|||||
| 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
Loading history...
|
|||||
| 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 |