Passed
Push — main ( d79e5f...42d171 )
by Dimitri
12:00 queued 06:40
created

ServerRequest   F

Complexity

Total Complexity 212

Size/Duplication

Total Lines 1799
Duplicated Lines 0 %

Test Coverage

Coverage 52.17%

Importance

Changes 7
Bugs 1 Features 0
Metric Value
eloc 476
dl 0
loc 1799
ccs 156
cts 299
cp 0.5217
rs 2
c 7
b 1
f 0
wmc 212

79 Methods

Rating   Name   Duplication   Size   Complexity  
A getUri() 0 3 1
A getOldInput() 0 3 1
A addDetector() 0 14 3
A _acceptHeaderDetector() 0 11 3
A processUrlOption() 0 16 3
A _environmentDetector() 0 17 5
A getAcceptableContentTypes() 0 10 2
A withQueryParams() 0 6 1
A allowMethod() 0 12 3
A accepts() 0 15 4
A is() 0 21 6
A withBody() 0 6 1
A withParsedBody() 0 6 1
A getCookieCollection() 0 3 1
A __call() 0 11 2
A scheme() 0 7 4
A clearDetectorCache() 0 3 1
A withHeader() 0 7 1
A withProtocolVersion() 0 9 2
A host() 0 7 3
A withoutData() 0 9 2
A getBody() 0 3 1
A withData() 0 9 2
A getQueryParams() 0 3 1
A getHeader() 0 12 3
A contentType() 0 3 2
A withMethod() 0 13 2
A setTrustedProxies() 0 5 1
A negotiate() 0 12 2
A domain() 0 10 2
A hasHeader() 0 11 3
A getRequestTarget() 0 16 4
B _is() 0 19 9
A validateUploadedFiles() 0 11 4
A getProtocolVersion() 0 15 3
A getHeaderLine() 0 5 1
B clientIp() 0 28 9
A acceptLanguage() 0 19 5
A withUploadedFiles() 0 7 1
A getUploadedFile() 0 18 5
A withAddedHeader() 0 12 2
A getUploadedFiles() 0 3 1
A withCookieParams() 0 6 1
A getPath() 0 9 2
A withUri() 0 18 5
A withParam() 0 6 1
C _setConfig() 0 57 12
A _headerDetector() 0 14 4
A normalizeHeaderName() 0 8 2
A __construct() 0 17 1
A getAttributes() 0 10 1
A getEnv() 0 8 3
A getServerParams() 0 3 1
A subdomains() 0 9 2
A withCookieCollection() 0 11 2
A _paramDetector() 0 13 5
A session() 0 3 1
A getMethod() 0 3 1
A withoutAttribute() 0 11 2
B referer() 0 26 10
A withEnv() 0 7 1
A getParsedBody() 0 3 1
A withAttribute() 0 10 2
A getParam() 0 3 1
A getData() 0 10 3
A withLocale() 0 12 2
A getHeaders() 0 20 5
A isAll() 0 9 3
A withRequestTarget() 0 6 1
A getCookie() 0 3 1
A port() 0 7 3
A getCookieParams() 0 3 1
A getQuery() 0 7 2
A getAttribute() 0 14 4
A getTrustedProxies() 0 3 1
A withoutHeader() 0 7 1
A parseAccept() 0 3 1
B _parseAcceptWithQualifier() 0 32 8
A getLocale() 0 7 2

How to fix   Complexity   

Complex Class

Complex classes like ServerRequest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ServerRequest, and based on these observations, apply Extract Interface, too.

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 BadMethodCallException;
15
use BlitzPHP\Container\Services;
16
use BlitzPHP\Exceptions\FrameworkException;
17
use BlitzPHP\Exceptions\HttpException;
18
use BlitzPHP\Filesystem\Files\UploadedFile;
19
use BlitzPHP\Session\Cookie\CookieCollection;
20
use BlitzPHP\Session\Store;
21
use BlitzPHP\Utilities\Iterable\Arr;
22
use Closure;
23
use GuzzleHttp\Psr7\ServerRequest as Psr7ServerRequest;
24
use GuzzleHttp\Psr7\Stream;
25
use GuzzleHttp\Psr7\Utils;
26
use InvalidArgumentException;
27
use Psr\Http\Message\ServerRequestInterface;
28
use Psr\Http\Message\StreamInterface;
29
use Psr\Http\Message\UploadedFileInterface;
30
use Psr\Http\Message\UriInterface;
31
32
/**
33
 * Une classe qui aide à envelopper les informations de la requête et les détails d'une seule requête.
34
 * Fournit des méthodes couramment utilisées pour effectuer une introspection sur les en-têtes et le corps de la requête.
35
 */
