GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — 3.x (#2462)
by
unknown
02:01
created

Request::createFromPsr7Request()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.8666
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
/**
3
 * Slim Framework (https://slimframework.com)
4
 *
5
 * @link      https://github.com/slimphp/Slim
6
 * @copyright Copyright (c) 2011-2017 Josh Lockhart
7
 * @license   https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License)
8
 */
9
namespace Slim\Http;
10
11
use Closure;
12
use InvalidArgumentException;
13
use Psr\Http\Message\UploadedFileInterface;
14
use RuntimeException;
15
use Psr\Http\Message\ServerRequestInterface;
16
use Psr\Http\Message\UriInterface;
17
use Psr\Http\Message\StreamInterface;
18
use Slim\Collection;
19
use Slim\Exception\InvalidMethodException;
20
use Slim\Interfaces\Http\HeadersInterface;
21
22
/**
23
 * Request
24
 *
25
 * This class represents an HTTP request. It manages
26
 * the request method, URI, headers, cookies, and body
27
 * according to the PSR-7 standard.
28
 *
29
 * @link https://github.com/php-fig/http-message/blob/master/src/MessageInterface.php
30
 * @link https://github.com/php-fig/http-message/blob/master/src/RequestInterface.php
31
 * @link https://github.com/php-fig/http-message/blob/master/src/ServerRequestInterface.php
32
 */
33
class Request extends Message implements ServerRequestInterface
34
{
35
    /**
36
     * The request method
37
     *
38
     * @var string
39
     */
40
    protected $method;
41
42
    /**
43
     * The original request method (ignoring override)
44
     *
45
     * @var string
46
     */
47
    protected $originalMethod;
48
49
    /**
50
     * The request URI object
51
     *
52
     * @var \Psr\Http\Message\UriInterface
53
     */
54
    protected $uri;
55
56
    /**
57
     * The request URI target (path + query string)
58
     *
59
     * @var string
60
     */
61
    protected $requestTarget;
62
63
    /**
64
     * The request query string params
65
     *
66
     * @var array
67
     */
68
    protected $queryParams;
69
70
    /**
71
     * The request cookies
72
     *
73
     * @var array
74
     */
75
    protected $cookies;
76
77
    /**
78
     * The server environment variables at the time the request was created.
79
     *
80
     * @var array
81
     */
82
    protected $serverParams;
83
84
    /**
85
     * The request attributes (route segment names and values)
86
     *
87
     * @var \Slim\Collection
88
     */
89
    protected $attributes;
90
91
    /**
92
     * The request body parsed (if possible) into a PHP array or object
93
     *
94
     * @var null|array|object
95
     */
96
    protected $bodyParsed = false;
97
98
    /**
99
     * List of request body parsers (e.g., url-encoded, JSON, XML, multipart)
100
     *
101
     * @var callable[]
102
     */
103
    protected $bodyParsers = [];
104
105
    /**
106
     * List of uploaded files
107
     *
108
     * @var UploadedFileInterface[]
109
     */
110
    protected $uploadedFiles;
111
112
    /**
113
     * Valid request methods
114
     *
115
     * @var string[]
116
     * @deprecated
117
     */
118
    protected $validMethods = [
119
        'CONNECT' => 1,
120
        'DELETE' => 1,
121
        'GET' => 1,
122
        'HEAD' => 1,
123
        'OPTIONS' => 1,
124
        'PATCH' => 1,
125
        'POST' => 1,
126
        'PUT' => 1,
127
        'TRACE' => 1,
128
    ];
129
130
    /**
131
     * Create new HTTP request with data extracted from the application
132
     * Environment object
133
     *
134
     * @param  Environment $environment The Slim application Environment
135
     *
136
     * @return static
137
     */
138
    public static function createFromEnvironment(Environment $environment)
0 ignored issues
show
Coding Style introduced by
createFromEnvironment uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
139
    {
140
        $method = $environment['REQUEST_METHOD'];
141
        $uri = Uri::createFromEnvironment($environment);
142
        $headers = Headers::createFromEnvironment($environment);
143
        $cookies = Cookies::parseHeader($headers->get('Cookie', []));
0 ignored issues
show
Documentation introduced by
$headers->get('Cookie', array()) is of type array<integer,string>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
144
        $serverParams = $environment->all();
145
        $body = new RequestBody();
146
        $uploadedFiles = UploadedFile::createFromEnvironment($environment);
147
148
        $request = new static($method, $uri, $headers, $cookies, $serverParams, $body, $uploadedFiles);
0 ignored issues
show
Bug introduced by
It seems like $uploadedFiles defined by \Slim\Http\UploadedFile:...vironment($environment) on line 146 can also be of type null; however, Slim\Http\Request::__construct() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
149
150
        if ($method === 'POST' &&
151
            in_array($request->getMediaType(), ['application/x-www-form-urlencoded', 'multipart/form-data'])
152
        ) {
153
            // parsed body must be $_POST
154
            $request = $request->withParsedBody($_POST);
155
        }
156
        return $request;
157
    }
158
159
    /**
160
     * Create new HTTP request form PSR-7 request
161
     *
162
     * @param  ServerRequestInterface $request The PSR-7 request
163
     *
164
     * @return static
165
     */
166
    public static function createFromPsr7Request(ServerRequestInterface $request)
167
    {
168
        return new static(
169
            $request->getMethod(),
170
            $request->getUri(),
171
            new Headers($request->getHeaders()),
172
            $request->getCookieParams(),
173
            $request->getServerParams(),
174
            $request->getBody(),
175
            $request->getUploadedFiles()
176
        );
177
    }
178
179
    /**
180
     * Create new HTTP request.
181
     *
182
     * Adds a host header when none was provided and a host is defined in uri.
183
     *
184
     * @param string           $method        The request method
185
     * @param UriInterface     $uri           The request URI object
186
     * @param HeadersInterface $headers       The request headers collection
187
     * @param array            $cookies       The request cookies collection
188
     * @param array            $serverParams  The server environment variables
189
     * @param StreamInterface  $body          The request body object
190
     * @param array            $uploadedFiles The request uploadedFiles collection
191
     * @throws InvalidMethodException on invalid HTTP method
192
     */
193
    public function __construct(
194
        $method,
195
        UriInterface $uri,
196
        HeadersInterface $headers,
197
        array $cookies,
198
        array $serverParams,
199
        StreamInterface $body,
200
        array $uploadedFiles = []
201
    ) {
202
        try {
203
            $this->originalMethod = $this->filterMethod($method);
204
        } catch (InvalidMethodException $e) {
205
            $this->originalMethod = $method;
206
        }
207
208
        $this->uri = $uri;
209
        $this->headers = $headers;
210
        $this->cookies = $cookies;
211
        $this->serverParams = $serverParams;
212
        $this->attributes = new Collection();
213
        $this->body = $body;
214
        $this->uploadedFiles = $uploadedFiles;
215
216
        if (isset($serverParams['SERVER_PROTOCOL'])) {
217
            $this->protocolVersion = str_replace('HTTP/', '', $serverParams['SERVER_PROTOCOL']);
218
        }
219
220
        if (!$this->headers->has('Host') && $this->uri->getHost() !== '') {
221
            $port = $this->uri->getPort() ? ":{$this->uri->getPort()}" : '';
222
223
            $this->headers->set('Host', $this->uri->getHost() . $port);
224
        }
225
226
        $this->registerMediaTypeParser('application/json', function ($input) {
227
            $result = json_decode($input, true);
228
            if (!is_array($result)) {
229
                return null;
230
            }
231
            return $result;
232
        });
233
234 View Code Duplication
        $this->registerMediaTypeParser('application/xml', function ($input) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
235
            $backup = libxml_disable_entity_loader(true);
236
            $backup_errors = libxml_use_internal_errors(true);
237
            $result = simplexml_load_string($input);
238
            libxml_disable_entity_loader($backup);
239
            libxml_clear_errors();
240
            libxml_use_internal_errors($backup_errors);
241
            if ($result === false) {
242
                return null;
243
            }
244
            return $result;
245
        });
246
247 View Code Duplication
        $this->registerMediaTypeParser('text/xml', function ($input) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
248
            $backup = libxml_disable_entity_loader(true);
249
            $backup_errors = libxml_use_internal_errors(true);
250
            $result = simplexml_load_string($input);
251
            libxml_disable_entity_loader($backup);
252
            libxml_clear_errors();
253
            libxml_use_internal_errors($backup_errors);
254
            if ($result === false) {
255
                return null;
256
            }
257
            return $result;
258
        });
259
260
        $this->registerMediaTypeParser('application/x-www-form-urlencoded', function ($input) {
261
            parse_str($input, $data);
262
            return $data;
263
        });
264
265
        // if the request had an invalid method, we can throw it now
266
        if (isset($e) && $e instanceof InvalidMethodException) {
267
            throw $e;
268
        }
269
    }
