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 (#1928)
by
unknown
04:20
created

Request::getCookieParam()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 9
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 9
loc 9
rs 9.6666
cc 2
eloc 6
nc 2
nop 2
1
<?php
2
/**
3
 * Slim Framework (http://slimframework.com)
4
 *
5
 * @link      https://github.com/slimphp/Slim
6
 * @copyright Copyright (c) 2011-2016 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\Interfaces\Http\HeadersInterface;
20
21
/**
22
 * Request
23
 *
24
 * This class represents an HTTP request. It manages
25
 * the request method, URI, headers, cookies, and body
26
 * according to the PSR-7 standard.
27
 *
28
 * @link https://github.com/php-fig/http-message/blob/master/src/MessageInterface.php
29
 * @link https://github.com/php-fig/http-message/blob/master/src/RequestInterface.php
30
 * @link https://github.com/php-fig/http-message/blob/master/src/ServerRequestInterface.php
31
 */
32
class Request extends Message implements ServerRequestInterface
33
{
34
    /**
35
     * The request method
36
     *
37
     * @var string
38
     */
39
    protected $method;
40
41
    /**
42
     * The original request method (ignoring override)
43
     *
44
     * @var string
45
     */
46
    protected $originalMethod;
47
48
    /**
49
     * The request URI object
50
     *
51
     * @var \Psr\Http\Message\UriInterface
52
     */
53
    protected $uri;
54
55
    /**
56
     * The request URI target (path + query string)
57
     *
58
     * @var string
59
     */
60
    protected $requestTarget;
61
62
    /**
63
     * The request query string params
64
     *
65
     * @var array
66
     */
67
    protected $queryParams;
68
69
    /**
70
     * The request cookies
71
     *
72
     * @var array
73
     */
74
    protected $cookies;
75
76
    /**
77
     * The server environment variables at the time the request was created.
78
     *
79
     * @var array
80
     */
81
    protected $serverParams;
82
83
    /**
84
     * The request attributes (route segment names and values)
85
     *
86
     * @var \Slim\Collection
87
     */
88
    protected $attributes;
89
90
    /**
91
     * The request body parsed (if possible) into a PHP array or object
92
     *
93
     * @var null|array|object
94
     */
95
    protected $bodyParsed = false;
96
97
    /**
98
     * List of request body parsers (e.g., url-encoded, JSON, XML, multipart)
99
     *
100
     * @var callable[]
101
     */
102
    protected $bodyParsers = [];
103
104
    /**
105
     * List of uploaded files
106
     *
107
     * @var UploadedFileInterface[]
108
     */
109
    protected $uploadedFiles;
110
111
    /**
112
     * Valid request methods
113
     *
114
     * @var string[]
115
     */
116
    protected $validMethods = [
117
        'CONNECT' => 1,
118
        'DELETE' => 1,
119
        'GET' => 1,
120
        'HEAD' => 1,
121
        'OPTIONS' => 1,
122
        'PATCH' => 1,
123
        'POST' => 1,
124
        'PUT' => 1,
125
        'TRACE' => 1,
126
    ];
127
128
    /**
129
     * Create new HTTP request with data extracted from the application
130
     * Environment object
131
     *
132
     * @param  Environment $environment The Slim application Environment
133
     *
134
     * @return self
135
     */
136
    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...
137
    {
138
        $method = $environment['REQUEST_METHOD'];
139
        $uri = Uri::createFromEnvironment($environment);
140
        $headers = Headers::createFromEnvironment($environment);
141
        $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...
142
        $serverParams = $environment->all();
143
        $body = new RequestBody();
144
        $uploadedFiles = UploadedFile::createFromEnvironment($environment);
145
146
        $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 144 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...
147
148
        if ($method === 'POST' &&
149
            in_array($request->getMediaType(), ['application/x-www-form-urlencoded', 'multipart/form-data'])
150
        ) {
151
            // parsed body must be $_POST
152
            $request = $request->withParsedBody($_POST);
153
        }
154
        return $request;
155
    }
156
157
    /**
158
     * Create new HTTP request.
159
     *
160
     * Adds a host header when none was provided and a host is defined in uri.
161
     *
162
     * @param string           $method        The request method
163
     * @param UriInterface     $uri           The request URI object
164
     * @param HeadersInterface $headers       The request headers collection
165
     * @param array            $cookies       The request cookies collection
166
     * @param array            $serverParams  The server environment variables
167
     * @param StreamInterface  $body          The request body object
168
     * @param array            $uploadedFiles The request uploadedFiles collection
169
     */
170
    public function __construct(
171
        $method,
172
        UriInterface $uri,
173
        HeadersInterface $headers,
174
        array $cookies,
175
        array $serverParams,
176
        StreamInterface $body,
177
        array $uploadedFiles = []
178
    ) {
179
        $this->originalMethod = $this->filterMethod($method);
180
        $this->uri = $uri;
181
        $this->headers = $headers;
182
        $this->cookies = $cookies;
183
        $this->serverParams = $serverParams;
184
        $this->attributes = new Collection();
185
        $this->body = $body;
186
        $this->uploadedFiles = $uploadedFiles;
187
188
        if (isset($serverParams['SERVER_PROTOCOL'])) {
189
            $this->protocolVersion = str_replace('HTTP/', '', $serverParams['SERVER_PROTOCOL']);
190
        }
191
192
        if (!$this->headers->has('Host') || $this->uri->getHost() !== '') {
193
            $this->headers->set('Host', $this->uri->getHost());
194
        }
195
196
        $this->registerMediaTypeParser('application/json', function ($input) {
197
            return json_decode($input, true);
198
        });
199
200 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...
201
            $backup = libxml_disable_entity_loader(true);
202
            $result = simplexml_load_string($input);
203
            libxml_disable_entity_loader($backup);
204
            return $result;
205
        });
206
207 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...
208
            $backup = libxml_disable_entity_loader(true);
209
            $result = simplexml_load_string($input);
210
            libxml_disable_entity_loader($backup);
211
            return $result;
212
        });
