Issues (536)

src/Middlewares/BodyParser.php (5 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\Middlewares;
13
14
use BlitzPHP\Exceptions\HttpException;
15
use BlitzPHP\Formatter\Formatter;
16
use Closure;
17
use Psr\Http\Message\ResponseInterface;
18
use Psr\Http\Message\ServerRequestInterface;
19
use Psr\Http\Server\MiddlewareInterface;
20
use Psr\Http\Server\RequestHandlerInterface;
21
22
/**
23
 * BodyParser
24
 *
25
 * Analysez les données du corps de la requête encodée.
26
 *
27
 * Permet aux charges utiles de requête JSON et XML d'être analysées dans la protection et la validation CSRF de la requête.
28
 *
29
 * Vous pouvez également ajouter vos propres analyseurs de corps de requête usi
30
 *
31
 * @credit		CakePHP (Cake\Http\Middleware\BodyParserMiddleware - https://cakephp.org)
32
 */
33
class BodyParser extends BaseMiddleware implements MiddlewareInterface
34
{
35
    /**
36
     * Parseurs enregistrés
37
     *
38
     * @var list<Closure>
0 ignored issues
show
The type BlitzPHP\Middlewares\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...
39
     */
40
    protected array $parsers = [];
41
42
    /**
43
     * Les méthodes HTTP sur lesquelles analyser les données.
44
     *
45
     * @var list<string>
46
     */
47
    protected array $methods = ['PUT', 'POST', 'PATCH', 'DELETE'];
48
49
    /**
50
     * Constructor
51
     *
52
     * ### Options
53
     *
54
     * - `json` Définir sur false pour désactiver l'analyse du corps JSON.
55
     * - `xml` Définir sur true pour activer l'analyse XML. La valeur par défaut est false, en tant que XML
56
     * La manipulation nécessite plus de soin que JSON.
57
     * - `methods` Les méthodes HTTP à analyser. Par défaut, PUT, POST, PATCH DELETE.
58
     */
59
    public function __construct(array $options = [])
60
    {
61
        $options += ['json' => true, 'xml' => false, 'methods' => null];
62
        if ($options['json']) {
63
            $this->addParser(
64
                ['application/json', 'text/json'],
65
                $this->decodeJson(...)
66
            );
67
        }
68
        if ($options['xml']) {
69
            $this->addParser(
70
                ['application/xml', 'text/xml'],
71
                $this->decodeXml(...)
72
            );
73
        }
74
        if ($options['methods']) {
75
            $this->setMethods($options['methods']);
76
        }
77
    }
78
79
    /**
80
     * Définissez les méthodes HTTP sur lesquelles analyser les corps de requête.
81
     *
82
     * @param list<string> $methods Les méthodes sur lesquelles analyser les données.
83
     */
84
    public function setMethods(?array $methods): static
85
    {
86
        if (is_array($methods)) {
0 ignored issues
show
The condition is_array($methods) is always true.
Loading history...
87
            $this->methods = $methods;
0 ignored issues
show
Documentation Bug introduced by
It seems like $methods of type array is incompatible with the declared type BlitzPHP\Middlewares\list of property $methods.

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...
88
        }
89
90
        return $this;
91
    }
92
93
    /**
94
     * Obtenez les méthodes HTTP pour analyser les corps de requête.
95
     *
96
     * @return list<string>
97
     */
98
    public function getMethods(): array
99
    {
100
        return $this->methods;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->methods returns the type array which is incompatible with the documented return type BlitzPHP\Middlewares\list.
Loading history...
101
    }
102
103
    /**
104
     * Ajoute un parser.
105
     *
106
     * Mappez un ensemble de valeurs d'en-tête de type de contenu à analyser par $parser.
107
     *
108
     * ### Example
109
     *
110
     * Un parseur de corps de requête CSV naïf pourrait être construit comme suit :
111
     *
112
     * ```
113
     * $parser->addParser(['text/csv'], function ($body) {
114
     *   return str_getcsv($body);
115
     * });
116
     * ```
117
     *
118
     * @param list<string> $types  Un tableau de valeurs d'en-tête de type de contenu à faire correspondre. par exemple. application/json
119
     * @param Closure      $parser La fonction de parser. Doit renvoyer un tableau de données à insérer dans la requête.
120
     */
121
    public function addParser(array $types, Closure $parser): static
122
    {
123
        foreach ($types as $type) {
124
            $type                 = strtolower($type);
125
            $this->parsers[$type] = $parser;
126
        }
127
128
        return $this;
129
    }
130
131
    /**
132
     * Obtenir les parseurs actuels
133
     *
134
     * @return list<Closure>
135
     */
136
    public function getParsers(): array
137
    {
138
        return $this->parsers;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->parsers returns the type array which is incompatible with the documented return type BlitzPHP\Middlewares\list.
Loading history...
139
    }
140
141
    /**
142
     * {@inheritDoc}
143
     *
144
     * Modifie la requête en ajoutant un corps analysé si le type de contenu est connu.
145
     */
146
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
147
    {
148
        if (! in_array($request->getMethod(), $this->methods, true)) {
149
            return $handler->handle($request);
150
        }
151
152
        [$type] = explode(';', $request->getHeaderLine('Content-Type'));
153
        $type   = strtolower($type);
154
        if (! isset($this->parsers[$type])) {
155
            return $handler->handle($request);
156
        }
157
158
        $parser = $this->parsers[$type];
159
        $result = $parser($request->getBody()->getContents());
160
        if (! is_array($result)) {
161
            throw HttpException::badRequest();
162
        }
163
        $request = $request->withParsedBody($result);
164
165
        return $handler->handle($request);
166
    }
167
168
    /**
169
     * Décode JSON dans un tableau.
170
     *
171
     * @param string $body Le corps de la requête à décoder
172
     */
173
    protected function decodeJson(string $body): ?array
174
    {
175
        if ($body === '') {
176
            return [];
177
        }
178
        $decoded = json_decode($body, true);
179
        if (json_last_error() === JSON_ERROR_NONE) {
180
            return (array) $decoded;
181
        }
182
183
        return null;
184
    }
185
186
    /**
187
     * Décode XML dans un tableau.
188
     *
189
     * @param string $body Le corps de la requête à décoder
190
     */
191
    protected function decodeXml(string $body): array
192
    {
193
        return Formatter::type('application/xml')->parse($body);
194
    }
195
}
196