270
271
    /**
272
     * This method is applied to the cloned object
273
     * after PHP performs an initial shallow-copy. This
274
     * method completes a deep-copy by creating new objects
275
     * for the cloned object's internal reference pointers.
276
     */
277
    public function __clone()
278
    {
279
        $this->headers = clone $this->headers;
280
        $this->attributes = clone $this->attributes;
281
        $this->body = clone $this->body;
282
    }
283
284
    /*******************************************************************************
285
     * Method
286
     ******************************************************************************/
287
288
    /**
289
     * Retrieves the HTTP method of the request.
290
     *
291
     * @return string Returns the request method.
292
     */
293
    public function getMethod()
294
    {
295
        if ($this->method === null) {
296
            $this->method = $this->originalMethod;
297
            $customMethod = $this->getHeaderLine('X-Http-Method-Override');
298
299
            if ($customMethod) {
300
                $this->method = $this->filterMethod($customMethod);
301
            } elseif ($this->originalMethod === 'POST') {
302
                $overrideMethod = $this->filterMethod($this->getParsedBodyParam('_METHOD'));
303
                if ($overrideMethod !== null) {
304
                    $this->method = $overrideMethod;
305
                }
306
307
                if ($this->getBody()->eof()) {
308
                    $this->getBody()->rewind();
309
                }
310
            }
311
        }
312
313
        return $this->method;
314
    }
