Completed
Push — master ( a5d324...4c5f08 )
by Dawid
03:32
created

ServerRequest::parseBody()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 5.3256

Importance

Changes 0
Metric Value
dl 0
loc 21
ccs 13
cts 17
cp 0.7647
rs 9.2728
c 0
b 0
f 0
cc 5
nc 5
nop 0
crap 5.3256
1
<?php declare(strict_types=1);
2
3
namespace Igni\Network\Http;
4
5
use Psr\Http\Message\ServerRequestInterface;
6
use Psr\Http\Message\StreamInterface;
7
use Psr\Http\Message\UploadedFileInterface;
8
use Swoole\Http\Request as SwooleHttRequest;
9
use Throwable;
10
11
use function Zend\Diactoros\marshalHeadersFromSapi;
12
use function Zend\Diactoros\normalizeUploadedFiles;
13
14
class ServerRequest extends Request implements ServerRequestInterface
15
{
16
    /**
17
     * @var array
18
     */
19
    private $attributes = [];
20
21
    /**
22
     * @var array
23
     */
24
    private $cookieParams = [];
25
26
    /**
27
     * @var null|array|object
28
     */
29
    private $parsedBody;
30
31
    /**
32
     * @var array
33
     */
34
    private $queryParams = [];
35
36
    /**
37
     * @var array
38
     */
39
    private $serverParams;
40
41
    /**
42
     * @var array
43
     */
44
    private $uploadedFiles;
45
46
47
    /**
48
     * Server request constructor.
49
     *
50
     * @param array $serverParams Server parameters, typically from $_SERVER
51
     * @param array $uploadedFiles Upload file information, a tree of UploadedFiles
52
     * @param null|string $uri URI for the request, if any.
53
     * @param null|string $method HTTP method for the request, if any.
54
     * @param string|resource|StreamInterface $body Messages body, if any.
55
     * @param array $headers Headers for the message, if any.
56
     * @throws \InvalidArgumentException for any invalid value.
57
     */
58 10
    public function __construct(
59
        string $uri = null,
60
        string $method = self::METHOD_GET,
61
        $body = 'php://input',
62
        array $headers = [],
63
        array $uploadedFiles = [],
64
        array $serverParams = []
65
    ) {
66 10
        parent::__construct($uri, $method, $body, $headers);
67 10
        $this->validateUploadedFiles($uploadedFiles);
68 10
        $this->serverParams  = $serverParams;
69 10
        $this->uploadedFiles = $uploadedFiles;
70 10
        parse_str($this->getUri()->getQuery(), $this->queryParams);
71 10
        $this->parseBody();
72 10
    }
73
74 10
    private function parseBody(): void
75
    {
76 10
        $contentType = $this->getHeader('Content-Type')[0] ?? '';
77
78 10
        $body = (string) $this->getBody();
79
80 10
        switch (strtolower($contentType)) {
81 10
            case 'application/json':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
82 1
                $this->parsedBody = json_decode($body, true);
83 1
                return;
84 9
            case 'application/x-www-form-urlencoded':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
85 1
                 parse_str($body, $this->parsedBody);
86 1
                 return;
87 8
            case 'application/xml':
88
                $this->parsedBody = simplexml_load_string($body);
89
                return;
90 8
            case 'text/csv':
91
                $this->parsedBody = str_getcsv($body);
92
                return;
93
        }
94 8
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99 2
    public function getServerParams(): array
100
    {
101 2
        return $this->serverParams;
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107 2
    public function getUploadedFiles(): array
108
    {
109 2
        return $this->uploadedFiles;
110
    }
111
112
    /**
113
     * {@inheritdoc}
114
     */
115 1
    public function withUploadedFiles(array $uploadedFiles)
116
    {
117 1
        $this->validateUploadedFiles($uploadedFiles);
118 1
        $new = clone $this;
119 1
        $new->uploadedFiles = $uploadedFiles;
120
121 1
        return $new;
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     */
127 2
    public function getCookieParams(): array
128
    {
129 2
        return $this->cookieParams;
130
    }
131
132
    /**
133
     * {@inheritdoc}
134
     */
135 1
    public function withCookieParams(array $cookies)
136
    {
137 1
        $new = clone $this;
138 1
        $new->cookieParams = $cookies;
139 1
        return $new;
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145 4
    public function getQueryParams(): array
146
    {
147 4
        return $this->queryParams;
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153 1
    public function withQueryParams(array $query)
154
    {
155 1
        $new = clone $this;
156 1
        $new->queryParams = $query;
157
158 1
        return $new;
159
    }
160
161
    /**
162
     * {@inheritdoc}
163
     */
164 4
    public function getParsedBody()
165
    {
166 4
        return $this->parsedBody;
167
    }
168
169
    /**
170
     * {@inheritdoc}
171
     */
172 1
    public function withParsedBody($data)
173
    {
174 1
        $new = clone $this;
175 1
        $new->parsedBody = $data;
176 1
        return $new;
177
    }
178
179
    /**
180
     * {@inheritdoc}
181
     */
182 2
    public function getAttributes(): array
183
    {
184 2
        return $this->attributes;
185
    }
186
187
    /**
188
     * {@inheritdoc}
189
     */
190 1
    public function getAttribute($attribute, $default = null)
191
    {
192 1
        if (! isset($this->attributes[$attribute])) {
193 1
            return $default;
194
        }
195
196 1
        return $this->attributes[$attribute];
197
    }
198
199
    /**
200
     * {@inheritdoc}
201
     */
202 1
    public function withAttribute($attribute, $value)
203
    {
204 1
        $new = clone $this;
205 1
        $new->attributes[$attribute] = $value;
206 1
        return $new;
207
    }
208
209
    /**
210
     * {@inheritdoc}
211
     */
212 1
    public function withoutAttribute($attribute)
213
    {
214 1
        if (!isset($this->attributes[$attribute])) {
215 1
            return clone $this;
216
        }
217
218 1
        $new = clone $this;
219 1
        unset($new->attributes[$attribute]);
220 1
        return $new;
221
    }
222
223
    /**
224
     * Sets request attributes
225
     *
226
     * This method returns a new instance.
227
     *
228
     * @param array $attributes
229
     * @return self
230
     */
231 1
    public function withAttributes(array $attributes): ServerRequest
232
    {
233 1
        $new = clone $this;
234 1
        $new->attributes = $attributes;
235 1
        return $new;
236
    }
237
238
    /**
239
     * Recursively validate the structure in an uploaded files array.
240
     *
241
     * @param array $uploadedFiles
242
     * @throws \InvalidArgumentException if any leaf is not an UploadedFileInterface instance.
243
     */
244 10
    private function validateUploadedFiles(array $uploadedFiles): void
245
    {
246 10
        foreach ($uploadedFiles as $file) {
247 1
            if (is_array($file)) {
248
                $this->validateUploadedFiles($file);
249
                continue;
250
            }
251
252 1
            if (! $file instanceof UploadedFileInterface) {
0 ignored issues
show
Bug introduced by
The class Psr\Http\Message\UploadedFileInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
253
                throw new \InvalidArgumentException('Invalid leaf in uploaded files structure');
254
            }
255
        }
256 10
    }
257
258 2
    public static function fromGlobals(): ServerRequest
259
    {
260 2
        $instance = new self(
261 2
            $_SERVER['REQUEST_URI'] ?? '',
262 2
            $_SERVER['REQUEST_METHOD'] ?? 'GET',
263 2
            'php://input',
264 2
            marshalHeadersFromSapi($_SERVER),
265 2
            normalizeUploadedFiles($_FILES),
266 2
            $_SERVER
267
        );
268
269 2
        return $instance;
270
    }
271
272
    /**
273
     * @param SwooleHttRequest $request
274
     * @return ServerRequest
275
     */
276 4
    public static function fromSwoole(SwooleHttRequest $request): ServerRequest
277
    {
278 4
        $serverParams  = $request->server ?? [];
279 4
        if (isset($request->server['query_string'])) {
280 1
            $uri = $request->server['request_uri'] . '?' . $request->server['query_string'];
281
        } else {
282 3
            $uri = $request->server['request_uri'];
283
        }
284
285
        // Normalize server params
286 4
        $serverParams = array_change_key_case($serverParams, CASE_UPPER);
287
288
        // Normalize headers
289 4
        $headers = [];
290 4
        if ($request->header) {
291 2
            foreach ($request->header as $name => $value) {
292 2
                if (!isset($headers[$name])) {
293 2
                    $headers[$name] = [];
294
                }
295 2
                array_push($headers[$name], $value);
296
            }
297
        }
298
299
        try {
300 4
            $body = $request->rawContent();
301 4
        } catch (Throwable $throwable) {
302 4
            $body = '';
303
        }
304
305 4
        if (!$body) {
306 4
            $body = '';
307
        }
308
309 4
        return new ServerRequest(
310 4
            $uri,
311 4
            $request->server['request_method'] ?? 'GET',
312 4
            $body,
313 4
            $headers,
314 4
            normalizeUploadedFiles($request->files ?? []) ?? [],
315 4
            $serverParams
316
        );
317
    }
318
}
319