ServerRequest::__construct()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 28
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 10
c 1
b 0
f 0
dl 0
loc 28
ccs 11
cts 11
cp 1
rs 9.9332
cc 2
nc 2
nop 10
crap 2

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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 11
    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 11
        parent::__construct($method, $uri, $body, $headers, $version);
115
116 11
        $this->serverParams = $serverParams;
117 11
        $this->cookieParams = $cookieParams;
118 11
        $this->queryParams  = $getParams;
119 11
        $this->attributes   = [];
120
121 11
        $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 11
        $this->uploadedFiles = [];
126
127 11
        if (!empty($filesParams)) {
128 2
            $this->uploadedFiles = UploadedFileHelper::uploadedFileSpecsConvert(
129 2
                UploadedFileHelper::uploadedFileParse($filesParams)
130 2
            );
131
        }
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137 2
    public function getServerParams(): array
138
    {
139 2
        return $this->serverParams;
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145 2
    public function getCookieParams(): array
146
    {
147 2
        return $this->cookieParams;
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153 1
    public function withCookieParams(array $cookies): ServerRequestInterface
154
    {
155 1
        $clone = clone $this;
156 1
        $clone->cookieParams = $cookies;
157
158 1
        return $clone;
159
    }
160
161
    /**
162
     * {@inheritdoc}
163
     */
164 2
    public function getQueryParams(): array
165
    {
166 2
        return $this->queryParams;
167
    }
168
169
    /**
170
     * {@inheritdoc}
171
     */
172 1
    public function withQueryParams(array $query): ServerRequestInterface
173
    {
174 1
        $clone = clone $this;
175 1
        $clone->queryParams = $query;
176
177 1
        return $clone;
178
    }
179
180
    /**
181
     * {@inheritdoc}
182
     */
183 2
    public function getUploadedFiles(): array
184
    {
185 2
        return $this->uploadedFiles;
186
    }
187
188
    /**
189
     * {@inheritdoc}
190
     */
191 1
    public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface
192
    {
193 1
        $this->assertUploadedFiles($uploadedFiles);
194
195 1
        $clone = clone $this;
196 1
        $clone->uploadedFiles = $uploadedFiles;
197
198 1
        return $clone;
199
    }
200
201
    /**
202
     * {@inheritdoc}
203
     */
204 2
    public function getParsedBody()
205
    {
206 2
        return $this->parsedBody;
207
    }
208
209
    /**
210
     * {@inheritdoc}
211
     */
212 1
    public function withParsedBody($data): ServerRequestInterface
213
    {
214 1
        $this->assertParsedBody($data);
215
216 1
        $clone = clone $this;
217 1
        $clone->parsedBody = $data;
218
219 1
        return $clone;
220
    }
221
222
    /**
223
     * {@inheritdoc}
224
     */
225 1
    public function getAttributes(): array
226
    {
227 1
        return $this->attributes;
228
    }
229
230
    /**
231
     * {@inheritdoc}
232
     */
233 2
    public function getAttribute($name, $default = null)
234
    {
235 2
        return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
236
    }
237
238
    /**
239
     * {@inheritdoc}
240
     */
241 3
    public function withAttribute($name, $value): ServerRequestInterface
242
    {
243 3
        $clone = clone $this;
244 3
        $clone->attributes[$name] = $value;
245
246 3
        return $clone;
247
    }
248
249
    /**
250
     * {@inheritdoc}
251
     */
252 1
    public function withoutAttribute($name): ServerRequestInterface
253
    {
254 1
        $clone = clone $this;
255
256 1
        if (isset($this->attributes[$name])) {
257 1
            unset($clone->attributes[$name]);
258
        }
259
260 1
        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 2
    protected function assertUploadedFiles(array $values): void
279
    {
280 2
        foreach ($values as $value) {
281 2
            if (is_array($value)) {
282 1
                $this->assertUploadedFiles($value);
283 2
            } elseif (!($value instanceof UploadedFileInterface)) {
284 1
                throw new InvalidArgumentException(
285 1
                    'Invalid PSR-7 array structure for handling UploadedFile.'
286 1
                );
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 2
    protected function assertParsedBody($data): void
302
    {
303
        if (
304 2
            ! is_null($data) &&
305 2
            ! is_array($data) && 
306 2
            ! is_object($data)
307
        ) {
308 1
            throw new InvalidArgumentException(
309 1
                sprintf(
310 1
                    'Only accepts array, object and null, but "%s" provided.',
311 1
                    gettype($data)
312 1
                )
313 1
            );
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 11
    protected function determineParsedBody(array $postParams)
324
    {
325 11
        $headerContentType = $this->getHeaderLine('Content-Type');
326 11
        $contentTypeArr = preg_split('/\s*[;,]\s*/', $headerContentType);
327 11
        $contentType = strtolower($contentTypeArr[0]);
328 11
        $httpMethod = strtoupper($this->getMethod());
329
330
        // Is it a form submit or not.
331 11
        $isForm = false;
332
333 11
        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 1
            $postRequiredContentTypes = [
339 1
                '', // For unit testing purpose.
340 1
                'application/x-www-form-urlencoded',
341 1
                'multipart/form-data',
342 1
            ];
343
344 1
            if (in_array($contentType, $postRequiredContentTypes)) {
345 1
                $this->parsedBody = $postParams ?? null;
346 1
                $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