315
316
    /**
317
     * Get the original HTTP method (ignore override).
318
     *
319
     * Note: This method is not part of the PSR-7 standard.
320
     *
321
     * @return string
322
     */
323
    public function getOriginalMethod()
324
    {
325
        return $this->originalMethod;
326
    }
327
328
    /**
329
     * Return an instance with the provided HTTP method.
330
     *
331
     * While HTTP method names are typically all uppercase characters, HTTP
332
     * method names are case-sensitive and thus implementations SHOULD NOT
333
     * modify the given string.
334
     *
335
     * This method MUST be implemented in such a way as to retain the
336
     * immutability of the message, and MUST return an instance that has the
337
     * changed request method.
338
     *
339
     * @param string $method Case-sensitive method.
340
     * @return static
341
     * @throws \InvalidArgumentException for invalid HTTP methods.
342
     */
343
    public function withMethod($method)
344
    {
345
        $method = $this->filterMethod($method);
346
        $clone = clone $this;
347
        $clone->originalMethod = $method;
348
        $clone->method = $method;
349
350
        return $clone;
351
    }
352
353
    /**
354
     * Validate the HTTP method
355
     *
356
     * @param  null|string $method
357
     * @return null|string
358
     * @throws \InvalidArgumentException on invalid HTTP method.
359
     */
360
    protected function filterMethod($method)
361
    {
362
        if ($method === null) {
363
            return $method;
364
        }
365
366
        if (!is_string($method)) {
367
            throw new InvalidArgumentException(sprintf(
368
                'Unsupported HTTP method; must be a string, received %s',
369
                (is_object($method) ? get_class($method) : gettype($method))
370
            ));
371
        }
372
373
        $method = strtoupper($method);
374
        if (preg_match("/^[!#$%&'*+.^_`|~0-9a-z-]+$/i", $method) !== 1) {
375
            throw new InvalidMethodException($this, $method);
376
        }
377
378
        return $method;
379
    }
380
381
    /**
382
     * Does this request use a given method?
383
     *
384
     * Note: This method is not part of the PSR-7 standard.
385
     *
386
     * @param  string $method HTTP method
387
     * @return bool
388
     */
389
    public function isMethod($method)
390
    {
391
        return $this->getMethod() === $method;
392
    }
393
394
    /**
395
     * Is this a GET request?
396
     *
397
     * Note: This method is not part of the PSR-7 standard.
398
     *
399
     * @return bool
400
     */
401
    public function isGet()
402
    {
403
        return $this->isMethod('GET');
404
    }
405
406
    /**
407
     * Is this a POST request?
408
     *
409
     * Note: This method is not part of the PSR-7 standard.
410
     *
411
     * @return bool
412
     */
413
    public function isPost()
414
    {
415
        return $this->isMethod('POST');
416
    }
417
418
    /**
419
     * Is this a PUT request?
420
     *
421
     * Note: This method is not part of the PSR-7 standard.
422
     *
423
     * @return bool
424
     */
425
    public function isPut()
426
    {
427
        return $this->isMethod('PUT');
428
    }
429
430
    /**
431
     * Is this a PATCH request?
432
     *
433
     * Note: This method is not part of the PSR-7 standard.
434
     *
435
     * @return bool
436
     */
437
    public function isPatch()
438
    {
439
        return $this->isMethod('PATCH');
440
    }
441
442
    /**
443
     * Is this a DELETE request?
444
     *
445
     * Note: This method is not part of the PSR-7 standard.
446
     *
447
     * @return bool
448
     */
449
    public function isDelete()
450
    {
451
        return $this->isMethod('DELETE');
452
    }
453
454
    /**
455
     * Is this a HEAD request?
456
     *
457
     * Note: This method is not part of the PSR-7 standard.
458
     *
459
     * @return bool
460
     */
461
    public function isHead()
462
    {
463
        return $this->isMethod('HEAD');
464
    }
465
466
    /**
467
     * Is this a OPTIONS request?
468
     *
469
     * Note: This method is not part of the PSR-7 standard.
470
     *
471
     * @return bool
472
     */
473
    public function isOptions()
474
    {
475
        return $this->isMethod('OPTIONS');
476
    }
477
478
    /**
479
     * Is this an XHR request?
480
     *
481
     * Note: This method is not part of the PSR-7 standard.
482
     *
483
     * @return bool
484
     */
485
    public function isXhr()