213
214
        $this->registerMediaTypeParser('application/x-www-form-urlencoded', function ($input) {
215
            parse_str($input, $data);
216
            return $data;
217
        });
218
    }
219
220
    /**
221
     * This method is applied to the cloned object
222
     * after PHP performs an initial shallow-copy. This
223
     * method completes a deep-copy by creating new objects
224
     * for the cloned object's internal reference pointers.
225
     */
226
    public function __clone()
227
    {
228
        $this->headers = clone $this->headers;
229
        $this->attributes = clone $this->attributes;
230
        $this->body = clone $this->body;
231
    }
232
233
    /*******************************************************************************
234
     * Method
235
     ******************************************************************************/
236
237
    /**
238
     * Retrieves the HTTP method of the request.
239
     *
240
     * @return string Returns the request method.
241
     */
242
    public function getMethod()
243
    {
244
        if ($this->method === null) {
245
            $this->method = $this->originalMethod;
246
            $customMethod = $this->getHeaderLine('X-Http-Method-Override');
247
248
            if ($customMethod) {
249
                $this->method = $this->filterMethod($customMethod);
250
            } elseif ($this->originalMethod === 'POST') {
251
                $body = $this->getParsedBody();
252
253
                if (is_object($body) && property_exists($body, '_METHOD')) {
254
                    $this->method = $this->filterMethod((string)$body->_METHOD);
255
                } elseif (is_array($body) && isset($body['_METHOD'])) {
256
                    $this->method = $this->filterMethod((string)$body['_METHOD']);
257
                }
258
259
                if ($this->getBody()->eof()) {
260
                    $this->getBody()->rewind();
261
                }
262
            }
263
        }
264
265
        return $this->method;
266
    }
267
268
    /**
269
     * Get the original HTTP method (ignore override).
270
     *
271
     * Note: This method is not part of the PSR-7 standard.
272
     *
273
     * @return string
274
     */
275
    public function getOriginalMethod()
276
    {
277
        return $this->originalMethod;
278
    }
279
280
    /**
281
     * Return an instance with the provided HTTP method.
282
     *
283
     * While HTTP method names are typically all uppercase characters, HTTP
284
     * method names are case-sensitive and thus implementations SHOULD NOT
285
     * modify the given string.
286
     *
287
     * This method MUST be implemented in such a way as to retain the
288
     * immutability of the message, and MUST return an instance that has the
289
     * changed request method.
290
     *
291
     * @param string $method Case-sensitive method.
292
     * @return self
293
     * @throws \InvalidArgumentException for invalid HTTP methods.
294
     */
295
    public function withMethod($method)
296
    {
297
        $method = $this->filterMethod($method);
298
        $clone = clone $this;
299
        $clone->originalMethod = $method;
300
        $clone->method = $method;
301
302
        return $clone;
303
    }
