Completed
Push — master ( 83af41...729d22 )
by Terry
02:37
created

ServerRequest::determineParsedBody()   B

Complexity

Conditions 11
Paths 30

Size

Total Lines 53
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 25
c 1
b 0
f 0
dl 0
loc 53
rs 7.3166
cc 11
nc 30
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php 
2
/*
3
 * This file is part of the Shieldon package.
4
 *
5
 * (c) Terry L. <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
declare(strict_types=1);
12
13
namespace Shieldon\Psr7;
14
15
use Psr\Http\Message\ServerRequestInterface;
16
use Psr\Http\Message\UploadedFileInterface;
17
use Psr\Http\Message\UriInterface;
18
use Psr\Http\Message\StreamInterface;
19
use Shieldon\Psr7\Request;
20
use Shieldon\Psr7\Utils\UploadedFileHelper;
21
use InvalidArgumentException;
22
use function file_get_contents;
23
use function gettype;
24
use function is_array;
25
use function is_null;
26
use function is_object;
27
use function json_decode;
28
use function json_last_error;
29
use function parse_str;
30
use function preg_split;
31
use function sprintf;
32
use function strtolower;
33
use function strtoupper;
34
use const JSON_ERROR_NONE;
35
36
/*
37
 * Representation of an incoming, server-side HTTP request.
38
 */