486
    {
487
        return $this->getHeaderLine('X-Requested-With') === 'XMLHttpRequest';
488
    }
489
490
    /*******************************************************************************
491
     * URI
492
     ******************************************************************************/
493
494
    /**
495
     * Retrieves the message's request target.
496
     *
497
     * Retrieves the message's request-target either as it will appear (for
498
     * clients), as it appeared at request (for servers), or as it was
499
     * specified for the instance (see withRequestTarget()).
500
     *
501
     * In most cases, this will be the origin-form of the composed URI,
502
     * unless a value was provided to the concrete implementation (see
503
     * withRequestTarget() below).
504
     *
505
     * If no URI is available, and no request-target has been specifically
506
     * provided, this method MUST return the string "/".
507
     *
508
     * @return string
509
     */
510
    public function getRequestTarget()
511
    {
512
        if ($this->requestTarget) {
513
            return $this->requestTarget;
514
        }
515
516
        if ($this->uri === null) {
517
            return '/';
518
        }
519
520
        $basePath = $this->uri->getBasePath();
521
        $path = $this->uri->getPath();
522
        $path = $basePath . '/' . ltrim($path, '/');
523
524
        $query = $this->uri->getQuery();
525
        if ($query) {
526
            $path .= '?' . $query;
527
        }
528
        $this->requestTarget = $path;
529
530
        return $this->requestTarget;
531
    }
532
533
    /**
534
     * Return an instance with the specific request-target.
535
     *
536
     * If the request needs a non-origin-form request-target — e.g., for
537
     * specifying an absolute-form, authority-form, or asterisk-form —
538
     * this method may be used to create an instance with the specified
539
     * request-target, verbatim.
540
     *
541
     * This method MUST be implemented in such a way as to retain the
542
     * immutability of the message, and MUST return an instance that has the
543
     * changed request target.
544
     *
545
     * @link http://tools.ietf.org/html/rfc7230#section-2.7 (for the various
546
     *     request-target forms allowed in request messages)
547
     * @param mixed $requestTarget
548
     * @return static
549
     * @throws InvalidArgumentException if the request target is invalid
550
     */
551
    public function withRequestTarget($requestTarget)
552
    {
553
        if (preg_match('#\s#', $requestTarget)) {
554
            throw new InvalidArgumentException(
555
                'Invalid request target provided; must be a string and cannot contain whitespace'
556
            );
557
        }
558
        $clone = clone $this;
559
        $clone->requestTarget = $requestTarget;
560
561
        return $clone;
562
    }
563
564
    /**
565
     * Retrieves the URI instance.
566
     *
567
     * This method MUST return a UriInterface instance.
568
     *
569
     * @link http://tools.ietf.org/html/rfc3986#section-4.3
570
     * @return UriInterface Returns a UriInterface instance
571
     *     representing the URI of the request.
572
     */
573
    public function getUri()
574
    {
575
        return $this->uri;
576
    }
577
578
    /**
579
     * Returns an instance with the provided URI.
580
     *
581
     * This method MUST update the Host header of the returned request by
582
     * default if the URI contains a host component. If the URI does not
583
     * contain a host component, any pre-existing Host header MUST be carried
584
     * over to the returned request.
585
     *
586
     * You can opt-in to preserving the original state of the Host header by
587
     * setting `$preserveHost` to `true`. When `$preserveHost` is set to
588
     * `true`, this method interacts with the Host header in the following ways:
589
     *
590
     * - If the the Host header is missing or empty, and the new URI contains
591
     *   a host component, this method MUST update the Host header in the returned
592
     *   request.
593
     * - If the Host header is missing or empty, and the new URI does not contain a
594
     *   host component, this method MUST NOT update the Host header in the returned
595
     *   request.
596
     * - If a Host header is present and non-empty, this method MUST NOT update
597
     *   the Host header in the returned request.
598
     *
599
     * This method MUST be implemented in such a way as to retain the
600
     * immutability of the message, and MUST return an instance that has the
601
     * new UriInterface instance.
602
     *
603
     * @link http://tools.ietf.org/html/rfc3986#section-4.3
604
     * @param UriInterface $uri New request URI to use.
605
     * @param bool $preserveHost Preserve the original state of the Host header.
606
     * @return static
607
     */
608
    public function withUri(UriInterface $uri, $preserveHost = false)
609
    {
610
        $clone = clone $this;
611
        $clone->uri = $uri;
612
613
        if (!$preserveHost) {
614
            if ($uri->getHost() !== '') {
615
                $clone->headers->set('Host', $uri->getHost());
616
            }
617
        } else {
618
            if ($uri->getHost() !== '' && (!$this->hasHeader('Host') || $this->getHeaderLine('Host') === '')) {
619
                $clone->headers->set('Host', $uri->getHost());
620
            }
621
        }
622
623
        return $clone;
624
    }