304
305
    /**
306
     * Validate the HTTP method
307
     *
308
     * @param  null|string $method
309
     * @return null|string
310
     * @throws \InvalidArgumentException on invalid HTTP method.
311
     */
312
    protected function filterMethod($method)
313
    {
314
        if ($method === null) {
315
            return $method;
316
        }
317
318
        if (!is_string($method)) {
319
            throw new InvalidArgumentException(sprintf(
320
                'Unsupported HTTP method; must be a string, received %s',
321
                (is_object($method) ? get_class($method) : gettype($method))
322
            ));
323
        }
324
325
        $method = strtoupper($method);
326
        if (!isset($this->validMethods[$method])) {
327
            throw new InvalidArgumentException(sprintf(
328
                'Unsupported HTTP method "%s" provided',
329
                $method
330
            ));
331
        }
332
333
        return $method;
334
    }
335
336
    /**
337
     * Does this request use a given method?
338
     *
339
     * Note: This method is not part of the PSR-7 standard.
340
     *
341
     * @param  string $method HTTP method
342
     * @return bool
343
     */
344
    public function isMethod($method)
345
    {
346
        return $this->getMethod() === $method;
347
    }
348
349
    /**
350
     * Is this a GET request?
351
     *
352
     * Note: This method is not part of the PSR-7 standard.
353
     *
354
     * @return bool
355
     */
356
    public function isGet()
357
    {
358
        return $this->isMethod('GET');
359
    }
360
361
    /**
362
     * Is this a POST request?
363
     *
364
     * Note: This method is not part of the PSR-7 standard.
365
     *
366
     * @return bool
367
     */
368
    public function isPost()
369
    {
370
        return $this->isMethod('POST');
371
    }
372
373
    /**
374
     * Is this a PUT request?
375
     *
376
     * Note: This method is not part of the PSR-7 standard.
377
     *
378
     * @return bool
379
     */
380
    public function isPut()
381
    {
382
        return $this->isMethod('PUT');
383
    }
384
385
    /**
386
     * Is this a PATCH request?
387
     *
388
     * Note: This method is not part of the PSR-7 standard.
389
     *
390
     * @return bool
391
     */
392
    public function isPatch()
393
    {
394
        return $this->isMethod('PATCH');
395
    }
396
397
    /**
398
     * Is this a DELETE request?
399
     *
400
     * Note: This method is not part of the PSR-7 standard.
401
     *
402
     * @return bool
403
     */
404
    public function isDelete()
405
    {
406
        return $this->isMethod('DELETE');
407
    }
408
409
    /**
410
     * Is this a HEAD request?
411
     *
412
     * Note: This method is not part of the PSR-7 standard.
413
     *
414
     * @return bool
415
     */
416
    public function isHead()
417
    {
418
        return $this->isMethod('HEAD');
419
    }
420
421
    /**
422
     * Is this a OPTIONS request?
423
     *
424
     * Note: This method is not part of the PSR-7 standard.
425
     *
426
     * @return bool
427
     */
428
    public function isOptions()
429
    {
430
        return $this->isMethod('OPTIONS');
431
    }
432
433
    /**
434
     * Is this an XHR request?
435
     *
436
     * Note: This method is not part of the PSR-7 standard.
437
     *
438
     * @return bool
439
     */
440
    public function isXhr()
441
    {
442
        return $this->getHeaderLine('X-Requested-With') === 'XMLHttpRequest';
443
    }
444
445
    /*******************************************************************************
446
     * URI
447
     ******************************************************************************/
448
449
    /**
450
     * Retrieves the message's request target.
451
     *
452
     * Retrieves the message's request-target either as it will appear (for
453
     * clients), as it appeared at request (for servers), or as it was
454
     * specified for the instance (see withRequestTarget()).
455
     *
456
     * In most cases, this will be the origin-form of the composed URI,
457
     * unless a value was provided to the concrete implementation (see
458
     * withRequestTarget() below).
459
     *
460
     * If no URI is available, and no request-target has been specifically
461
     * provided, this method MUST return the string "/".
462
     *
463
     * @return string
464
     */
465
    public function getRequestTarget()