39
class ServerRequest extends Request implements ServerRequestInterface
40
{
41
    /**
42
     * Typically derived from PHP's $_SERVER superglobal.
43
     * 
44
     * @var array
45
     */
46
    protected $serverParams;
47
48
    /**
49
     * Typically derived from PHP's $_COOKIE superglobal.
50
     * 
51
     * @var array
52
     */
53
    protected $cookieParams;
54
55
    /**
56
     * Typically derived from PHP's $_POST superglobal.
57
     * 
58
     * @var array|object|null
59
     */
60
    protected $parsedBody;
61
62
    /**
63
     * Typically derived from PHP's $_GET superglobal.
64
     * 
65
     * @var array
66
     */
67
    protected $queryParams;
68
69
    /**
70
     * Typically derived from PHP's $_FILES superglobal.
71
     * A collection of uploadFileInterface instances.
72
     * 
73
     * @var array
74
     */
75
    protected $uploadedFiles;
76
77
    /**
78
     * The request "attributes" may be used to allow injection of any
79
     * parameters derived from the request: e.g., the results of path
80
     * match operations; the results of decrypting cookies; the results of
81
     * deserializing non-form-encoded message bodies; etc. Attributes
82
     * will be application and request specific, and CAN be mutable.
83
     *
84
     * @var array
85
     */
86
    protected $attributes;
87
88
    /**
89
     * ServerRequest constructor.
90
     *
91
     * @param string                 $method       Request HTTP method
92
     * @param string|UriInterface    $uri          Request URI object URI or URL
93
     * @param string|StreamInterface $body         Request body
94
     * @param array                  $headers      Request headers
95
     * @param string                 $version      Request protocol version
96
     * @param array                  $serverParams Typically $_SERVER superglobal
97
     * @param array                  $cookieParams Typically $_COOKIE superglobal
98
     * @param array                  $postParams   Typically $_POST superglobal
99
     * @param array                  $getParams    Typically $_GET superglobal
100
     * @param array                  $filesParams  Typically $_FILES superglobal
101
     */
102
    public function __construct(
103
        string $method       = 'GET',
104
               $uri          = ''   ,
105
               $body         = ''   ,
106
        array  $headers      = []   ,
107
        string $version      = '1.1',
108
        array  $serverParams = []   ,
109
        array  $cookieParams = []   ,
110
        array  $postParams   = []   ,
111
        array  $getParams    = []   ,
112
        array  $filesParams  = []
113
    ) {
114
        parent::__construct($method, $uri, $body, $headers, $version);
115
116
        $this->serverParams = $serverParams;
117
        $this->cookieParams = $cookieParams;
118
        $this->queryParams  = $getParams;
119
        $this->attributes   = [];
120
121
        $this->determineParsedBody($postParams);
122
123
        // This property will be assigned to a parsed array that contains 
124
        // the UploadedFile instance(s) as the $filesParams is given.
125
        $this->uploadedFiles = [];
126
127
        if (!empty($filesParams)) {
128
            $this->uploadedFiles = UploadedFileHelper::uploadedFileSpecsConvert(
129
                UploadedFileHelper::uploadedFileParse($filesParams)
130
            );
131
        }
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137
    public function getServerParams(): array
138
    {
139
        return $this->serverParams;
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145
    public function getCookieParams(): array
146
    {
147
        return $this->cookieParams;
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153
    public function withCookieParams(array $cookies)
154
    {
155
        $clone = clone $this;
156
        $clone->cookieParams = $cookies;
157
158
        return $clone;
159
    }
160
161
    /**
162
     * {@inheritdoc}
163
     */
164
    public function getQueryParams(): array
165
    {
166
        return $this->queryParams;
167
    }
168
169
    /**
170
     * {@inheritdoc}
171
     */
172
    public function withQueryParams(array $query)
173
    {
174
        $clone = clone $this;
175
        $clone->queryParams = $query;
176
177
        return $clone;
178
    }
179
180
    /**
181
     * {@inheritdoc}
182
     */
183
    public function getUploadedFiles(): array
184
    {
185
        return $this->uploadedFiles;
186
    }
187
188
    /**
189
     * {@inheritdoc}
190
     */
191
    public function withUploadedFiles(array $uploadedFiles)
192
    {
193
        $this->assertUploadedFiles($uploadedFiles);
194
195
        $clone = clone $this;
196
        $clone->uploadedFiles = $uploadedFiles;
197
198
        return $clone;
199
    }
200
201
    /**
202
     * {@inheritdoc}
203
     */
204
    public function getParsedBody()
205
    {
206
        return $this->parsedBody;
207
    }
208
209
    /**
210
     * {@inheritdoc}
211
     */
212
    public function withParsedBody($data)
213
    {
214
        $this->assertParsedBody($data);
215
216
        $clone = clone $this;
217
        $clone->parsedBody = $data;
218
219
        return $clone;
220
    }
221
222
    /**
223
     * {@inheritdoc}
224
     */
225
    public function getAttributes(): array
226
    {
227
        return $this->attributes;
228
    }
229
230
    /**
231
     * {@inheritdoc}
232
     */
233
    public function getAttribute($name, $default = null)
234
    {
235
        return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
236
    }
237
238
    /**
239
     * {@inheritdoc}
240
     */
241
    public function withAttribute($name, $value)
242
    {
243
        $clone = clone $this;
244
        $clone->attributes[$name] = $value;
245
246
        return $clone;
247
    }
248
249
    /**
250
     * {@inheritdoc}
251
     */
252
    public function withoutAttribute($name) 
253
    {
254
        $clone = clone $this;
255
256
        if (isset($this->attributes[$name])) {
257
            unset($clone->attributes[$name]);
258
        }
259
260
        return $clone;
261
    }
262
263
    /*
264
    |--------------------------------------------------------------------------
265
    | Non-PSR-7 Methods.
266
    |--------------------------------------------------------------------------
267
    */
268
269
    /**
270
     * Check out whether an array is compatible to PSR-7 file structure.
271
     * 
272
     * @param array $values The array to check.
273
     *
274
     * @return void
275
     *
276
     * @throws InvalidArgumentException
277
     */
278
    protected function assertUploadedFiles(array $values): void
279
    {
280
        foreach ($values as $value) {
281
            if (is_array($value)) {
282
                $this->assertUploadedFiles($value);
283
            } elseif (!($value instanceof UploadedFileInterface)) {
284
                throw new InvalidArgumentException(
285
                    'Invalid PSR-7 array structure for handling UploadedFile.'
286
                );
287
            }
288
        }
289
    }
290
291
    /**
292
     * Throw an exception if an unsupported argument type is provided.
293
     * 
294
     * @param string|array|null $data The deserialized body data. This will
295
     *     typically be in an array or object.
296
     *
297
     * @return void
298
     *
299
     * @throws InvalidArgumentException
300
     */
301
    protected function assertParsedBody($data): void
302
    {
303
        if (
304
            ! is_null($data) &&
305
            ! is_array($data) && 
306
            ! is_object($data)
307
        ) {
308
            throw new InvalidArgumentException(
309
                sprintf(
310
                    'Only accepts array, object and null, but "%s" provided.',
311
                    gettype($data)
312
                )
313
            );
314
        }
315
    }
316
317
    /**
318
     * Confirm the content type and post values whether fit the requirement.
319
     *
320
     * @param array $postParams 
321
     * @return void
322
     */
323
    protected function determineParsedBody(array $postParams)
324
    {
325
        $headerContentType = $this->getHeaderLine('Content-Type');
326
        $contentTypeArr = preg_split('/\s*[;,]\s*/', $headerContentType);
327
        $contentType = strtolower($contentTypeArr[0]);
328
        $httpMethod = strtoupper($this->getMethod());
329
330
        // Is it a form submit or not.
331
        $isForm = false;
332
333
        if ($httpMethod === 'POST') {
334
335
            // If the request Content-Type is either application/x-www-form-urlencoded
336
            // or multipart/form-data, and the request method is POST, this method MUST
337
            // return the contents of $_POST.
338
            $postRequiredContentTypes = [
339
                '', // For unit testing purpose.
340
                'application/x-www-form-urlencoded',
341
                'multipart/form-data',
342
            ];
343
344
            if (in_array($contentType, $postRequiredContentTypes)) {
345
                $this->parsedBody = $postParams ?? null;
346
                $isForm = true;
347
            }
348
        }
349
350
        // @codeCoverageIgnoreStart
351
        // Maybe other http methods such as PUT, DELETE, etc...
352
        if ($httpMethod !== 'GET' && !$isForm) {
353
354
            // If it a JSON formatted string?
355
            $isJson = false;
356
357
            // Receive content from PHP stdin input, if exists.
358
            $rawText = file_get_contents('php://input');
359
360
            if (!empty($rawText)) {
361
362
                if ($contentType === 'application/json') {
363
                    $jsonParsedBody = json_decode($rawText);
364
                    $isJson = (json_last_error() === JSON_ERROR_NONE);
365
                }
366
367
                // Condition 1 - It's a JSON, now the body is a JSON object.
368
                if ($isJson) {
369
                    $this->parsedBody = $jsonParsedBody ?: null;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $jsonParsedBody does not seem to be defined for all execution paths leading up to this point.
Loading history...
370
                }
371
372
                // Condition 2 - It's not a JSON, might be a http build query.
373
                if (!$isJson) {
374
                    parse_str($rawText, $parsedStr);
375
                    $this->parsedBody = $parsedStr ?: null;
376
                }
377
            }
378
        }
379
380
        // This part is manually tested by using PostMan.
381
        // @codeCoverageIgnoreEnd
382
    }
383
}
384