625
626
    /**
627
     * Get request content type.
628
     *
629
     * Note: This method is not part of the PSR-7 standard.
630
     *
631
     * @return string|null The request content type, if known
632
     */
633
    public function getContentType()
634
    {
635
        $result = $this->getHeader('Content-Type');
636
637
        return $result ? $result[0] : null;
638
    }
639
640
    /**
641
     * Get request media type, if known.
642
     *
643
     * Note: This method is not part of the PSR-7 standard.
644
     *
645
     * @return string|null The request media type, minus content-type params
646
     */
647
    public function getMediaType()
648
    {
649
        $contentType = $this->getContentType();
650
        if ($contentType) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $contentType of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
651
            $contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType);
652
653
            return strtolower($contentTypeParts[0]);
654
        }
655
656
        return null;
657
    }
658
659
    /**
660
     * Get request media type params, if known.
661
     *
662
     * Note: This method is not part of the PSR-7 standard.
663
     *
664
     * @return array
665
     */
666
    public function getMediaTypeParams()
667
    {
668
        $contentType = $this->getContentType();
669
        $contentTypeParams = [];
670
        if ($contentType) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $contentType of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
671
            $contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType);
672
            $contentTypePartsLength = count($contentTypeParts);
673
            for ($i = 1; $i < $contentTypePartsLength; $i++) {
674
                $paramParts = explode('=', $contentTypeParts[$i]);
675
                $contentTypeParams[strtolower($paramParts[0])] = $paramParts[1];
676
            }
677
        }
678
679
        return $contentTypeParams;
680
    }
681
682
    /**
683
     * Get request content character set, if known.
684
     *
685
     * Note: This method is not part of the PSR-7 standard.
686
     *
687
     * @return string|null
688
     */
689
    public function getContentCharset()
690
    {
691
        $mediaTypeParams = $this->getMediaTypeParams();
692
        if (isset($mediaTypeParams['charset'])) {
693
            return $mediaTypeParams['charset'];
694
        }
695
696
        return null;
697
    }
698
699
    /**
700
     * Get request content length, if known.
701
     *
702
     * Note: This method is not part of the PSR-7 standard.
703
     *
704
     * @return int|null
705
     */
706
    public function getContentLength()
707
    {
708
        $result = $this->headers->get('Content-Length');
709
710
        return $result ? (int)$result[0] : null;
711
    }
712
713
    /*******************************************************************************
714
     * Cookies
715
     ******************************************************************************/
716
717
    /**
718
     * Retrieve cookies.
719
     *
720
     * Retrieves cookies sent by the client to the server.
721
     *
722
     * The data MUST be compatible with the structure of the $_COOKIE
723
     * superglobal.
724
     *
725
     * @return array
726
     */
727
    public function getCookieParams()
728
    {
729
        return $this->cookies;
730
    }
731
732
    /**
733
     * Fetch cookie value from cookies sent by the client to the server.
734
     *
735
     * Note: This method is not part of the PSR-7 standard.
736
     *
737
     * @param string $key     The attribute name.
738
     * @param mixed  $default Default value to return if the attribute does not exist.
739
     *
740
     * @return mixed
741
     */