466
    {
467
        if ($this->requestTarget) {
468
            return $this->requestTarget;
469
        }
470
471
        if ($this->uri === null) {
472
            return '/';
473
        }
474
475
        $basePath = $this->uri->getBasePath();
476
        $path = $this->uri->getPath();
477
        $path = $basePath . '/' . ltrim($path, '/');
478
479
        $query = $this->uri->getQuery();
480
        if ($query) {
481
            $path .= '?' . $query;
482
        }
483
        $this->requestTarget = $path;
484
485
        return $this->requestTarget;
486
    }
487
488
    /**
489
     * Return an instance with the specific request-target.
490
     *
491
     * If the request needs a non-origin-form request-target — e.g., for
492
     * specifying an absolute-form, authority-form, or asterisk-form —
493
     * this method may be used to create an instance with the specified
494
     * request-target, verbatim.
495
     *
496
     * This method MUST be implemented in such a way as to retain the
497
     * immutability of the message, and MUST return an instance that has the
498
     * changed request target.
499
     *
500
     * @link http://tools.ietf.org/html/rfc7230#section-2.7 (for the various
501
     *     request-target forms allowed in request messages)
502
     * @param mixed $requestTarget
503
     * @return self
504
     * @throws InvalidArgumentException if the request target is invalid
505
     */
506
    public function withRequestTarget($requestTarget)
507
    {
508
        if (preg_match('#\s#', $requestTarget)) {
509
            throw new InvalidArgumentException(
510
                'Invalid request target provided; must be a string and cannot contain whitespace'
511
            );
512
        }
513
        $clone = clone $this;
514
        $clone->requestTarget = $requestTarget;
515
516
        return $clone;
517
    }
518
519
    /**
520
     * Retrieves the URI instance.
521
     *
522
     * This method MUST return a UriInterface instance.
523
     *
524
     * @link http://tools.ietf.org/html/rfc3986#section-4.3
525
     * @return UriInterface Returns a UriInterface instance
526
     *     representing the URI of the request.
527
     */
528
    public function getUri()
529
    {
530
        return $this->uri;
531
    }
532
533
    /**
534
     * Returns an instance with the provided URI.
535
     *
536
     * This method MUST update the Host header of the returned request by
537
     * default if the URI contains a host component. If the URI does not
538
     * contain a host component, any pre-existing Host header MUST be carried
539
     * over to the returned request.
540
     *
541
     * You can opt-in to preserving the original state of the Host header by
542
     * setting `$preserveHost` to `true`. When `$preserveHost` is set to
543
     * `true`, this method interacts with the Host header in the following ways:
544
     *
545
     * - If the the Host header is missing or empty, and the new URI contains
546
     *   a host component, this method MUST update the Host header in the returned
547
     *   request.
548
     * - If the Host header is missing or empty, and the new URI does not contain a
549
     *   host component, this method MUST NOT update the Host header in the returned
550
     *   request.
551
     * - If a Host header is present and non-empty, this method MUST NOT update
552
     *   the Host header in the returned request.
553
     *
554
     * This method MUST be implemented in such a way as to retain the
555
     * immutability of the message, and MUST return an instance that has the
556
     * new UriInterface instance.
557
     *
558
     * @link http://tools.ietf.org/html/rfc3986#section-4.3
559
     * @param UriInterface $uri New request URI to use.
560
     * @param bool $preserveHost Preserve the original state of the Host header.
561
     * @return self
562
     */
563
    public function withUri(UriInterface $uri, $preserveHost = false)
564
    {
565
        $clone = clone $this;
566
        $clone->uri = $uri;
567
568
        if (!$preserveHost) {
569
            if ($uri->getHost() !== '') {
570
                $clone->headers->set('Host', $uri->getHost());
571
            }
572
        } else {
573
            if ($this->uri->getHost() !== '' && (!$this->hasHeader('Host') || $this->getHeader('Host') === null)) {
574
                $clone->headers->set('Host', $uri->getHost());
575
            }
576
        }
577
578
        return $clone;
579
    }
580
581
    /**
582
     * Get request content type.
583
     *
584
     * Note: This method is not part of the PSR-7 standard.
585
     *
586
     * @return string|null The request content type, if known
587
     */
588
    public function getContentType()
589
    {
590
        $result = $this->getHeader('Content-Type');
591
592
        return $result ? $result[0] : null;
593
    }
594
595
    /**
596
     * Get request media type, if known.
597
     *
598
     * Note: This method is not part of the PSR-7 standard.
599
     *
600
     * @return string|null The request media type, minus content-type params
601
     */
602
    public function getMediaType()
603
    {
604
        $contentType = $this->getContentType();
605
        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...
606
            $contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType);