36
class ServerRequest implements ServerRequestInterface
37
{
38
    /**
39
     * Tableau de paramètres analysés à partir de l'URL.
40
     */
41
    protected array $params = [
42
        'plugin'     => null,
43
        'controller' => null,
44
        'action'     => null,
45
        '_ext'       => null,
46
        'pass'       => [],
47
    ];
48
49
    /**
50
     * Tableau de données POST. Contiendra des données de formulaire ainsi que des fichiers téléchargés.
51
     * Dans les requêtes PUT/PATCH/DELETE, cette propriété contiendra les données encodées du formulaire.
52
     */
53
    protected array|object|null $data = [];
54
55
    /**
56
     * Tableau d'arguments de chaîne de requête
57
     */
58
    protected array $query = [];
59
60
    /**
61
     * Tableau de données de cookie.
62
     *
63
     * @var array<string, mixed>
64
     */
65
    protected array $cookies = [];
66
67
    /**
68
     * Tableau de données d'environnement.
69
     *
70
     * @var array<string, mixed>
71
     */
72
    protected array $_environment = [];
73
74
    /**
75
     * Chemin de l'URL de base.
76
     */
77
    protected string $base;
78
79
    /**
80
     * segment de chemin webroot pour la demande.
81
     */
82
    protected string $webroot = '/';
83
84
    /**
85
     * S'il faut faire confiance aux en-têtes HTTP_X définis par la plupart des équilibreurs de charge.
86
     * Défini sur vrai uniquement si votre application s'exécute derrière des équilibreurs de charge/proxies que vous contrôlez.
87
     */
88
    public bool $trustProxy = false;
89
90
    /**
91
     * Liste des proxys de confiance
92
     *
93
     * @var list<string>
0 ignored issues
show
Bug introduced by
The type BlitzPHP\Http\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
94
     */
95
    protected array $trustedProxies = [];
96
97
    /**
98
     * Les détecteurs intégrés utilisés avec `is()` peuvent être modifiés avec `addDetector()`.
99
     *
100
     * Il existe plusieurs façons de spécifier un détecteur, voir `addDetector()` pour
101
     * les différents formats et façons de définir des détecteurs.
102
     *
103
     * @var array<string, array|Closure>
104
     */
105
    protected static array $_detectors = [
106
        'get'     => ['env' => 'REQUEST_METHOD', 'value' => 'GET'],
107
        'post'    => ['env' => 'REQUEST_METHOD', 'value' => 'POST'],
108
        'put'     => ['env' => 'REQUEST_METHOD', 'value' => 'PUT'],
109
        'patch'   => ['env' => 'REQUEST_METHOD', 'value' => 'PATCH'],
110
        'delete'  => ['env' => 'REQUEST_METHOD', 'value' => 'DELETE'],
111
        'head'    => ['env' => 'REQUEST_METHOD', 'value' => 'HEAD'],
112
        'options' => ['env' => 'REQUEST_METHOD', 'value' => 'OPTIONS'],
113
        'https'   => ['env' => 'HTTPS', 'options' => [1, 'on']],
114
        'ssl'     => ['env' => 'HTTPS', 'options' => [1, 'on']],
115
        'ajax'    => ['env' => 'HTTP_X_REQUESTED_WITH', 'value' => 'XMLHttpRequest'],
116
        'json'    => ['accept' => ['application/json'], 'param' => '_ext', 'value' => 'json'],
117
        'xml'     => ['accept' => ['application/xml', 'text/xml'], 'param' => '_ext', 'value' => 'xml'],
118
    ];
119
120
    /**
121
     * Cache d'instance pour les résultats des appels is(something)
122
     *
123
     * @var array<string, bool>
124
     */
125
    protected array $_detectorCache = [];
126
127
    /**
128
     * Flux du corps de la requête. Contient php://input sauf si l'option constructeur `input` est utilisée.
129
     */
130
    protected StreamInterface $stream;
131
132
    /**
133
     * instance Uri
134
     */
135
    protected UriInterface $uri;
136
137
    /**
138
     * Instance d'un objet Session relative à cette requête
139
     */
140
    protected Store $session;
141
142
    /**
143
     * Stockez les attributs supplémentaires attachés à la requête.
144
     *
145
     * @var array<string, mixed>
146
     */
147
    protected array $attributes = [];
148
149
    /**
150
     * Une liste de propriétés émulées par les méthodes d'attribut PSR7.
151
     *
152
     * @var list<string>
153
     */
154
    protected array $emulatedAttributes = ['session', 'flash', 'webroot', 'base', 'params', 'here'];
155
156
    /**
157
     * Tableau de fichiers.
158
     */
159
    protected array $uploadedFiles = [];
160
161
    /**
162
     * La version du protocole HTTP utilisée.
163
     */
164
    protected ?string $protocol = null;
165
166
    /**
167
     * La cible de la requête si elle est remplacée
168
     */
169
    protected ?string $requestTarget = null;
170
171
    /**
172
     * Negotiator
173
     */
174
    protected ?Negotiator $negotiator = null;
175
176
    /**
177
     * Créer un nouvel objet de requête.
178
     *
179
     * Vous pouvez fournir les données sous forme de tableau ou de chaîne. Si tu utilises
180
     * une chaîne, vous ne pouvez fournir que l'URL de la demande. L'utilisation d'un tableau
181
     * vous permettent de fournir les clés suivantes :
182
     *
183
     * - `post` Données POST ou données de chaîne sans requête
184
     * - `query` Données supplémentaires de la chaîne de requête.
185
     * - `files` Fichiers téléchargés dans une structure normalisée, avec chaque feuille une instance de UploadedFileInterface.
186
     * - `cookies` Cookies pour cette demande.
187
     * - `environment` $_SERVER et $_ENV données.
188
     * - `url` L'URL sans le chemin de base de la requête.
189
     * - `uri` L'objet PSR7 UriInterface. Si nul, un sera créé à partir de `url` ou `environment`.
190
     * - `base` L'URL de base de la requête.
191
     * - `webroot` Le répertoire webroot pour la requête.
192
     * - `input` Les données qui proviendraient de php://input ceci est utile pour simuler
193
     * requêtes avec mise, patch ou suppression de données.
194
     * - `session` Une instance d'un objet Session
195
     *
196
     * @param array<string, mixed> $config Un tableau de données de requête avec lequel créer une requête.
197
     */
198
    public function __construct(array $config = [])
199
    {
200
        $config += [
201
            'params'      => $this->params,
202
            'query'       => [],
203
            'post'        => [],
204
            'files'       => [],
205
            'cookies'     => [],
206
            'environment' => [],
207
            'url'         => '',
208
            'uri'         => null,
209
            'base'        => '',
210
            'webroot'     => '',
211
            'input'       => null,
212 29
        ];
213
214 29
        $this->_setConfig($config);
215
    }
216
217
    /**
218
     * Traitez les données de configuration/paramètres dans les propriétés.
219
     *
220
     * @param array<string, mixed> $config
221
     */
222
    protected function _setConfig(array $config): void
223
    {
224
        if (empty($config['session'])) {
225 19
            $config['session'] = Services::session(false);
226
        }
227
228
        if (empty($config['environment']['REQUEST_METHOD'])) {
229 27
            $config['environment']['REQUEST_METHOD'] = $_SERVER['REQUEST_METHOD'] ?? 'GET';
230
        }
231
232 29
        $this->cookies = $config['cookies'];
233
234
        if (isset($config['uri'])) {
235
            if (! $config['uri'] instanceof UriInterface) {
236 2
                throw new FrameworkException('The `uri` key must be an instance of ' . UriInterface::class);
237
            }
238 2
            $uri = $config['uri'];
239
        } elseif ($config['url'] !== '') {
240 2
            $config = $this->processUrlOption($config);
241 2
            $uri    = new Uri(implode('?', [$config['url'], $config['environment']['QUERY_STRING'] ?? '']));
242
        } elseif (isset($config['environment']['REQUEST_URI'])) {
243 4
            $uri = new Uri($config['environment']['REQUEST_URI']);
244
        } else {
245 23
            $uri = Psr7ServerRequest::getUriFromGlobals();
246
        }
247
248
        if (in_array($uri->getHost(), ['localhost', '127.0.0.1'], true)) {
249 23
            $uri = $uri->withHost(parse_url(config('app.base_url'), PHP_URL_HOST));
0 ignored issues
show
Bug introduced by
It seems like config('app.base_url') can also be of type BlitzPHP\Config\Config; however, parameter $url of parse_url() 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

249
            $uri = $uri->withHost(parse_url(/** @scrutinizer ignore-type */ config('app.base_url'), PHP_URL_HOST));
Loading history...
250
        }
251
252 29
        $this->_environment = $config['environment'];
253
254 29
        $this->uri     = $uri;
255 29
        $this->base    = $config['base'];
256 29
        $this->webroot = $config['webroot'];
257
258
        if (isset($config['input'])) {
259 2
            $stream = new Stream(Utils::tryFopen('php://memory', 'rw'));
260 2
            $stream->write($config['input']);
261 2
            $stream->rewind();
262
        } else {
263 29
            $stream = new Stream(Utils::tryFopen('php://input', 'r'));
264
        }
265 29
        $this->stream = $stream;
266
267 29
        $post = $config['post'];
268
        if (! (is_array($post) || is_object($post) || $post === null)) {
269
            throw new InvalidArgumentException(sprintf(
270
                'La clé `post` doit être un tableau, un objet ou null. On a obtenu `%s` à la place.',
271
                get_debug_type($post)
272 29
            ));
273
        }
274 29
        $this->data          = $post;
275 29
        $this->uploadedFiles = $config['files'];
276 29
        $this->query         = $config['query'];
277 29
        $this->params        = $config['params'];
278 29
        $this->session       = $config['session'];
279
    }
280
281
    /**
282
     * Définissez les variables d'environnement en fonction de l'option `url` pour faciliter la génération d'instance UriInterface.
283
     *
284
     * L'option `query` est également mise à jour en fonction de la chaîne de requête de l'URL.
285
     */
286
    protected function processUrlOption(array $config): array
287
    {
288
        if ($config['url'][0] !== '/') {
289 2
            $config['url'] = '/' . $config['url'];
0 ignored issues
show
Bug introduced by
Are you sure $config['url'] of type array can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

289
            $config['url'] = '/' . /** @scrutinizer ignore-type */ $config['url'];
Loading history...
290
        }
291
292
        if (str_contains($config['url'], '?')) {
293 2
            [$config['url'], $config['environment']['QUERY_STRING']] = explode('?', $config['url']);
294
295 2
            parse_str($config['environment']['QUERY_STRING'], $queryArgs);
296 2
            $config['query'] += $queryArgs;
297
        }
298
299 2
        $config['environment']['REQUEST_URI'] = $config['url'];
300
301 2
        return $config;
302
    }
303
304
    /**
305
     * Obtenez le type de contenu utilisé dans cette requête.
306
     */
307
    public function contentType(): ?string
308
    {
309 2
        return $this->getEnv('CONTENT_TYPE') ?: $this->getEnv('HTTP_CONTENT_TYPE');
310
    }
311
312
    /**
313
     * Renvoie l'instance de l'objet Session pour cette requête
314
     */
315
    public function session(): Store
316
    {
317 10
        return $this->session;
318
    }
319
320
    /**
321
     * Obtenez l'adresse IP que le client utilise ou dit qu'il utilise.
322
     */
323
    public function clientIp(): string
324
    {
325
        if ($this->trustProxy && $this->getEnv('HTTP_X_FORWARDED_FOR')) {
326
            $addresses = array_map('trim', explode(',', $this->getEnv('HTTP_X_FORWARDED_FOR')));
0 ignored issues
show
Bug introduced by
It seems like $this->getEnv('HTTP_X_FORWARDED_FOR') can also be of type null; however, parameter $string of explode() 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

326
            $addresses = array_map('trim', explode(',', /** @scrutinizer ignore-type */ $this->getEnv('HTTP_X_FORWARDED_FOR')));
Loading history...
327
            $trusted   = $this->trustedProxies !== [];
328
            $n         = count($addresses);
329
330
            if ($trusted) {
331
                $trusted = array_diff($addresses, $this->trustedProxies);
332
                $trusted = (count($trusted) === 1);
333
            }
334
335
            if ($trusted) {
336
                return $addresses[0];
337
            }
338
339
            return $addresses[$n - 1];
340
        }
341
342
        if ($this->trustProxy && $this->getEnv('HTTP_X_REAL_IP')) {
343
            $ipaddr = $this->getEnv('HTTP_X_REAL_IP');
344
        } elseif ($this->trustProxy && $this->getEnv('HTTP_CLIENT_IP')) {
345
            $ipaddr = $this->getEnv('HTTP_CLIENT_IP');
346
        } else {
347
            $ipaddr = $this->getEnv('REMOTE_ADDR');
348
        }
349
350
        return trim((string) $ipaddr);
351
    }
352
353
    /**
354
     * Enregistrer des proxys de confiance
355
     *
356
     * @param list<string> $proxies ips liste des proxys de confiance
357
     */
358
    public function setTrustedProxies(array $proxies): void
359
    {
360 2
        $this->trustedProxies = $proxies;
0 ignored issues
show
Documentation Bug introduced by
It seems like $proxies of type array is incompatible with the declared type BlitzPHP\Http\list of property $trustedProxies.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
361 2
        $this->trustProxy     = true;
362 2
        $this->uri            = $this->uri->withScheme($this->scheme());
0 ignored issues
show
Bug introduced by
It seems like $this->scheme() can also be of type null; however, parameter $scheme of Psr\Http\Message\UriInterface::withScheme() 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

362
        $this->uri            = $this->uri->withScheme(/** @scrutinizer ignore-type */ $this->scheme());
Loading history...
363
    }
364
365
    /**
366
     * Obtenez les proxys de confiance
367
     */
368
    public function getTrustedProxies(): array
369
    {
370
        return $this->trustedProxies;
371
    }
372
373
    /**
374
     * Renvoie le référent qui a référé cette requête.
375
     *
376
     * @param bool $local Tentative de renvoi d'une adresse locale.
377
     *                    Les adresses locales ne contiennent pas de noms d'hôtes..
378
     */
379
    public function referer(bool $local = true): ?string
380
    {
381
        $ref = $this->getEnv('HTTP_REFERER');
382
383
        $base = config('app.base_url') . $this->webroot;
0 ignored issues
show
Bug introduced by
Are you sure config('app.base_url') of type BlitzPHP\Config\Config|null can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

383
        $base = /** @scrutinizer ignore-type */ config('app.base_url') . $this->webroot;
Loading history...
384
        if ($base === '' || ($ref === null || $ref === '')) {
385
            return null;
386
        }
387
388
        if ($local && str_starts_with($ref, $base)) {
389
            $ref = substr($ref, strlen($base));
390
            if ($ref === '' || str_starts_with($ref, '//')) {
391
                $ref = '/';
392
            }
393
            if ($ref[0] !== '/') {
394
                $ref = '/' . $ref;
395
            }
396
397
            return $ref;
398
        }
399
400
        if ($local) {
401
            return null;
402
        }
403
404
        return $ref;
405
    }
406
407
    /**
408
     * Gestionnaire de méthodes manquant, les poignées enveloppent les anciennes méthodes de type isAjax()
409
     *
410
     * @return bool
411
     *
412
     * @throws BadMethodCallException lorsqu'une méthode invalide est appelée.
413
     */
414
    public function __call(string $name, array $params)
415
    {
416
        if (str_starts_with($name, 'is')) {
417 2
            $type = strtolower(substr($name, 2));
418
419 2
            array_unshift($params, $type);
420
421 2
            return $this->is(...$params);
422
        }
423
424
        throw new BadMethodCallException(sprintf('La méthode "%s()" n\'existe pas', $name));
425
    }
426
427
    /**
428
     * Vérifiez si une demande est d'un certain type.
429
     *
430
     * Utilise les règles de détection intégrées ainsi que des règles supplémentaires
431
     * défini avec {@link \BlitzPHP\Http\ServerRequest::addDetector()}. Tout détecteur peut être appelé
432
     * comme `is($type)` ou `is$Type()`.
433
     *
434
     * @param list<string>|string $type Le type de requête que vous souhaitez vérifier. S'il s'agit d'un tableau, cette méthode renverra true si la requête correspond à n'importe quel type.
435
     *
436
     * @return bool Si la demande est du type que vous vérifiez.
437
     */
438
    public function is($type, ...$args): bool
439
    {
440
        if (is_array($type)) {
0 ignored issues
show
introduced by
The condition is_array($type) is always false.
Loading history...
441
            foreach ($type as $_type) {
442
                if ($this->is($_type)) {
443
                    return true;
444
                }
445
            }
446
447
            return false;
448
        }
449
450 4
        $type = strtolower($type);
451
        if (! isset(static::$_detectors[$type])) {
452
            return false;
453
        }
454
        if ($args !== []) {
455 2
            return $this->_is($type, $args);
456
        }
457
458 4
        return $this->_detectorCache[$type] ??= $this->_is($type, $args);
459
    }
460
461
    /**
462
     * Efface le cache du détecteur d'instance, utilisé par la fonction is()
463
     */
464
    public function clearDetectorCache(): void
465
    {
466 12
        $this->_detectorCache = [];
467
    }
468
469
    /**
470
     * Worker pour la fonction publique is()
471
     *
472
     * @param string $type Le type de requête que vous souhaitez vérifier.
473
     * @param array  $args Tableau d'arguments de détecteur personnalisés.
474
     *
475
     * @return bool Si la demande est du type que vous vérifiez.
476
     */
477
    protected function _is(string $type, array $args): bool
478
    {
479 4
        $detect = static::$_detectors[$type];
480
        if ($detect instanceof Closure) {
481 2
            array_unshift($args, $this);
482
483 2
            return $detect(...$args);
484
        }
485
        if (isset($detect['env']) && $this->_environmentDetector($detect)) {
486
            return true;
487
        }
488
        if (isset($detect['header']) && $this->_headerDetector($detect)) {
489 2
            return true;
490
        }
491
        if (isset($detect['accept']) && $this->_acceptHeaderDetector($detect)) {
492 2
            return true;
493
        }
494
495 4
        return isset($detect['param']) && $this->_paramDetector($detect);
496
    }
497
498
    /**
499
     * Détecte si un en-tête d'acceptation spécifique est présent.
500
     *
501
     * @param array $detect Tableau d'options du détecteur.
502
     *
503
     * @return bool Si la demande est du type que vous vérifiez.
504
     */
505
    protected function _acceptHeaderDetector(array $detect): bool
506
    {
507 2
        $acceptHeaders = explode(',', (string) $this->getEnv('HTTP_ACCEPT'));
508
509
        foreach ($detect['accept'] as $header) {
510
            if (in_array($header, $acceptHeaders, true)) {
511 2
                return true;
512
            }
513
        }
514
515 2
        return false;
516
    }
517
518
    /**
519
     * Détecte si un en-tête spécifique est présent.
520
     *
521
     * @param array $detect Tableau d'options du détecteur.
522
     *
523
     * @return bool Si la demande est du type que vous vérifiez.
524
     */
525
    protected function _headerDetector(array $detect): bool
526
    {
527
        foreach ($detect['header'] as $header => $value) {
528 2
            $header = $this->getEnv('http_' . $header);
529
            if ($header !== null) {
530
                if ($value instanceof Closure) {
531 2
                    return $value($header);
532
                }
533
534 2
                return $header === $value;
535
            }
536
        }
537
538
        return false;
539
    }
540
541
    /**
542
     * Détecte si un paramètre de requête spécifique est présent.
543
     *
544
     * @param array $detect Tableau d'options du détecteur.
545
     *
546
     * @return bool Si la demande est du type que vous vérifiez.
547
     */
548
    protected function _paramDetector(array $detect): bool
549
    {
550 2
        $key = $detect['param'];
551
        if (isset($detect['value'])) {
552 2
            $value = $detect['value'];
553
554 2
            return isset($this->params[$key]) && $this->params[$key] === $value;
555
        }
556
        if (isset($detect['options'])) {
557
            return isset($this->params[$key]) && in_array($this->params[$key], $detect['options'], true);
558
        }
559
560
        return false;
561
    }
562
563
    /**
564
     * Détecte si une variable d'environnement spécifique est présente.
565
     *
566
     * @param array $detect Tableau d'options du détecteur.
567
     *
568
     * @return bool Si la demande est du type que vous vérifiez.
569
     */
570
    protected function _environmentDetector(array $detect): bool
571
    {
572
        if (isset($detect['env'])) {
573
            if (isset($detect['value'])) {
574 2
                return $this->getEnv($detect['env']) === $detect['value'];
575
            }
576
            if (isset($detect['pattern'])) {
577
                return (bool) preg_match($detect['pattern'], (string) $this->getEnv($detect['env']));
578
            }
579
            if (isset($detect['options'])) {
580
                $pattern = '/' . implode('|', $detect['options']) . '/i';
581
582
                return (bool) preg_match($pattern, (string) $this->getEnv($detect['env']));
583
            }
584
        }
585
586
        return false;
587
    }
588
589
    /**
590
     * Vérifier qu'une requête correspond à tous les types donnés.
591
     *
592
     * Vous permet de tester plusieurs types et d'unir les résultats.
593
     * Voir Request::is() pour savoir comment ajouter des types supplémentaires et le
594
     * types intégrés.
595
     *
596
     * @param list<string> $types Les types à vérifier.
597
     *
598
     * @see ServerRequest::is()
599
     */
600
    public function isAll(array $types): bool
601
    {
602
        foreach ($types as $type) {
603
            if (! $this->is($type)) {
604
                return false;
605
            }
606
        }
607
608
        return true;
609
    }
610
611
    /**
612
     * Ajouter un nouveau détecteur à la liste des détecteurs qu'une requête peut utiliser.
613
     * Il existe plusieurs types de détecteurs différents qui peuvent être réglés.
614
     *
615
     * ### Comparaison des rappels
616
     *
617
     * Les détecteurs de rappel vous permettent de fournir un callable pour gérer le chèque.
618
     * Le rappel recevra l'objet de requête comme seul paramètre.
619
     *
620
     * ```
621
     * addDetector('custom', function ($request) { //Renvoyer un booléen });
622
     * ```
623
     *
624
     * ### Comparaison des valeurs d'environnement
625
     *
626
     * Une comparaison de valeur d'environnement, compare une valeur extraite de `env()` à une valeur connue
627
     * la valeur d'environnement est l'égalité vérifiée par rapport à la valeur fournie.
628
     *
629
     * ```
630
     * addDetector('post', ['env' => 'REQUEST_METHOD', 'value' => 'POST']);
631
     * ```
632
     *
633
     * ### Comparaison des paramètres de demande
634
     *
635
     * Permet des détecteurs personnalisés sur les paramètres de demande.
636
     *
637
     * ```
638
     * addDetector('admin', ['param' => 'prefix', 'value' => 'admin']);
639
     * ```
640
     *
641
     * ### Accepter la comparaison
642
     *
643
     * Permet au détecteur de comparer avec la valeur d'en-tête Accepter.
644
     *
645
     * ```
646
     * addDetector('csv', ['accept' => 'text/csv']);
647
     * ```
648
     *
649
     * ### Comparaison d'en-tête
650
     *
651
     * Permet de comparer un ou plusieurs en-têtes.
652
     *
653
     * ```
654
     * addDetector('fancy', ['header' => ['X-Fancy' => 1]);
655
     * ```
656
     *
657
     * Les types `param`, `env` et de comparaison permettent ce qui suit
658
     * options de comparaison de valeur :
659
     *
660
     * ### Comparaison des valeurs de modèle
661
     *
662
     * La comparaison de valeurs de modèles vous permet de comparer une valeur extraite de `env()` à une expression régulière.
663
     *
664
     * ```
665
     * addDetector('iphone', ['env' => 'HTTP_USER_AGENT', 'pattern' => '/iPhone/i']);
666
     * ```
667
     *
668
     * ### Comparaison basée sur les options
669
     *
670
     * Les comparaisons basées sur des options utilisent une liste d'options pour créer une expression régulière. Appels ultérieurs
671
     * ajouter un détecteur d'options déjà défini fusionnera les options.
672
     *
673
     * ```
674
     * addDetector('mobile', ['env' => 'HTTP_USER_AGENT', 'options' => ['Fennec']]);
675
     * ```
676
     *
677
     * Vous pouvez également comparer plusieurs valeurs
678
     * en utilisant la touche `options`. Ceci est utile lorsque vous souhaitez vérifier
679
     * si une valeur de requête se trouve dans une liste d'options.
680
     *
681
     * `addDetector('extension', ['param' => '_ext', 'options' => ['pdf', 'csv']]`
682
     *
683
     * @param array|callable $detector Un callback ou tableau d'options pour la définition du détecteur.
684
     */
685
    public static function addDetector(string $name, $detector): void
686
    {
687 2
        $name = strtolower($name);
688
        if ($detector instanceof Closure) {
689 2
            static::$_detectors[$name] = $detector;
690
691 2
            return;
692
        }
693
        if (isset(static::$_detectors[$name], $detector['options'])) {
694
            /** @var array $data */
695
            $data     = static::$_detectors[$name];
696
            $detector = Arr::merge($data, $detector);
697
        }
698 2
        static::$_detectors[$name] = $detector;
699
    }
700
701
    /**
702
     * Normaliser un nom d'en-tête dans la version SERVER.
703
     */
704
    protected function normalizeHeaderName(string $name): string
705
    {
706 16
        $name = str_replace('-', '_', strtoupper($name));
707
        if (! in_array($name, ['CONTENT_LENGTH', 'CONTENT_TYPE'], true)) {
708 16
            $name = 'HTTP_' . $name;
709
        }
710
711 16
        return $name;
712
    }
713
714
    /**
715
     * Obtenez tous les en-têtes de la requête.
716
     *
717
     * Renvoie un tableau associatif où les noms d'en-tête sont
718
     * les clés et les valeurs sont une liste de valeurs d'en-tête.
719
     *
720
     * Bien que les noms d'en-tête ne soient pas sensibles à la casse, getHeaders() normalisera
721
     * les en-têtes.
722
     *
723
     * @return array<string, list<string>> Un tableau associatif d'en-têtes et leurs valeurs.
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string, list<string>> at position 4 could not be parsed: Expected '>' at position 4, but found 'list'.
Loading history...
724
     *
725
     * @see http://www.php-fig.org/psr/psr-7/ Cette méthode fait partie de l'interface de requête du serveur PSR-7.
726
     */
727
    public function getHeaders(): array
728
    {
729
        $headers = [];
730
731
        foreach ($this->_environment as $key => $value) {
732
            $name = null;
733
            if (str_starts_with($key, 'HTTP_')) {
734
                $name = substr($key, 5);
735
            }
736
            if (str_starts_with($key, 'CONTENT_')) {
737
                $name = $key;
738
            }
739
            if ($name !== null) {
740
                $name           = str_replace('_', ' ', strtolower($name));
741
                $name           = str_replace(' ', '-', ucwords($name));
742
                $headers[$name] = (array) $value;
743
            }
744
        }
745
746
        return $headers;
747
    }
748
749
    /**
750
     * Vérifiez si un en-tête est défini dans la requête.
751
     *
752
     * @param string $name L'en-tête que vous souhaitez obtenir (insensible à la casse)
753
     *
754
     * @see http://www.php-fig.org/psr/psr-7/ Cette méthode fait partie de l'interface de requête du serveur PSR-7.
755
     */
756
    public function hasHeader(string $name): bool
757
    {
758
        if (isset($this->_environment[$name])) {
759 2
            return true;
760
        }
761
762
        if (isset($this->_environment[$this->normalizeHeaderName($name)])) {
763 6
            return true;
764
        }
765
766 12
        return [] !== $this->getHeader($name);
767
    }
768
769
    /**
770
     * Obtenez un seul en-tête de la requête.
771
     *
772
     * Renvoie la valeur de l'en-tête sous forme de tableau. Si l'en-tête
773
     * n'est pas présent, un tableau vide sera retourné.
774
     *
775
     * @param string $name L'en-tête que vous souhaitez obtenir (insensible à la casse)
776
     *
777
     * @return list<string> Un tableau associatif d'en-têtes et leurs valeurs.
778
     *                      Si l'en-tête n'existe pas, un tableau vide sera retourné.
779
     *
780
     * @see http://www.php-fig.org/psr/psr-7/ Cette méthode fait partie de l'interface de requête du serveur PSR-7.
781
     */
782
    public function getHeader(string $name): array
783
    {
784
        if (isset($this->_environment[$name])) {
785 2
            return (array) $this->_environment[$name];
0 ignored issues
show
Bug Best Practice introduced by
The expression return (array)$this->_environment[$name] returns the type array which is incompatible with the documented return type BlitzPHP\Http\list.
Loading history...
786
        }
787
788 16
        $name = $this->normalizeHeaderName($name);
789
        if (isset($this->_environment[$name])) {
790 6
            return (array) $this->_environment[$name];
0 ignored issues
show
Bug Best Practice introduced by
The expression return (array)$this->_environment[$name] returns the type array which is incompatible with the documented return type BlitzPHP\Http\list.
Loading history...
791
        }
792
793 16
        return (array) $this->getEnv($name);
0 ignored issues
show
Bug Best Practice introduced by
The expression return (array)$this->getEnv($name) returns the type array which is incompatible with the documented return type BlitzPHP\Http\list.
Loading history...
794
    }
795
796
    /**
797
     * Obtenez un seul en-tête sous forme de chaîne à partir de la requête.
798
     *
799
     * @param string $name L'en-tête que vous souhaitez obtenir (insensible à la casse)
800
     *
801
     * @return string Les valeurs d'en-tête sont réduites à une chaîne séparée par des virgules.
802
     *
803
     * @see http://www.php-fig.org/psr/psr-7/ Cette méthode fait partie de l'interface de requête du serveur PSR-7.
804
     */
805
    public function getHeaderLine(string $name): string
806
    {
807 10
        $value = $this->getHeader($name);
808
809 10
        return implode(', ', $value);
810
    }
811
812
    /**
813
     * Obtenez une demande modifiée avec l'en-tête fourni.
814
     *
815
     * @param array|string $value
816
     *
817
     * @see http://www.php-fig.org/psr/psr-7/ Cette méthode fait partie de l'interface de requête du serveur PSR-7.
818
     */
819
    public function withHeader(string $name, $value): static
820
    {
821 2
        $new                      = clone $this;
822 2
        $name                     = $this->normalizeHeaderName($name);
823 2
        $new->_environment[$name] = $value;
824
825 2
        return $new;
826
    }
827
828
    /**
829
     * Obtenez une demande modifiée avec l'en-tête fourni.
830
     *
831
     * Les valeurs d'en-tête existantes seront conservées. La valeur fournie
832
     * sera ajouté aux valeurs existantes.
833
     *
834
     * @param array|string $value
835
     *
836
     * @see http://www.php-fig.org/psr/psr-7/ Cette méthode fait partie de l'interface de requête du serveur PSR-7.
837
     */
838
    public function withAddedHeader(string $name, $value): static
839
    {
840
        $new      = clone $this;
841
        $name     = $this->normalizeHeaderName($name);
842
        $existing = [];
843
        if (isset($new->_environment[$name])) {
844
            $existing = (array) $new->_environment[$name];
845
        }
846
        $existing                 = array_merge($existing, (array) $value);
847
        $new->_environment[$name] = $existing;
848
849
        return $new;
850
    }
851
852
    /**
853
     * Obtenez une demande modifiée sans en-tête fourni.
854
     *
855
     * @see http://www.php-fig.org/psr/psr-7/ Cette méthode fait partie de l'interface de requête du serveur PSR-7.
856
     */
857
    public function withoutHeader(string $name): static
858
    {
859
        $new  = clone $this;
860
        $name = $this->normalizeHeaderName($name);
861
        unset($new->_environment[$name]);
862
863
        return $new;
864
    }
865
866
    /**
867
     * Obtenez la méthode HTTP utilisée pour cette requête.
868
     * Il existe plusieurs manières de spécifier une méthode.
869
     *
870
     * - Si votre client le prend en charge, vous pouvez utiliser des méthodes HTTP natives.
871
     * - Vous pouvez définir l'en-tête HTTP-X-Method-Override.
872
     * - Vous pouvez soumettre une entrée avec le nom `_method`
873
     *
874
     * Chacune de ces 3 approches peut être utilisée pour définir la méthode HTTP utilisée
875
     * par BlitzPHP en interne, et affectera le résultat de cette méthode.
876
     *
877
     * @see http://www.php-fig.org/psr/psr-7/ Cette méthode fait partie de l'interface de requête du serveur PSR-7.
878
     */
879
    public function getMethod(): string
880
    {
881 34
        return (string) $this->getEnv('REQUEST_METHOD', $_SERVER['REQUEST_METHOD'] ?? 'GET');
882
    }
883
884
    /**
885
     * Mettez à jour la méthode de requête et obtenez une nouvelle instance.
886
     *
887
     * @see http://www.php-fig.org/psr/psr-7/ Cette méthode fait partie de l'interface de requête du serveur PSR-7.
888
     */
889
    public function withMethod(string $method): static
890
    {
891 38
        $new = clone $this;
892
893
        if (! preg_match('/^[!#$%&\'*+.^_`\|~0-9a-z-]+$/i', $method)) {
894
            throw new InvalidArgumentException(sprintf(
895
                'Méthode HTTP non prise en charge "%s" fournie',
896
                $method
897 38
            ));
898
        }
899 38
        $new->_environment['REQUEST_METHOD'] = $method;
900
901 38
        return $new;
902
    }
903
904
    /**
905
     * Obtenez tous les paramètres de l'environnement du serveur.
906
     *
907
     * Lire toutes les données 'environnement' ou 'serveur' qui ont été
908
     * utilisé pour créer cette requête.
909
     *
910
     * @see http://www.php-fig.org/psr/psr-7/ Cette méthode fait partie de l'interface de requête du serveur PSR-7.
911
     */
912
    public function getServerParams(): array
913
    {
914 4
        return $this->_environment;
915
    }
916
917
    /**
918
     * Obtenez tous les paramètres de requête conformément aux spécifications PSR-7. Pour lire des valeurs de requête spécifiques
919
     * utilisez la méthode alternative getQuery().
920
     *
921
     * @see http://www.php-fig.org/psr/psr-7/ Cette méthode fait partie de l'interface de requête du serveur PSR-7.
922
     */
923
    public function getQueryParams(): array
924
    {
925 2
        return $this->query;
926
    }
927
928
    /**
929
     * Mettez à jour les données de la chaîne de requête et obtenez une nouvelle instance.
930
     *
931
     * @param array $query Les données de la chaîne de requête à utiliser
932
     *
933
     * @see http://www.php-fig.org/psr/psr-7/ Cette méthode fait partie de l'interface de requête du serveur PSR-7.
934
     */
935
    public function withQueryParams(array $query): static
936
    {
937
        $new        = clone $this;
938
        $new->query = $query;
939
940
        return $new;
941
    }
942
943
    /**
944
     * Obtenez l'hôte sur lequel la demande a été traitée.
945
     */
946
    public function host(): ?string
947
    {
948
        if ($this->trustProxy && $this->getEnv('HTTP_X_FORWARDED_HOST')) {
949
            return $this->getEnv('HTTP_X_FORWARDED_HOST');
950
        }
951
952
        return $this->getEnv('HTTP_HOST');
953
    }
954
955
    /**
956
     * Obtenez le port sur lequel la demande a été traitée.
957
     */
958
    public function port(): ?string
959
    {
960
        if ($this->trustProxy && $this->getEnv('HTTP_X_FORWARDED_PORT')) {
961
            return $this->getEnv('HTTP_X_FORWARDED_PORT');
962
        }
963
964
        return $this->getEnv('SERVER_PORT');
965
    }
966
967
    /**
968
     * Obtenez le schéma d'URL actuel utilisé pour la demande.
969
     *
970
     * par exemple. 'http' ou 'https'
971
     */
972
    public function scheme(): ?string
973
    {
974
        if ($this->trustProxy && $this->getEnv('HTTP_X_FORWARDED_PROTO')) {
975 2
            return $this->getEnv('HTTP_X_FORWARDED_PROTO');
976
        }
977
978 10
        return $this->getEnv('HTTPS') ? 'https' : 'http';
979
    }
980
981
    /**
982
     * Obtenez le nom de domaine et incluez les segments $tldLength du tld.
983
     *
984
     * @param int $tldLength Nombre de segments que contient votre tld. Par exemple : `example.com` contient 1 tld.
985
     *                       Alors que `example.co.uk` contient 2.
986
     *
987
     * @return string Nom de domaine sans sous-domaines.
988
     */
989
    public function domain(int $tldLength = 1): string
990
    {
991
        if (empty($host = $this->host())) {
992
            return '';
993
        }
994
995
        $segments = explode('.', $host);
996
        $domain   = array_slice($segments, -1 * ($tldLength + 1));
997
998
        return implode('.', $domain);
999
    }
1000
1001
    /**
1002
     * Obtenez les sous-domaines d'un hôte.
1003
     *
1004
     * @param int $tldLength Nombre de segments que contient votre tld. Par exemple : `example.com` contient 1 tld.
1005
     *                       Alors que `example.co.uk` contient 2.
1006
     *
1007
     * @return list<string> Un tableau de sous-domaines.
1008
     */
1009
    public function subdomains(int $tldLength = 1): array
1010
    {
1011
        if (empty($host = $this->host())) {
1012
            return [];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array() returns the type array which is incompatible with the documented return type BlitzPHP\Http\list.
Loading history...
1013
        }
1014
1015
        $segments = explode('.', $host);
1016
1017
        return array_slice($segments, 0, -1 * ($tldLength + 1));
0 ignored issues
show
Bug Best Practice introduced by
The expression return array_slice($segm...0, -1 * $tldLength + 1) returns the type array which is incompatible with the documented return type BlitzPHP\Http\list.
Loading history...
1018
    }
1019
1020
    /**
1021
     * Obtient une liste de types de contenu acceptables par le navigateur client dans l'ordre préférable.
1022
     *
1023
     * @return list<string>
1024
     */
1025
    public function getAcceptableContentTypes(): array
1026
    {
1027 2
        $raw    = $this->parseAccept();
1028 2
        $accept = [];
1029
1030
        foreach ($raw as $types) {
1031 2
            $accept = array_merge($accept, $types);
1032
        }
1033
1034 2
        return $accept;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $accept returns the type array which is incompatible with the documented return type BlitzPHP\Http\list.
Loading history...
1035
    }
1036
1037
    /**
1038
     * Découvrez quels types de contenu le client accepte ou vérifiez s'il accepte un
1039
     * type particulier de contenu.
1040
     *
1041
     * #### Obtenir tous les types :
1042
     *
1043
     * ```
1044
     * $this->request->accepts();
1045
     * ```
1046
     *
1047
     * #### Vérifier un seul type :
1048
     *
1049
     * ```
1050
     * $this->request->accepts('application/json');
1051
     * ```
1052
     *
1053
     * Cette méthode ordonnera les types de contenu renvoyés par les valeurs de préférence indiquées
1054
     * par le client.
1055
     *
1056
     * @param array|string|null $types Le type de contenu à vérifier. Laissez null pour obtenir tous les types qu'un client accepte.
1057
     *
1058
     * @return bool|list<string> Soit un tableau de tous les types acceptés par le client, soit un booléen s'il accepte le type fourni.
1059
     */
1060
    public function accepts(array|string|null $types = null)
1061
    {
1062
        $accept = $this->getAcceptableContentTypes();
1063
1064
        if ($types === null) {
1065
            return $accept;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $accept returns the type array which is incompatible with the documented return type BlitzPHP\Http\list|boolean.
Loading history...
1066
        }
1067
1068
        foreach ((array) $types as $type) {
1069
            if (in_array($type, $accept, true)) {
1070
                return true;
1071
            }
1072
        }
1073
1074
        return false;
1075
    }
1076
1077
    /**
1078
     * Analyser l'en-tête HTTP_ACCEPT et renvoyer un tableau trié avec les types de contenu
1079
     * comme clés et valeurs pref comme valeurs.
1080
     *
1081
     * Généralement, vous souhaitez utiliser {@link \BlitzPHP\Http\ServerRequest::accepts()} pour obtenir une liste simple
1082
     * des types de contenu acceptés.
1083
     *
1084
     * @return array Un tableau de `prefValue => [contenu/types]`
1085
     */
1086
    public function parseAccept(): array
1087
    {
1088 2
        return $this->_parseAcceptWithQualifier($this->getHeaderLine('Accept'));
1089
    }
1090
1091
    /**
1092
     * Obtenez les langues acceptées par le client ou vérifiez si une langue spécifique est acceptée.
1093
     *
1094
     * Obtenez la liste des langues acceptées :
1095
     *
1096
     * ``` \BlitzPHP\Http\ServerRequest::acceptLanguage(); ```
1097
     *
1098
     * Vérifiez si une langue spécifique est acceptée :
1099
     *
1100
     * ``` \BlitzPHP\Http\ServerRequest::acceptLanguage('es-es'); ```
1101
     *
1102
     * @return array|bool Si un $language est fourni, un booléen. Sinon, le tableau des langues acceptées.
1103
     */
1104
    public function acceptLanguage(?string $language = null)
1105
    {
1106
        $raw    = $this->_parseAcceptWithQualifier($this->getHeaderLine('Accept-Language'));
1107
        $accept = [];
1108
1109
        foreach ($raw as $languages) {
1110
            foreach ($languages as &$lang) {
1111
                if (strpos($lang, '_')) {
1112
                    $lang = str_replace('_', '-', $lang);
1113
                }
1114
                $lang = strtolower($lang);
1115
            }
1116
            $accept = array_merge($accept, $languages);
1117
        }
1118
        if ($language === null) {
1119
            return $accept;
1120
        }
1121
1122
        return in_array(strtolower($language), $accept, true);
1123
    }
1124
1125
    /**
1126
     * Analysez les en-têtes Accept* avec les options de qualificateur.
1127
     *
1128
     * Seuls les qualificatifs seront extraits, toutes les autres extensions acceptées seront
1129
     * jetés car ils ne sont pas fréquemment utilisés.
1130
     */
1131
    protected function _parseAcceptWithQualifier(string $header): array
1132
    {
1133 2
        $accept  = [];
1134 2
        $headers = explode(',', $header);
1135
1136
        foreach (array_filter($headers) as $value) {
1137 2
            $prefValue = '1.0';
1138
            $value     = trim($value);
1139
1140
            $semiPos = strpos($value, ';');
1141
            if ($semiPos !== false) {
1142
                $params = explode(';', $value);
1143
                $value  = trim($params[0]);
1144
1145
                foreach ($params as $param) {
1146
                    $qPos = strpos($param, 'q=');
1147
                    if ($qPos !== false) {
1148
                        $prefValue = substr($param, $qPos + 2);
1149
                    }
1150
                }
1151
            }
1152
1153
            if (! isset($accept[$prefValue])) {
1154
                $accept[$prefValue] = [];
1155
            }
1156
            if ($prefValue !== '' && $prefValue !== '0') {
1157
                $accept[$prefValue][] = $value;
1158
            }
1159
        }
1160 2
        krsort($accept);
1161
1162 2
        return $accept;
1163
    }
1164
1165
    /**
1166
     * Lire une valeur de requête spécifique ou un chemin en pointillés.
1167
     *
1168
     * Les développeurs sont encouragés à utiliser getQueryParams() s'ils ont besoin de tout le tableau de requête,
1169
     * car il est compatible PSR-7, et cette méthode ne l'est pas. En utilisant Hash::get(), vous pouvez également obtenir des paramètres uniques.
1170
     *
1171
     * ### Alternative PSR-7
1172
     *
1173
     * ```
1174
     * $value = Arr::get($request->getQueryParams(), 'Post.id');
1175
     * ```
1176
     *
1177
     * @param string|null $name    Le nom ou le chemin en pointillé vers le paramètre de requête ou null pour tout lire.
1178
     * @param mixed       $default La valeur par défaut si le paramètre nommé n'est pas défini et que $name n'est pas nul.
1179
     *
1180
     * @return array|string|null Requête de données.
1181
     *
1182
     * @see ServerRequest::getQueryParams()
1183
     */
1184
    public function getQuery(?string $name = null, $default = null)
1185
    {
1186
        if ($name === null) {
1187
            return $this->query;
1188
        }
1189
1190 6
        return Arr::get($this->query, $name, $default);
1191
    }
1192
1193
    /**
1194
     * Fournit un accesseur sécurisé pour les données de requête. Permet
1195
     * vous permet d'utiliser des chemins compatibles Arr::get().
1196
     *
1197
     * ### Lecture des valeurs.
1198
     *
1199
     * ```
1200
     * // récupère toutes les données
1201
     * $request->getData();
1202
     *
1203
     * // Lire un champ spécifique.
1204
     * $request->getData('Post.title');
1205
     *
1206
     * // Avec une valeur par défaut.
1207
     * $request->getData('Post.not there', 'default value');
1208
     * ```
1209
     *
1210
     * Lors de la lecture des valeurs, vous obtiendrez `null` pour les clés/valeurs qui n'existent pas.
1211
     *
1212
     * Les développeurs sont encouragés à utiliser getParsedBody() s'ils ont besoin de tout le tableau de données,
1213
     * car il est compatible PSR-7, et cette méthode ne l'est pas. En utilisant Hash::get(), vous pouvez également obtenir des paramètres uniques.
1214
     *
1215
     * ### Alternative PSR-7
1216
     *
1217
     * ```
1218
     * $value = Arr::get($request->getParsedBody(), 'Post.id');
1219
     * ```
1220
     *
1221
     * @param string|null $name    Nom séparé par un point de la valeur à lire. Ou null pour lire toutes les données.
1222
     * @param mixed       $default Les données par défaut.
1223
     *
1224
     * @return mixed La valeur en cours de lecture.
1225
     */
1226
    public function getData(?string $name = null, $default = null)
1227
    {
1228
        if ($name === null) {
1229 10
            return $this->data;
1230
        }
1231
        if (! is_array($this->data)) {
1232
            return $default;
1233
        }
1234
1235 4
        return Arr::get($this->data, $name, $default);
1236
    }
1237
1238
    /**
1239
     * Lire les données de cookie à partir des données de cookie de la demande.
1240
     *
1241
     * @param string            $key     La clé ou le chemin en pointillés que vous voulez lire.
1242
     * @param array|string|null $default La valeur par défaut si le cookie n'est pas défini.
1243
     *
1244
     * @return array|string|null Soit la valeur du cookie, soit null si la valeur n'existe pas.
1245
     */
1246
    public function getCookie(string $key, $default = null)
1247
    {
1248 2
        return Arr::get($this->cookies, $key, $default);
1249
    }
1250
1251
    /**
1252
     * Obtenir une collection de cookies basée sur les cookies de la requête
1253
     *
1254
     * La CookieCollection vous permet d'interagir avec les cookies de demande en utilisant
1255
     * Objets `\BlitzPHP\Http\Cookie\Cookie` et peut faire des cookies de demande de conversion
1256
     * dans les cookies de réponse plus facile.
1257
     *
1258
     * Cette méthode créera une nouvelle collection de cookies à chaque appel.
1259
     * Il s'agit d'une optimisation qui permet d'allouer moins d'objets jusqu'à
1260
     * plus la CookieCollection est nécessaire. En général, vous devriez préférer
1261
     * `getCookie()` et `getCookieParams()` sur cette méthode. Utilisation d'une collection de cookies
1262
     * est idéal si vos cookies contiennent des données complexes encodées en JSON.
1263
     */
1264
    public function getCookieCollection(): CookieCollection
1265
    {
1266
        return CookieCollection::createFromServerRequest($this);
1267
    }
1268
1269
    /**
1270
     * Remplacez les cookies de la requête par ceux contenus dans
1271
     * la CookieCollection fournie.
1272
     */
1273
    public function withCookieCollection(CookieCollection $cookies): static
1274
    {
1275
        $new    = clone $this;
1276
        $values = [];
1277
1278
        foreach ($cookies as $cookie) {
1279
            $values[$cookie->getName()] = $cookie->getValue();
1280
        }
1281
        $new->cookies = $values;
1282
1283
        return $new;
1284
    }
1285
1286
    /**
1287
     * Obtenez toutes les données de cookie de la requête.
1288
     *
1289
     * @return array Un tableau de données de cookie.
1290
     */
1291
    public function getCookieParams(): array
1292
    {
1293
        return $this->cookies;
1294
    }
1295
1296
    /**
1297
     * Remplacez les cookies et obtenez une nouvelle instance de requête.
1298
     *
1299
     * @param array $cookies Les nouvelles données de cookie à utiliser.
1300
     */
1301
    public function withCookieParams(array $cookies): static
1302
    {
1303
        $new          = clone $this;
1304
        $new->cookies = $cookies;
1305
1306
        return $new;
1307
    }
1308
1309
    /**
1310
     * Obtenez les données de corps de requête analysées.
1311
     *
1312
     * Si la requête Content-Type est soit application/x-www-form-urlencoded
1313
     * ou multipart/form-data, et la méthode de requête est POST, ce sera le
1314
     * publier des données. Pour les autres types de contenu, il peut s'agir de la requête désérialisée
1315
     * corps.
1316
     *
1317
     * @return array|object|null Les paramètres de corps désérialisés, le cas échéant.
1318
     *                           Il s'agira généralement d'un tableau.
1319
     */
1320
    public function getParsedBody()
1321
    {
1322 10
        return $this->data;
1323
    }
1324
1325
    /**
1326
     * Mettez à jour le corps analysé et obtenez une nouvelle instance.
1327
     *
1328
     * @param array|object|null $data Les données de corps désérialisées. Cette volonté
1329
     *                                être généralement dans un tableau ou un objet.
1330
     */
1331
    public function withParsedBody($data): static
1332
    {
1333 10
        $new       = clone $this;
1334 10
        $new->data = $data;
1335
1336 10
        return $new;
1337
    }
1338
1339
    /**
1340
     * Récupère la version du protocole HTTP sous forme de chaîne.
1341
     *
1342
     * @return string Version du protocole HTTP.
1343
     */
1344
    public function getProtocolVersion(): string
1345
    {
1346
        if ($this->protocol !== null) {
1347
            return $this->protocol;
1348
        }
1349
1350
        // Remplissez paresseusement ces données car elles ne sont généralement pas utilisées.
1351
        preg_match('/^HTTP\/([\d.]+)$/', (string) $this->getEnv('SERVER_PROTOCOL'), $match);
1352
        $protocol = '1.1';
1353
        if (isset($match[1])) {
1354
            $protocol = $match[1];
1355
        }
1356
        $this->protocol = $protocol;
1357
1358
        return $this->protocol;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->protocol returns the type null which is incompatible with the type-hinted return string.
Loading history...
1359
    }
1360
1361
    /**
1362
     * Renvoie une instance avec la version de protocole HTTP spécifiée.
1363
     *
1364
     * La chaîne de version DOIT contenir uniquement le numéro de version HTTP (par exemple,
1365
     * "1.1", "1.0").
1366
     *
1367
     * @param string $version Version du protocole HTTP
1368
     */
1369
    public function withProtocolVersion(string $version): static
1370
    {
1371
        if (! preg_match('/^(1\.[01]|2(\.[0])?)$/', $version)) {
1372
            throw new InvalidArgumentException(sprintf('Version de protocole `%s` non prise en charge fournie.', $version));
1373
        }
1374
        $new           = clone $this;
1375
        $new->protocol = $version;
1376
1377
        return $new;
1378
    }
1379
1380
    /**
1381
     * Obtenez une valeur à partir des données d'environnement de la demande.
1382
     * Se replier sur env() si la clé n'est pas définie dans la propriété $environment.
1383
     *
1384
     * @param string      $key     La clé à partir de laquelle vous voulez lire.
1385
     * @param string|null $default Valeur par défaut lors de la tentative de récupération d'un environnement
1386
     *                             valeur de la variable qui n'existe pas.
1387
     *
1388
     * @return string|null Soit la valeur de l'environnement, soit null si la valeur n'existe pas.
1389
     */
1390
    public function getEnv(string $key, ?string $default = null): ?string
1391
    {
1392 38
        $key = strtoupper($key);
1393
        if (! array_key_exists($key, $this->_environment) || null === $this->_environment[$key]) {
1394 18
            $this->_environment[$key] = env($key, $default);
1395
        }
1396
1397 38
        return $this->_environment[$key];
1398
    }
1399
1400
    /**
1401
     * Mettez à jour la demande avec un nouvel élément de données d'environnement.
1402
     *
1403
     * Renvoie un objet de requête mis à jour. Cette méthode retourne
1404
     * un *nouvel* objet de requête et ne mute pas la requête sur place.
1405
     */
1406
    public function withEnv(string $key, string $value): static
1407
    {
1408 12
        $new                     = clone $this;
1409 12
        $new->_environment[$key] = $value;
1410 12
        $new->clearDetectorCache();
1411
1412 12
        return $new;
1413
    }
1414
1415
    /**
1416
     * Autoriser uniquement certaines méthodes de requête HTTP, si la méthode de requête ne correspond pas
1417
     * une erreur 405 s'affichera et l'en-tête de réponse "Autoriser" requis sera défini.
1418
     *
1419
     * Exemple:
1420
     *
1421
     * $this->request->allowMethod('post');
1422
     * ou alors
1423
     * $this->request->allowMethod(['post', 'delete']);
1424
     *
1425
     * Si la requête est GET, l'en-tête de réponse "Autoriser : POST, SUPPRIMER" sera défini
1426
     * et une erreur 405 sera renvoyée.
1427
     *
1428
     * @param list<string>|string $methods Méthodes de requête HTTP autorisées.
1429
     *
1430
     * @throws HttpException
1431
     */
1432
    public function allowMethod($methods): bool
1433
    {
1434
        $methods = (array) $methods;
1435
1436
        foreach ($methods as $method) {
1437
            if ($this->is($method)) {
1438
                return true;
1439
            }
1440
        }
1441
        $allowed = strtoupper(implode(', ', $methods));
1442
1443
        throw HttpException::methodNotAllowed($allowed);
1444
    }
1445
1446
    /**
1447
     * Mettez à jour la demande avec un nouvel élément de données de demande.
1448
     *
1449
     * Renvoie un objet de requête mis à jour. Cette méthode retourne
1450
     * un *nouvel* objet de requête et ne mute pas la requête sur place.
1451
     *
1452
     * Utilisez `withParsedBody()` si vous devez remplacer toutes les données de la requête.
1453
     *
1454
     * @param string $name Le chemin séparé par des points où insérer $value.
1455
     */
1456
    public function withData(string $name, mixed $value): static
1457
    {
1458
        $copy = clone $this;
1459
1460
        if (is_array($copy->data)) {
1461
            $copy->data = Arr::insert($copy->data, $name, $value);
1462
        }
1463
1464
        return $copy;
1465
    }
1466
1467
    /**
1468
     * Mettre à jour la demande en supprimant un élément de données.
1469
     *
1470
     * Renvoie un objet de requête mis à jour. Cette méthode retourne
1471
     * un *nouvel* objet de requête et ne mute pas la requête sur place.
1472
     *
1473
     * @param string $name Le chemin séparé par des points à supprimer.
1474
     */
1475
    public function withoutData(string $name): static
1476
    {
1477
        $copy = clone $this;
1478
1479
        if (is_array($copy->data)) {
1480
            $copy->data = Arr::remove($copy->data, $name);
1481
        }
1482
1483
        return $copy;
1484
    }
1485
1486
    /**
1487
     * Mettre à jour la requête avec un nouveau paramètre de routage
1488
     *
1489
     * Renvoie un objet de requête mis à jour. Cette méthode retourne
1490
     * un *nouvel* objet de requête et ne mute pas la requête sur place.
1491
     *
1492
     * @param string $name Le chemin séparé par des points où insérer $value.
1493
     */
1494
    public function withParam(string $name, mixed $value): static
1495
    {
1496 2
        $copy         = clone $this;
1497 2
        $copy->params = Arr::insert($copy->params, $name, $value);
1498
1499 2
        return $copy;
1500
    }
1501
1502
    /**
1503
     * Accédez en toute sécurité aux valeurs dans $this->params.
1504
     */
1505
    public function getParam(string $name, mixed $default = null)
1506
    {
1507 2
        return Arr::get($this->params, $name, $default);
1508
    }
1509
1510
    /**
1511
     * Renvoie une instance avec l'attribut de requête spécifié.
1512
     *
1513
     * @param string $name  Le nom de l'attribut.
1514
     * @param mixed  $value La valeur de l'attribut.
1515
     */
1516
    public function withAttribute(string $name, mixed $value): static
1517
    {
1518
        $new = clone $this;
1519
        if (in_array($name, $this->emulatedAttributes, true)) {
1520
            // $new->{$name} = $value;
1521
        } else {
1522
            $new->attributes[$name] = $value;
1523
        }
1524
1525
        return $new;
1526
    }
1527
1528
    /**
1529
     * Renvoie une instance sans l'attribut de requête spécifié.
1530
     *
1531
     * @param string $name Le nom de l'attribut.
1532
     *
1533
     * @throws InvalidArgumentException
1534
     */
1535
    public function withoutAttribute(string $name): static
1536
    {
1537
        $new = clone $this;
1538
        if (in_array($name, $this->emulatedAttributes, true)) {
1539
            throw new InvalidArgumentException(
1540
                "Vous ne pouvez pas supprimer '{$name}'. C'est un attribut BlitzPHP obligatoire."
1541
            );
1542
        }
1543
        unset($new->attributes[$name]);
1544
1545
        return $new;
1546
    }
1547
1548
    /**
1549
     * Tentatives d'obtenir de vieilles données d'entrée qui a été flashé à la session avec redirect_with_input().
1550
     * Il vérifie d'abord les données dans les anciennes données POST, puis les anciennes données GET et enfin vérifier les tableaux de points
1551
     *
1552
     * @return array|string|null
1553
     */
1554
    public function getOldInput(string $key)
1555
    {
1556
        return $this->session()->getOldInput($key);
1557
    }
1558
1559
    /**
1560
     * Lire un attribut de la requête ou obtenir la valeur par défaut
1561
     *
1562
     * @param string $name    Le nom de l'attribut.
1563
     * @param mixed  $default La valeur par défaut si l'attribut n'a pas été défini.
1564
     */
1565
    public function getAttribute(string $name, mixed $default = null): mixed
1566
    {
1567
        if (in_array($name, $this->emulatedAttributes, true)) {
1568
            if ($name === 'here') {
1569
                return $this->base . $this->uri->getPath();
1570
            }
1571
1572
            return $this->{$name};
1573
        }
1574
        if (array_key_exists($name, $this->attributes)) {
1575 70
            return $this->attributes[$name];
1576
        }
1577
1578 70
        return $default;
1579
    }
1580
1581
    /**
1582
     * Obtenez tous les attributs de la requête.
1583
     *
1584
     * Cela inclura les attributs params, webroot, base et here fournis par BlitzPHP.
1585
     */
1586
    public function getAttributes(): array
1587
    {
1588
        $emulated = [
1589
            'params'  => $this->params,
1590
            'webroot' => $this->webroot,
1591
            'base'    => $this->base,
1592
            'here'    => $this->base . $this->uri->getPath(),
1593
        ];
1594
1595
        return $this->attributes + $emulated;
1596
    }
1597
1598
    /**
1599
     * Obtenez le fichier téléchargé à partir d'un chemin en pointillés.
1600
     *
1601
     * @param string $path Le chemin séparé par des points vers le fichier que vous voulez.
1602
     *
1603
     * @return list<UploadedFileInterface>|UploadedFileInterface|null
1604
     */
1605
    public function getUploadedFile(string $path)
1606
    {
1607 2
        $file = Arr::get($this->uploadedFiles, $path);
1608
        if (is_array($file)) {
1609
            foreach ($file as $f) {
1610
                if (! ($f instanceof UploadedFile)) {
1611 2
                    return null;
1612
                }
1613
            }
1614
1615 2
            return $file;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $file returns the type array which is incompatible with the documented return type BlitzPHP\Http\list.
Loading history...
1616
        }
1617
1618
        if (! ($file instanceof UploadedFileInterface)) {
1619 2
            return null;
1620
        }
1621
1622 2
        return $file;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $file returns the type Psr\Http\Message\UploadedFileInterface which is incompatible with the documented return type BlitzPHP\Http\list.
Loading history...
1623
    }
1624
1625
    /**
1626
     * Obtenez le tableau des fichiers téléchargés à partir de la requête.
1627
     */
1628
    public function getUploadedFiles(): array
1629
    {
1630 6
        return $this->uploadedFiles;
1631
    }
1632
1633
    /**
1634
     * Mettez à jour la demande en remplaçant les fichiers et en créant une nouvelle instance.
1635
     *
1636
     * @param array $uploadedFiles Un tableau d'objets de fichiers téléchargés.
1637
     *
1638
     * @throws InvalidArgumentException lorsque $files contient un objet invalide.
1639
     */
1640
    public function withUploadedFiles(array $uploadedFiles): static
1641
    {
1642 12
        $this->validateUploadedFiles($uploadedFiles, '');
1643 12
        $new                = clone $this;
1644 12
        $new->uploadedFiles = $uploadedFiles;
1645
1646 12
        return $new;
1647
    }
1648
1649
    /**
1650
     * Validez de manière récursive les données de fichier téléchargées.
1651
     *
1652
     * @param array  $uploadedFiles Le nouveau tableau de fichiers à valider.
1653
     * @param string $path          Le chemin jusqu'ici.
1654
     *
1655
     * @throws InvalidArgumentException Si des éléments feuilles ne sont pas des fichiers valides.
1656
     */
1657
    protected function validateUploadedFiles(array $uploadedFiles, string $path): void
1658
    {
1659
        foreach ($uploadedFiles as $key => $file) {
1660
            if (is_array($file)) {
1661 4
                $this->validateUploadedFiles($file, $key . '.');
1662
1663 4
                continue;
1664
            }
1665
1666
            if (! $file instanceof UploadedFileInterface) {
1667 2
                throw new InvalidArgumentException("Fichier invalide à `{$path}{$key}`");
1668
            }
1669
        }
1670
    }
1671
1672
    /**
1673
     * Obtient le corps du message.
1674
     */
1675
    public function getBody(): StreamInterface
1676
    {
1677 2
        return $this->stream;
1678
    }
1679
1680
    /**
1681
     * Renvoie une instance avec le corps de message spécifié.
1682
     */
1683
    public function withBody(StreamInterface $body): static
1684
    {
1685
        $new         = clone $this;
1686
        $new->stream = $body;
1687
1688
        return $new;
1689
    }
1690
1691
    /**
1692
     * Récupère l'instance d'URI.
1693
     */
1694
    public function getUri(): UriInterface
1695
    {
1696 16
        return $this->uri;
1697
    }
1698
1699
    /**
1700
     * Renvoie une instance avec l'uri spécifié
1701
     *
1702
     * *Attention* Remplacer l'Uri ne mettra pas à jour la `base`, `webroot`,
1703
     * et les attributs `url`.
1704
     *
1705
     * @param bool $preserveHost Indique si l'hôte doit être conservé.
1706
     */
1707
    public function withUri(UriInterface $uri, bool $preserveHost = false): static
1708
    {
1709 10
        $new      = clone $this;
1710 10
        $new->uri = $uri;
1711
1712
        if ($preserveHost && $this->hasHeader('Host')) {
1713 2
            return $new;
1714
        }
1715
1716
        if (empty($host = $uri->getHost())) {
1717 4
            return $new;
1718
        }
1719
        if (! empty($port = $uri->getPort())) {
1720 8
            $host .= ':' . $port;
1721
        }
1722 8
        $new->_environment['HTTP_HOST'] = $host;
1723
1724 8
        return $new;
1725
    }
1726
1727
    /**
1728
     * Créez une nouvelle instance avec une cible de demande spécifique.
1729
     *
1730
     * Vous pouvez utiliser cette méthode pour écraser la cible de la demande qui est
1731
     * déduit de l'Uri de la requête. Cela vous permet également de modifier la demande
1732
     * la forme de la cible en une forme absolue, une forme d'autorité ou une forme d'astérisque
1733
     *
1734
     * @see https://tools.ietf.org/html/rfc7230#section-2.7 (pour les différentes formes de demande-cible autorisées dans les messages de demande)
1735
     *
1736
     * @param string $requestTarget La cible de la requête.
1737
     *
1738
     * @psalm-suppress MoreSpecificImplementedParamType
1739
     */
1740
    public function withRequestTarget(string $requestTarget): static
1741
    {
1742 2
        $new                = clone $this;
1743 2
        $new->requestTarget = $requestTarget;
1744
1745 2
        return $new;
1746
    }
1747
1748
    /**
1749
     * Récupère la cible de la requête.
1750
     *
1751
     * Récupère la cible de la demande du message soit telle qu'elle a été demandée,
1752
     * ou comme défini avec `withRequestTarget()`. Par défaut, cela renverra le
1753
     * chemin relatif de l'application sans répertoire de base et la chaîne de requête
1754
     * défini dans l'environnement SERVER.
1755
     */
1756
    public function getRequestTarget(): string
1757
    {
1758
        if ($this->requestTarget !== null) {
1759
            return $this->requestTarget;
1760
        }
1761
1762 4
        $target = $this->uri->getPath();
1763
        if ($this->uri->getQuery() !== '') {
1764 4
            $target .= '?' . $this->uri->getQuery();
1765
        }
1766
1767
        if ($target === '') {
1768 4
            $target = '/';
1769
        }
1770
1771 4
        return $target;
1772
    }
1773
1774
    /**
1775
     * Récupère le chemin de la requête en cours.
1776
     */
1777
    public function getPath(): string
1778
    {
1779
        if ($this->requestTarget === null) {
1780 4
            return $this->uri->getPath();
1781
        }
1782
1783 2
        [$path] = explode('?', $this->requestTarget);
1784
1785 2
        return $path;
1786
    }
1787
1788
    /**
1789
     * Fournit un moyen pratique de travailler avec la classe Negotiate
1790
     * pour la négociation de contenu.
1791
     */
1792
    public function negotiate(string $type, array $supported, bool $strictMatch = false): string
1793
    {
1794
        if (null === $this->negotiator) {
1795
            $this->negotiator = Services::negotiator($this, true);
1796
        }
1797
1798
        return match (strtolower($type)) {
1799
            'media'    => $this->negotiator->media($supported, $strictMatch),
1800
            'charset'  => $this->negotiator->charset($supported),
1801
            'encoding' => $this->negotiator->encoding($supported),
1802
            'language' => $this->negotiator->language($supported),
1803
            default    => throw new HttpException($type . ' is not a valid negotiation type. Must be one of: media, charset, encoding, language.'),
1804
        };
1805
    }
1806
1807
    /**
1808
     * Définit la chaîne locale pour cette requête.
1809
     */
1810
    public function withLocale(string $locale): static
1811
    {
1812
        $validLocales = config('app.supported_locales');
1813
        // S'il ne s'agit pas d'un paramètre régional valide, définissez-le
1814
        // aux paramètres régionaux par défaut du site.
1815
        if (! in_array($locale, $validLocales, true)) {
0 ignored issues
show
Bug introduced by
$validLocales of type BlitzPHP\Config\Config|null is incompatible with the type array expected by parameter $haystack of in_array(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1815
        if (! in_array($locale, /** @scrutinizer ignore-type */ $validLocales, true)) {
Loading history...
1816
            $locale = config('app.language');
1817
        }
1818
1819
        Services::translator()->setLocale($locale);
0 ignored issues
show
Bug introduced by
It seems like $locale can also be of type BlitzPHP\Config\Config; however, parameter $locale of BlitzPHP\Translator\Translate::setLocale() does only seem to accept null|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

1819
        Services::translator()->setLocale(/** @scrutinizer ignore-type */ $locale);
Loading history...
1820
1821
        return $this->withAttribute('locale', $locale);
1822
    }
1823
1824
    /**
1825
     * Obtient les paramètres régionaux actuels, avec un retour à la valeur par défaut
1826
     * locale si aucune n'est définie.
1827
     */
1828
    public function getLocale(): string
1829
    {
1830
        if (empty($locale = $this->getAttribute('locale'))) {
1831 70
            $locale = $this->getAttribute('lang');
1832
        }
1833
1834 70
        return $locale ?? config('app.language');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $locale ?? config('app.language') could return the type BlitzPHP\Config\Config|null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
1835
    }
1836
}
1837