742 View Code Duplication
    public function getCookieParam($key, $default = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
743
    {
744
        $cookies = $this->getCookieParams();
745
        $result = $default;
746
        if (isset($cookies[$key])) {
747
            $result = $cookies[$key];
748
        }
749
750
        return $result;
751
    }
752
753
    /**
754
     * Return an instance with the specified cookies.
755
     *
756
     * The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST
757
     * be compatible with the structure of $_COOKIE. Typically, this data will
758
     * be injected at instantiation.
759
     *
760
     * This method MUST NOT update the related Cookie header of the request
761
     * instance, nor related values in the server params.
762
     *
763
     * This method MUST be implemented in such a way as to retain the
764
     * immutability of the message, and MUST return an instance that has the
765
     * updated cookie values.
766
     *
767
     * @param array $cookies Array of key/value pairs representing cookies.
768
     * @return static
769
     */
770
    public function withCookieParams(array $cookies)
771
    {
772
        $clone = clone $this;
773
        $clone->cookies = $cookies;
774
775
        return $clone;
776
    }
777
778
    /*******************************************************************************
779
     * Query Params
780
     ******************************************************************************/
781
782
    /**
783
     * Retrieve query string arguments.
784
     *
785
     * Retrieves the deserialized query string arguments, if any.
786
     *
787
     * Note: the query params might not be in sync with the URI or server
788
     * params. If you need to ensure you are only getting the original
789
     * values, you may need to parse the query string from `getUri()->getQuery()`
790
     * or from the `QUERY_STRING` server param.
791
     *
792
     * @return array
793
     */
794
    public function getQueryParams()
795
    {
796
        if (is_array($this->queryParams)) {
797
            return $this->queryParams;
798
        }
799
800
        if ($this->uri === null) {
801
            return [];
802
        }
803
804
        parse_str($this->uri->getQuery(), $this->queryParams); // <-- URL decodes data
805
806
        return $this->queryParams;
807
    }
808
809
    /**
810
     * Return an instance with the specified query string arguments.
811
     *
812
     * These values SHOULD remain immutable over the course of the incoming
813
     * request. They MAY be injected during instantiation, such as from PHP's
814
     * $_GET superglobal, or MAY be derived from some other value such as the
815
     * URI. In cases where the arguments are parsed from the URI, the data
816
     * MUST be compatible with what PHP's parse_str() would return for
817
     * purposes of how duplicate query parameters are handled, and how nested
818
     * sets are handled.
819
     *
820
     * Setting query string arguments MUST NOT change the URI stored by the
821
     * request, nor the values in the server params.
822
     *
823
     * This method MUST be implemented in such a way as to retain the
824
     * immutability of the message, and MUST return an instance that has the
825
     * updated query string arguments.
826
     *
827
     * @param array $query Array of query string arguments, typically from
828
     *     $_GET.
829
     * @return static
830
     */
831
    public function withQueryParams(array $query)
832
    {
833
        $clone = clone $this;
834
        $clone->queryParams = $query;
835
836
        return $clone;
837
    }
838
839
    /*******************************************************************************
840
     * File Params
841
     ******************************************************************************/
842
843
    /**
844
     * Retrieve normalized file upload data.
845
     *
846
     * This method returns upload metadata in a normalized tree, with each leaf
847
     * an instance of Psr\Http\Message\UploadedFileInterface.
848
     *
849
     * These values MAY be prepared from $_FILES or the message body during
850
     * instantiation, or MAY be injected via withUploadedFiles().
851
     *
852
     * @return array An array tree of UploadedFileInterface instances; an empty
853
     *     array MUST be returned if no data is present.
854
     */
855
    public function getUploadedFiles()
856
    {
857
        return $this->uploadedFiles;
858
    }
859
860
    /**
861
     * Create a new instance with the specified uploaded files.
862
     *
863
     * This method MUST be implemented in such a way as to retain the
864
     * immutability of the message, and MUST return an instance that has the
865
     * updated body parameters.
866
     *
867
     * @param array $uploadedFiles An array tree of UploadedFileInterface instances.
868
     * @return static
869
     * @throws \InvalidArgumentException if an invalid structure is provided.
870
     */
871
    public function withUploadedFiles(array $uploadedFiles)
872
    {
873
        $clone = clone $this;
874
        $clone->uploadedFiles = $uploadedFiles;
875
876
        return $clone;
877
    }
878
879
    /*******************************************************************************
880
     * Server Params
881
     ******************************************************************************/
882
883
    /**
884
     * Retrieve server parameters.
885
     *
886
     * Retrieves data related to the incoming request environment,
887
     * typically derived from PHP's $_SERVER superglobal. The data IS NOT
888
     * REQUIRED to originate from $_SERVER.
889
     *
890
     * @return array
891
     */
892
    public function getServerParams()
893
    {
894
        return $this->serverParams;
895
    }
896
897
    /**
898
     * Retrieve a server parameter.
899
     *
900
     * Note: This method is not part of the PSR-7 standard.
901
     *
902
     * @param  string $key
903
     * @param  mixed  $default
904
     * @return mixed
905
     */
906
    public function getServerParam($key, $default = null)
907
    {
908
        $serverParams = $this->getServerParams();
909
910
        return isset($serverParams[$key]) ? $serverParams[$key] : $default;
911
    }
912
913
    /*******************************************************************************
914
     * Attributes
915
     ******************************************************************************/
916
917
    /**
918
     * Retrieve attributes derived from the request.
919
     *
920
     * The request "attributes" may be used to allow injection of any
921
     * parameters derived from the request: e.g., the results of path
922
     * match operations; the results of decrypting cookies; the results of
923
     * deserializing non-form-encoded message bodies; etc. Attributes
924
     * will be application and request specific, and CAN be mutable.
925
     *
926
     * @return array Attributes derived from the request.
927
     */
928
    public function getAttributes()
929
    {
930
        return $this->attributes->all();
931
    }
932
933
    /**
934
     * Retrieve a single derived request attribute.
935
     *
936
     * Retrieves a single derived request attribute as described in
937
     * getAttributes(). If the attribute has not been previously set, returns
938
     * the default value as provided.
939
     *
940
     * This method obviates the need for a hasAttribute() method, as it allows
941
     * specifying a default value to return if the attribute is not found.
942
     *
943
     * @see getAttributes()
944
     * @param string $name The attribute name.
945
     * @param mixed $default Default value to return if the attribute does not exist.
946
     * @return mixed
947
     */
948
    public function getAttribute($name, $default = null)
949
    {
950
        return $this->attributes->get($name, $default);
951
    }
952
953
    /**
954
     * Return an instance with the specified derived request attribute.
955
     *
956
     * This method allows setting a single derived request attribute as
957
     * described in getAttributes().
958
     *
959
     * This method MUST be implemented in such a way as to retain the
960
     * immutability of the message, and MUST return an instance that has the
961
     * updated attribute.
962
     *
963
     * @see getAttributes()
964
     * @param string $name The attribute name.
965
     * @param mixed $value The value of the attribute.
966
     * @return static
967
     */
968
    public function withAttribute($name, $value)
969
    {
970
        $clone = clone $this;
971
        $clone->attributes->set($name, $value);
972
973
        return $clone;
974
    }
975
976
    /**
977
     * Create a new instance with the specified derived request attributes.
978
     *
979
     * Note: This method is not part of the PSR-7 standard.
980
     *
981
     * This method allows setting all new derived request attributes as
982
     * described in getAttributes().
983
     *
984
     * This method MUST be implemented in such a way as to retain the
985
     * immutability of the message, and MUST return a new instance that has the
986
     * updated attributes.
987
     *
988
     * @param  array $attributes New attributes
989
     * @return static
990
     */
991
    public function withAttributes(array $attributes)
992
    {
993
        $clone = clone $this;
994
        $clone->attributes = new Collection($attributes);
995
996
        return $clone;
997
    }
998
999
    /**
1000
     * Return an instance that removes the specified derived request attribute.
1001
     *
1002
     * This method allows removing a single derived request attribute as
1003
     * described in getAttributes().
1004
     *
1005
     * This method MUST be implemented in such a way as to retain the
1006
     * immutability of the message, and MUST return an instance that removes
1007
     * the attribute.
1008
     *
1009
     * @see getAttributes()
1010
     * @param string $name The attribute name.
1011
     * @return static
1012
     */
1013
    public function withoutAttribute($name)
1014
    {
1015
        $clone = clone $this;
1016
        $clone->attributes->remove($name);
1017
1018
        return $clone;
1019
    }
1020
1021
    /*******************************************************************************
1022
     * Body
1023
     ******************************************************************************/
1024
1025
    /**
1026
     * Retrieve any parameters provided in the request body.
1027
     *
1028
     * If the request Content-Type is either application/x-www-form-urlencoded
1029
     * or multipart/form-data, and the request method is POST, this method MUST
1030
     * return the contents of $_POST.
1031
     *
1032
     * Otherwise, this method may return any results of deserializing
1033
     * the request body content; as parsing returns structured content, the
1034
     * potential types MUST be arrays or objects only. A null value indicates
1035
     * the absence of body content.
1036
     *
1037
     * @return null|array|object The deserialized body parameters, if any.
1038
     *     These will typically be an array or object.
1039
     * @throws RuntimeException if the request body media type parser returns an invalid value
1040
     */
1041
    public function getParsedBody()
1042
    {
1043
        if ($this->bodyParsed !== false) {
1044
            return $this->bodyParsed;
1045
        }
1046
1047
        if (!$this->body) {
1048
            return null;
1049
        }
1050
1051
        $mediaType = $this->getMediaType();
1052
1053
        // look for a media type with a structured syntax suffix (RFC 6839)
1054
        $parts = explode('+', $mediaType);
1055
        if (count($parts) >= 2) {
1056
            $mediaType = 'application/' . $parts[count($parts)-1];
1057
        }
1058
1059
        if (isset($this->bodyParsers[$mediaType]) === true) {
1060
            $body = (string)$this->getBody();
1061
            $parsed = $this->bodyParsers[$mediaType]($body);
1062
1063
            if (!is_null($parsed) && !is_object($parsed) && !is_array($parsed)) {
1064
                throw new RuntimeException(
1065
                    'Request body media type parser return value must be an array, an object, or null'
1066
                );
1067
            }
1068
            $this->bodyParsed = $parsed;
1069
            return $this->bodyParsed;
1070
        }
1071
1072
        return null;
1073
    }
1074
1075
    /**
1076
     * Return an instance with the specified body parameters.
1077
     *
1078
     * These MAY be injected during instantiation.
1079
     *
1080
     * If the request Content-Type is either application/x-www-form-urlencoded
1081
     * or multipart/form-data, and the request method is POST, use this method
1082
     * ONLY to inject the contents of $_POST.
1083
     *
1084
     * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of
1085
     * deserializing the request body content. Deserialization/parsing returns
1086
     * structured data, and, as such, this method ONLY accepts arrays or objects,
1087
     * or a null value if nothing was available to parse.
1088
     *
1089
     * As an example, if content negotiation determines that the request data
1090
     * is a JSON payload, this method could be used to create a request
1091
     * instance with the deserialized parameters.
1092
     *
1093
     * This method MUST be implemented in such a way as to retain the
1094
     * immutability of the message, and MUST return an instance that has the
1095
     * updated body parameters.
1096
     *
1097
     * @param null|array|object $data The deserialized body data. This will
1098
     *     typically be in an array or object.
1099
     * @return static
1100
     * @throws \InvalidArgumentException if an unsupported argument type is
1101
     *     provided.
1102
     */
1103
    public function withParsedBody($data)
1104
    {
1105
        if (!is_null($data) && !is_object($data) && !is_array($data)) {
1106
            throw new InvalidArgumentException('Parsed body value must be an array, an object, or null');
1107
        }
1108
1109
        $clone = clone $this;
1110
        $clone->bodyParsed = $data;
1111
1112
        return $clone;
1113
    }
1114
1115
    /**
1116
     * Force Body to be parsed again.
1117
     *
1118
     * Note: This method is not part of the PSR-7 standard.
1119
     *
1120
     * @return $this
1121
     */
1122
    public function reparseBody()
1123
    {
1124
        $this->bodyParsed = false;
0 ignored issues
show
Documentation Bug introduced by
It seems like false of type false is incompatible with the declared type null|array|object of property $bodyParsed.

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...
1125
1126
        return $this;
1127
    }
1128
1129
    /**
1130
     * Register media type parser.
1131
     *
1132
     * Note: This method is not part of the PSR-7 standard.
1133
     *
1134
     * @param string   $mediaType A HTTP media type (excluding content-type
1135
     *     params).
1136
     * @param callable $callable  A callable that returns parsed contents for
1137
     *     media type.
1138
     */
1139
    public function registerMediaTypeParser($mediaType, callable $callable)
1140
    {
1141
        if ($callable instanceof Closure) {
1142
            $callable = $callable->bindTo($this);
1143
        }
1144
        $this->bodyParsers[(string)$mediaType] = $callable;
1145
    }
1146
1147
    /*******************************************************************************
1148
     * Parameters (e.g., POST and GET data)
1149
     ******************************************************************************/
1150
1151
    /**
1152
     * Fetch request parameter value from body or query string (in that order).
1153
     *
1154
     * Note: This method is not part of the PSR-7 standard.
1155
     *
1156
     * @param  string $key The parameter key.
1157
     * @param  mixed $default The default value.
1158
     *
1159
     * @return mixed The parameter value.
1160
     */
1161
    public function getParam($key, $default = null)
1162
    {
1163
        $postParams = $this->getParsedBody();
1164
        $getParams = $this->getQueryParams();
1165
        $result = $default;
1166
        if (is_array($postParams) && isset($postParams[$key])) {
1167
            $result = $postParams[$key];
1168
        } elseif (is_object($postParams) && property_exists($postParams, $key)) {
1169
            $result = $postParams->$key;
1170
        } elseif (isset($getParams[$key])) {
1171
            $result = $getParams[$key];
1172
        }
1173
1174
        return $result;
1175
    }
1176
1177
    /**
1178
     * Fetch parameter value from request body.
1179
     *
1180
     * Note: This method is not part of the PSR-7 standard.
1181
     *
1182
     * @param string $key
1183
     * @param mixed $default
1184
     *
1185
     * @return mixed
1186
     */
1187
    public function getParsedBodyParam($key, $default = null)
1188
    {
1189
        $postParams = $this->getParsedBody();
1190
        $result = $default;
1191
        if (is_array($postParams) && isset($postParams[$key])) {
1192
            $result = $postParams[$key];
1193
        } elseif (is_object($postParams) && property_exists($postParams, $key)) {
1194
            $result = $postParams->$key;
1195
        }
1196
1197
        return $result;
1198
    }
1199
1200
    /**
1201
     * Fetch parameter value from query string.
1202
     *
1203
     * Note: This method is not part of the PSR-7 standard.
1204
     *
1205
     * @param string $key
1206
     * @param mixed $default
1207
     *
1208
     * @return mixed
1209
     */
1210 View Code Duplication
    public function getQueryParam($key, $default = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1211
    {
1212
        $getParams = $this->getQueryParams();
1213
        $result = $default;
1214
        if (isset($getParams[$key])) {
1215
            $result = $getParams[$key];
1216
        }
1217
1218
        return $result;
1219
    }
1220
1221
    /**
1222
     * Fetch associative array of body and query string parameters.
1223
     *
1224
     * Note: This method is not part of the PSR-7 standard.
1225
     *
1226
     * @param array|null $only list the keys to retrieve.
1227
     * @return array|null
1228
     */
1229
    public function getParams(array $only = null)
1230
    {
1231
        $params = $this->getQueryParams();
1232
        $postParams = $this->getParsedBody();
1233
        if ($postParams) {
1234
            $params = array_replace($params, (array)$postParams);
1235
        }
1236
1237
        if ($only) {
1238
            $onlyParams = [];
1239
            foreach ($only as $key) {
1240
                if (array_key_exists($key, $params)) {
1241
                    $onlyParams[$key] = $params[$key];
1242
                }
1243
            }
1244
            return $onlyParams;
1245
        }
1246
1247
        return $params;
1248
    }
1249
}
1250