607
608
            return strtolower($contentTypeParts[0]);
609
        }
610
611
        return null;
612
    }
613
614
    /**
615
     * Get request media type params, if known.
616
     *
617
     * Note: This method is not part of the PSR-7 standard.
618
     *
619
     * @return array
620
     */
621
    public function getMediaTypeParams()
622
    {
623
        $contentType = $this->getContentType();
624
        $contentTypeParams = [];
625
        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...
626
            $contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType);
627
            $contentTypePartsLength = count($contentTypeParts);
628
            for ($i = 1; $i < $contentTypePartsLength; $i++) {
629
                $paramParts = explode('=', $contentTypeParts[$i]);
630
                $contentTypeParams[strtolower($paramParts[0])] = $paramParts[1];
631
            }
632
        }
633
634
        return $contentTypeParams;
635
    }
636
637
    /**
638
     * Get request content character set, if known.
639
     *
640
     * Note: This method is not part of the PSR-7 standard.
641
     *
642
     * @return string|null
643
     */
644
    public function getContentCharset()
645
    {
646
        $mediaTypeParams = $this->getMediaTypeParams();
647
        if (isset($mediaTypeParams['charset'])) {
648
            return $mediaTypeParams['charset'];
649
        }
650
651
        return null;
652
    }
653
654
    /**
655
     * Get request content length, if known.
656
     *
657
     * Note: This method is not part of the PSR-7 standard.
658
     *
659
     * @return int|null
660
     */
661
    public function getContentLength()
662
    {
663
        $result = $this->headers->get('Content-Length');
664
665
        return $result ? (int)$result[0] : null;
666
    }
667
668
    /*******************************************************************************
669
     * Cookies
670
     ******************************************************************************/
671
672
    /**
673
     * Fetch cookie value from cookies sent by the client to the server.
674
     *
675
     * Note: This method is not part of the PSR-7 standard.
676
     *
677
     * @param      $key
678
     * @param null $default
679
     *
680
     * @return null
681
     */
682 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...
683
    {
684
        $getCookies = $this->getCookieParams();
685
        $result = $default;
686
        if (isset($getCookies[$key])) {
687
            $result = $getCookies[$key];
688
        }
689
        return $result;
690
    }
691
    
692
    /**
693
     * Retrieve cookies.
694
     *
695
     * Retrieves cookies sent by the client to the server.
696
     *
697
     * The data MUST be compatible with the structure of the $_COOKIE
698
     * superglobal.
699
     *
700
     * @return array
701
     */
702
    public function getCookieParams()
703
    {
704
        return $this->cookies;
705
    }
706
707
    /**
708
     * Return an instance with the specified cookies.
709
     *
710
     * The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST
711
     * be compatible with the structure of $_COOKIE. Typically, this data will
712
     * be injected at instantiation.
713
     *
714
     * This method MUST NOT update the related Cookie header of the request
715
     * instance, nor related values in the server params.
716
     *
717
     * This method MUST be implemented in such a way as to retain the
718
     * immutability of the message, and MUST return an instance that has the
719
     * updated cookie values.
720
     *
721
     * @param array $cookies Array of key/value pairs representing cookies.
722
     * @return self
723
     */
724
    public function withCookieParams(array $cookies)
725
    {
726
        $clone = clone $this;
727
        $clone->cookies = $cookies;
728
729
        return $clone;
730
    }
731
732
    /*******************************************************************************
733
     * Query Params
734
     ******************************************************************************/
735
736
    /**
737
     * Retrieve query string arguments.
738
     *
739
     * Retrieves the deserialized query string arguments, if any.
740
     *
741
     * Note: the query params might not be in sync with the URI or server
742
     * params. If you need to ensure you are only getting the original
743
     * values, you may need to parse the query string from `getUri()->getQuery()`
744
     * or from the `QUERY_STRING` server param.
745
     *
746
     * @return array
747
     */
748
    public function getQueryParams()
749
    {
750
        if (is_array($this->queryParams)) {
751
            return $this->queryParams;
752
        }
753
754
        if ($this->uri === null) {
755
            return [];
756
        }
757
758
        parse_str($this->uri->getQuery(), $this->queryParams); // <-- URL decodes data
759
760
        return $this->queryParams;
761
    }
