Issues (536)

src/Http/Request.php (10 issues)

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
The doc comment array|class-string<DataValidation> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array|class-string<DataValidation>.
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
Documentation Bug introduced by
The doc comment array|class-string<DataValidation> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array|class-string<DataValidation>.
Loading history...
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
The condition is_string($rules) is always false.
Loading history...
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
Bug Best Practice introduced by
The expression return BlitzPHP\Validati...l(), $rules, $messages) returns the type Dimtrovich\Validation\Validation which includes types incompatible with the type-hinted return BlitzPHP\Validation\Validation.
Loading history...
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
The assignment to $query is dead and can be removed.
Loading history...
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 ignore-unused  annotation

232
    public function routeIs(/** @scrutinizer ignore-unused */ ...$patterns): bool

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 ignore-type  annotation

277
        return $this->is(/** @scrutinizer ignore-type */ 'ajax');
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 ignore-type  annotation

293
        return strcasecmp(/** @scrutinizer ignore-type */ $this->server('HTTP_X_MOZ', ''), 'prefetch') === 0
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 ignore-type  annotation

302
        return $this->is(/** @scrutinizer ignore-type */ 'ssl');
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 ignore-type  annotation

326
        $this->data = array_merge(/** @scrutinizer ignore-type */ $this->data, $input);
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