762
763
    /**
764
     * Return an instance with the specified query string arguments.
765
     *
766
     * These values SHOULD remain immutable over the course of the incoming
767
     * request. They MAY be injected during instantiation, such as from PHP's
768
     * $_GET superglobal, or MAY be derived from some other value such as the
769
     * URI. In cases where the arguments are parsed from the URI, the data
770
     * MUST be compatible with what PHP's parse_str() would return for
771
     * purposes of how duplicate query parameters are handled, and how nested
772
     * sets are handled.
773
     *
774
     * Setting query string arguments MUST NOT change the URI stored by the
775
     * request, nor the values in the server params.
776
     *
777
     * This method MUST be implemented in such a way as to retain the
778
     * immutability of the message, and MUST return an instance that has the
779
     * updated query string arguments.
780
     *
781
     * @param array $query Array of query string arguments, typically from
782
     *     $_GET.
783
     * @return self
784
     */
785
    public function withQueryParams(array $query)
786
    {
787
        $clone = clone $this;
788
        $clone->queryParams = $query;
789
790
        return $clone;
791
    }
792
793
    /*******************************************************************************
794
     * File Params
795
     ******************************************************************************/
796
797
    /**
798
     * Retrieve normalized file upload data.
799
     *
800
     * This method returns upload metadata in a normalized tree, with each leaf
801
     * an instance of Psr\Http\Message\UploadedFileInterface.
802
     *
803
     * These values MAY be prepared from $_FILES or the message body during
804
     * instantiation, or MAY be injected via withUploadedFiles().
805
     *
806
     * @return array An array tree of UploadedFileInterface instances; an empty
807
     *     array MUST be returned if no data is present.
808
     */
809
    public function getUploadedFiles()
810
    {
811
        return $this->uploadedFiles;
812
    }
813
814
    /**
815
     * Create a new instance with the specified uploaded files.
816
     *
817
     * This method MUST be implemented in such a way as to retain the
818
     * immutability of the message, and MUST return an instance that has the
819
     * updated body parameters.
820
     *
821
     * @param array $uploadedFiles An array tree of UploadedFileInterface instances.
822
     * @return self
823
     * @throws \InvalidArgumentException if an invalid structure is provided.
824
     */
825
    public function withUploadedFiles(array $uploadedFiles)
826
    {
827
        $clone = clone $this;
828
        $clone->uploadedFiles = $uploadedFiles;
829
830
        return $clone;
831
    }
832
833
    /*******************************************************************************
834
     * Server Params
835
     ******************************************************************************/
836
837
    /**
838
     * Retrieve server parameters.
839
     *
840
     * Retrieves data related to the incoming request environment,
841
     * typically derived from PHP's $_SERVER superglobal. The data IS NOT
842
     * REQUIRED to originate from $_SERVER.
843
     *
844
     * @return array
845
     */
846
    public function getServerParams()
847
    {
848
        return $this->serverParams;
849
    }
850
851
    /*******************************************************************************
852
     * Attributes
853
     ******************************************************************************/
854
855
    /**
856
     * Retrieve attributes derived from the request.
857
     *
858
     * The request "attributes" may be used to allow injection of any
859
     * parameters derived from the request: e.g., the results of path
860
     * match operations; the results of decrypting cookies; the results of
861
     * deserializing non-form-encoded message bodies; etc. Attributes
862
     * will be application and request specific, and CAN be mutable.
863
     *
864
     * @return array Attributes derived from the request.
865
     */
866
    public function getAttributes()
867
    {
868
        return $this->attributes->all();
869
    }
870
871
    /**
872
     * Retrieve a single derived request attribute.
873
     *
874
     * Retrieves a single derived request attribute as described in
875
     * getAttributes(). If the attribute has not been previously set, returns
876
     * the default value as provided.
877
     *
878
     * This method obviates the need for a hasAttribute() method, as it allows
879
     * specifying a default value to return if the attribute is not found.
880
     *
881
     * @see getAttributes()
882
     * @param string $name The attribute name.
883
     * @param mixed $default Default value to return if the attribute does not exist.
884
     * @return mixed
885
     */
886
    public function getAttribute($name, $default = null)
887
    {
888
        return $this->attributes->get($name, $default);
889
    }
890
891
    /**
892
     * Return an instance with the specified derived request attribute.
893
     *
894
     * This method allows setting a single derived request attribute as
895
     * described in getAttributes().
896
     *
897
     * This method MUST be implemented in such a way as to retain the
898
     * immutability of the message, and MUST return an instance that has the
899
     * updated attribute.
900
     *
901
     * @see getAttributes()
902
     * @param string $name The attribute name.
903
     * @param mixed $value The value of the attribute.
904
     * @return self
905
     */
906
    public function withAttribute($name, $value)
907
    {
908
        $clone = clone $this;
909
        $clone->attributes->set($name, $value);
910
911
        return $clone;
912
    }
913
914
    /**
915
     * Create a new instance with the specified derived request attributes.
916
     *
917
     * Note: This method is not part of the PSR-7 standard.
918
     *
919
     * This method allows setting all new derived request attributes as
920
     * described in getAttributes().
921
     *
922
     * This method MUST be implemented in such a way as to retain the
923
     * immutability of the message, and MUST return a new instance that has the
924
     * updated attributes.
925
     *
926
     * @param  array $attributes New attributes
927
     * @return self
928
     */
929
    public function withAttributes(array $attributes)
930
    {
931
        $clone = clone $this;
932
        $clone->attributes = new Collection($attributes);
933
934
        return $clone;
935
    }
936
937
    /**
938
     * Return an instance that removes the specified derived request attribute.
939
     *
940
     * This method allows removing a single derived request attribute as
941
     * described in getAttributes().
942
     *
943
     * This method MUST be implemented in such a way as to retain the
944
     * immutability of the message, and MUST return an instance that removes
945
     * the attribute.
946
     *
947
     * @see getAttributes()
948
     * @param string $name The attribute name.
949
     * @return self
950
     */
951
    public function withoutAttribute($name)
952
    {
953
        $clone = clone $this;
954
        $clone->attributes->remove($name);
955
956
        return $clone;
957
    }
958
959
    /*******************************************************************************
960
     * Body
961
     ******************************************************************************/
962
963
    /**
964
     * Retrieve any parameters provided in the request body.
965
     *
966
     * If the request Content-Type is either application/x-www-form-urlencoded
967
     * or multipart/form-data, and the request method is POST, this method MUST
968
     * return the contents of $_POST.
969
     *
970
     * Otherwise, this method may return any results of deserializing
971
     * the request body content; as parsing returns structured content, the
972
     * potential types MUST be arrays or objects only. A null value indicates
973
     * the absence of body content.
974
     *
975
     * @return null|array|object The deserialized body parameters, if any.
976
     *     These will typically be an array or object.
977
     * @throws RuntimeException if the request body media type parser returns an invalid value
978
     */
979
    public function getParsedBody()
980
    {
981
        if ($this->bodyParsed !== false) {
982
            return $this->bodyParsed;
983
        }
984
985
        if (!$this->body) {
986
            return null;
987
        }
988
989
        $mediaType = $this->getMediaType();
990
991
        // look for a media type with a structured syntax suffix (RFC 6839)
992
        $parts = explode('+', $mediaType);
993
        if (count($parts) >= 2) {
994
            $mediaType = 'application/' . $parts[count($parts)-1];
995
        }
996
997
        if (isset($this->bodyParsers[$mediaType]) === true) {
998
            $body = (string)$this->getBody();
999
            $parsed = $this->bodyParsers[$mediaType]($body);
1000
1001
            if (!is_null($parsed) && !is_object($parsed) && !is_array($parsed)) {
1002
                throw new RuntimeException(
1003
                    'Request body media type parser return value must be an array, an object, or null'
1004
                );
1005
            }
1006
            $this->bodyParsed = $parsed;
1007
            return $this->bodyParsed;
1008
        }
1009
1010
        return null;
1011
    }
1012
1013
    /**
1014
     * Return an instance with the specified body parameters.
1015
     *
1016
     * These MAY be injected during instantiation.
1017
     *
1018
     * If the request Content-Type is either application/x-www-form-urlencoded
1019
     * or multipart/form-data, and the request method is POST, use this method
1020
     * ONLY to inject the contents of $_POST.
1021
     *
1022
     * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of
1023
     * deserializing the request body content. Deserialization/parsing returns
1024
     * structured data, and, as such, this method ONLY accepts arrays or objects,
1025
     * or a null value if nothing was available to parse.
1026
     *
1027
     * As an example, if content negotiation determines that the request data
1028
     * is a JSON payload, this method could be used to create a request
1029
     * instance with the deserialized parameters.
1030
     *
1031
     * This method MUST be implemented in such a way as to retain the
1032
     * immutability of the message, and MUST return an instance that has the
1033
     * updated body parameters.
1034
     *
1035
     * @param null|array|object $data The deserialized body data. This will
1036
     *     typically be in an array or object.
1037
     * @return self
1038
     * @throws \InvalidArgumentException if an unsupported argument type is
1039
     *     provided.
1040
     */
1041
    public function withParsedBody($data)
1042
    {
1043
        if (!is_null($data) && !is_object($data) && !is_array($data)) {
1044
            throw new InvalidArgumentException('Parsed body value must be an array, an object, or null');
1045
        }
1046
1047
        $clone = clone $this;
1048
        $clone->bodyParsed = $data;
1049
1050
        return $clone;
1051
    }
1052
1053
    /**
1054
     * Force Body to be parsed again.
1055
     *
1056
     * Note: This method is not part of the PSR-7 standard.
1057
     *
1058
     * @return self
1059
     */
1060
    public function reparseBody()
1061
    {
1062
        $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...
1063
1064
        return $this;
1065
    }
1066
1067
    /**
1068
     * Register media type parser.
1069
     *
1070
     * Note: This method is not part of the PSR-7 standard.
1071
     *
1072
     * @param string   $mediaType A HTTP media type (excluding content-type
1073
     *     params).
1074
     * @param callable $callable  A callable that returns parsed contents for
1075
     *     media type.
1076
     */
1077
    public function registerMediaTypeParser($mediaType, callable $callable)
1078
    {
1079
        if ($callable instanceof Closure) {
1080
            $callable = $callable->bindTo($this);
1081
        }
1082
        $this->bodyParsers[(string)$mediaType] = $callable;
1083
    }
1084
1085
    /*******************************************************************************
1086
     * Parameters (e.g., POST and GET data)
1087
     ******************************************************************************/
1088
1089
    /**
1090
     * Fetch request parameter value from body or query string (in that order).
1091
     *
1092
     * Note: This method is not part of the PSR-7 standard.
1093
     *
1094
     * @param  string $key The parameter key.
1095
     * @param  string $default The default value.
1096
     *
1097
     * @return mixed The parameter value.
1098
     */
1099
    public function getParam($key, $default = null)
1100
    {
1101
        $postParams = $this->getParsedBody();
1102
        $getParams = $this->getQueryParams();
1103
        $result = $default;
1104
        if (is_array($postParams) && isset($postParams[$key])) {
1105
            $result = $postParams[$key];
1106
        } elseif (is_object($postParams) && property_exists($postParams, $key)) {
1107
            $result = $postParams->$key;
1108
        } elseif (isset($getParams[$key])) {
1109
            $result = $getParams[$key];
1110
        }
1111
1112
        return $result;
1113
    }
1114
1115
    /**
1116
     * Fetch parameter value from request body.
1117
     *
1118
     * Note: This method is not part of the PSR-7 standard.
1119
     *
1120
     * @param      $key
1121
     * @param null $default
1122
     *
1123
     * @return null
1124
     */
1125
    public function getParsedBodyParam($key, $default = null)
1126
    {
1127
        $postParams = $this->getParsedBody();
1128
        $result = $default;
1129
        if (is_array($postParams) && isset($postParams[$key])) {
1130
            $result = $postParams[$key];
1131
        } elseif (is_object($postParams) && property_exists($postParams, $key)) {
1132
            $result = $postParams->$key;
1133
        }
1134
1135
        return $result;
1136
    }
1137
1138
    /**
1139
     * Fetch parameter value from query string.
1140
     *
1141
     * Note: This method is not part of the PSR-7 standard.
1142
     *
1143
     * @param      $key
1144
     * @param null $default
1145
     *
1146
     * @return null
1147
     */
1148 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...
1149
    {
1150
        $getParams = $this->getQueryParams();
1151
        $result = $default;
1152
        if (isset($getParams[$key])) {
1153
            $result = $getParams[$key];
1154
        }
1155
1156
        return $result;
1157
    }
1158
1159
    /**
1160
     * Fetch assocative array of body and query string parameters.
1161
     *
1162
     * Note: This method is not part of the PSR-7 standard.
1163
     *
1164
     * @return array
1165
     */
1166
    public function getParams()
1167
    {
1168
        $params = $this->getQueryParams();
1169
        $postParams = $this->getParsedBody();
1170
        if ($postParams) {
1171
            $params = array_merge($params, (array)$postParams);
1172
        }
1173
1174
        return $params;
1175
    }
1176
}
1177