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
Push — master ( 3bd813...b7079d )
by Robert
13:06
created

Request::getRemoteIP()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 0
crap 2
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\web;
9
10
use Yii;
11
use yii\base\InvalidConfigException;
12
13
/**
14
 * The web Request class represents an HTTP request
15
 *
16
 * It encapsulates the $_SERVER variable and resolves its inconsistency among different Web servers.
17
 * Also it provides an interface to retrieve request parameters from $_POST, $_GET, $_COOKIES and REST
18
 * parameters sent via other HTTP methods like PUT or DELETE.
19
 *
20
 * Request is configured as an application component in [[\yii\web\Application]] by default.
21
 * You can access that instance via `Yii::$app->request`.
22
 *
23
 * For more details and usage information on Request, see the [guide article on requests](guide:runtime-requests).
24
 *
25
 * @property string $absoluteUrl The currently requested absolute URL. This property is read-only.
26
 * @property array $acceptableContentTypes The content types ordered by the quality score. Types with the
27
 * highest scores will be returned first. The array keys are the content types, while the array values are the
28
 * corresponding quality score and other parameters as given in the header.
29
 * @property array $acceptableLanguages The languages ordered by the preference level. The first element
30
 * represents the most preferred language.
31
 * @property string|null $authPassword The password sent via HTTP authentication, null if the password is not
32
 * given. This property is read-only.
33
 * @property string|null $authUser The username sent via HTTP authentication, null if the username is not
34
 * given. This property is read-only.
35
 * @property string $baseUrl The relative URL for the application.
36
 * @property array $bodyParams The request parameters given in the request body.
37
 * @property string $contentType Request content-type. Null is returned if this information is not available.
38
 * This property is read-only.
39
 * @property CookieCollection $cookies The cookie collection. This property is read-only.
40
 * @property string $csrfToken The token used to perform CSRF validation. This property is read-only.
41
 * @property string $csrfTokenFromHeader The CSRF token sent via [[CSRF_HEADER]] by browser. Null is returned
42
 * if no such header is sent. This property is read-only.
43
 * @property array $eTags The entity tags. This property is read-only.
44
 * @property HeaderCollection $headers The header collection. This property is read-only.
45
 * @property string|null $hostInfo Schema and hostname part (with port number if needed) of the request URL
46
 * (e.g. `http://www.yiiframework.com`), null if can't be obtained from `$_SERVER` and wasn't set. See
47
 * [[getHostInfo()]] for security related notes on this property.
48
 * @property string|null $hostName Hostname part of the request URL (e.g. `www.yiiframework.com`). This
49
 * property is read-only.
50
 * @property bool $isAjax Whether this is an AJAX (XMLHttpRequest) request. This property is read-only.
51
 * @property bool $isDelete Whether this is a DELETE request. This property is read-only.
52
 * @property bool $isFlash Whether this is an Adobe Flash or Adobe Flex request. This property is read-only.
53
 * @property bool $isGet Whether this is a GET request. This property is read-only.
54
 * @property bool $isHead Whether this is a HEAD request. This property is read-only.
55
 * @property bool $isOptions Whether this is a OPTIONS request. This property is read-only.
56
 * @property bool $isPatch Whether this is a PATCH request. This property is read-only.
57
 * @property bool $isPjax Whether this is a PJAX request. This property is read-only.
58
 * @property bool $isPost Whether this is a POST request. This property is read-only.
59
 * @property bool $isPut Whether this is a PUT request. This property is read-only.
60
 * @property bool $isSecureConnection If the request is sent via secure channel (https). This property is
61
 * read-only.
62
 * @property string $method Request method, such as GET, POST, HEAD, PUT, PATCH, DELETE. The value returned is
63
 * turned into upper case. This property is read-only.
64
 * @property string $pathInfo Part of the request URL that is after the entry script and before the question
65
 * mark. Note, the returned path info is already URL-decoded.
66
 * @property int $port Port number for insecure requests.
67
 * @property array $queryParams The request GET parameter values.
68
 * @property string $queryString Part of the request URL that is after the question mark. This property is
69
 * read-only.
70
 * @property string $rawBody The request body.
71
 * @property string|null $referrer URL referrer, null if not available. This property is read-only.
72
 * @property string|null $origin URL origin, null if not available. This property is read-only.
73
 * @property string $scriptFile The entry script file path.
74
 * @property string $scriptUrl The relative URL of the entry script.
75
 * @property int $securePort Port number for secure requests.
76
 * @property string $serverName Server name, null if not available. This property is read-only.
77
 * @property int|null $serverPort Server port number, null if not available. This property is read-only.
78
 * @property string $url The currently requested relative URL. Note that the URI returned may be URL-encoded
79
 * depending on the client.
80
 * @property string|null $userAgent User agent, null if not available. This property is read-only.
81
 * @property string|null $userHost User host name, null if not available. This property is read-only.
82
 * @property string|null $userIP User IP address, null if not available. This property is read-only.
83
 *
84
 * @author Qiang Xue <[email protected]>
85
 * @since 2.0
86
 * @SuppressWarnings(PHPMD.SuperGlobals)
87
 */
88
class Request extends \yii\base\Request
89
{
90
    /**
91
     * The name of the HTTP header for sending CSRF token.
92
     */
93
    const CSRF_HEADER = 'X-CSRF-Token';
94
    /**
95
     * The length of the CSRF token mask.
96
     * @deprecated 2.0.12 The mask length is now equal to the token length.
97
     */
98
    const CSRF_MASK_LENGTH = 8;
99
100
    /**
101
     * @var bool whether to enable CSRF (Cross-Site Request Forgery) validation. Defaults to true.
102
     * When CSRF validation is enabled, forms submitted to an Yii Web application must be originated
103
     * from the same application. If not, a 400 HTTP exception will be raised.
104
     *
105
     * Note, this feature requires that the user client accepts cookie. Also, to use this feature,
106
     * forms submitted via POST method must contain a hidden input whose name is specified by [[csrfParam]].
107
     * You may use [[\yii\helpers\Html::beginForm()]] to generate his hidden input.
108
     *
109
     * In JavaScript, you may get the values of [[csrfParam]] and [[csrfToken]] via `yii.getCsrfParam()` and
110
     * `yii.getCsrfToken()`, respectively. The [[\yii\web\YiiAsset]] asset must be registered.
111
     * You also need to include CSRF meta tags in your pages by using [[\yii\helpers\Html::csrfMetaTags()]].
112
     *
113
     * @see Controller::enableCsrfValidation
114
     * @see http://en.wikipedia.org/wiki/Cross-site_request_forgery
115
     */
116
    public $enableCsrfValidation = true;
117
    /**
118
     * @var string the name of the token used to prevent CSRF. Defaults to '_csrf'.
119
     * This property is used only when [[enableCsrfValidation]] is true.
120
     */
121
    public $csrfParam = '_csrf';
122
    /**
123
     * @var array the configuration for creating the CSRF [[Cookie|cookie]]. This property is used only when
124
     * both [[enableCsrfValidation]] and [[enableCsrfCookie]] are true.
125
     */
126
    public $csrfCookie = ['httpOnly' => true];
127
    /**
128
     * @var bool whether to use cookie to persist CSRF token. If false, CSRF token will be stored
129
     * in session under the name of [[csrfParam]]. Note that while storing CSRF tokens in session increases
130
     * security, it requires starting a session for every page, which will degrade your site performance.
131
     */
132
    public $enableCsrfCookie = true;
133
    /**
134
     * @var bool whether cookies should be validated to ensure they are not tampered. Defaults to true.
135
     */
136
    public $enableCookieValidation = true;
137
    /**
138
     * @var string a secret key used for cookie validation. This property must be set if [[enableCookieValidation]] is true.
139
     */
140
    public $cookieValidationKey;
141
    /**
142
     * @var string the name of the POST parameter that is used to indicate if a request is a PUT, PATCH or DELETE
143
     * request tunneled through POST. Defaults to '_method'.
144
     * @see getMethod()
145
     * @see getBodyParams()
146
     */
147
    public $methodParam = '_method';
148
    /**
149
     * @var array the parsers for converting the raw HTTP request body into [[bodyParams]].
150
     * The array keys are the request `Content-Types`, and the array values are the
151
     * corresponding configurations for [[Yii::createObject|creating the parser objects]].
152
     * A parser must implement the [[RequestParserInterface]].
153
     *
154
     * To enable parsing for JSON requests you can use the [[JsonParser]] class like in the following example:
155
     *
156
     * ```
157
     * [
158
     *     'application/json' => 'yii\web\JsonParser',
159
     * ]
160
     * ```
161
     *
162
     * To register a parser for parsing all request types you can use `'*'` as the array key.
163
     * This one will be used as a fallback in case no other types match.
164
     *
165
     * @see getBodyParams()
166
     */
167
    public $parsers = [];
168
    /**
169
     * @var array the configuration for trusted security related headers.
170
     *
171
     * An array key is a regular expression for matching a client (ususally a proxy) by
172
     * host name or ip address.
173
     *
174
     * An array value is a list of headers to trust. These will be matched against
175
     * [[secureHeaders]] to determine which headers are allowed to be sent by a specified host.
176
     * The case of the header names must be the same as specified in [[secureHeaders]].
177
     *
178
     * To specify a host for which you trust all headers listed in [[secureHeaders]] you can just specify
179
     * the regular expression as the array value.
180
     *
181
     * For example, to trust all headers listed in [[secureHeaders]]
182
     * from domains ending in '.trusted.com' use the following:
183
     *
184
     * ```php
185
     * [
186
     *     '/\.trusted\.com$/',
187
     * ]
188
     * ```
189
     *
190
     * To trust just the `x-forwarded-for` header from domains ending in `.partial.com` use:
191
     *
192
     * ```
193
     * [
194
     *     '/\.partial\.com$/' => ['X-Forwarded-For']
195
     * ]
196
     * ```
197
     *
198
     * Default is to trust all headers except those listed in [[secureHeaders]] from all hosts.
199
     * Matches are tried in order and searching is stopped when a host or IP matches.
200
     * @see $secureHeaders
201
     * @since 2.0.13
202
     */
203
    public $trustedHosts = [];
204
    /**
205
     * @var array lists of headers that are, by default, subject to the trusted host configuration.
206
     * These headers will be filtered unless explicitly allowed in [[$trustedHosts]].
207
     * The match of header names is case-insensitive.
208
     * @see https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
209
     * @see $trustedHosts
210
     * @since 2.0.13
211
     */
212
    public $secureHeaders = [
213
        'X-Forwarded-For',
214
        'X-Forwarded-Host',
215
        'X-Forwarded-Proto',
216
        'Front-End-Https',
217
    ];
218
    /**
219
     * @var string[] List of headers where proxies store the real client IP.
220
     * It's not advisable to put insecure headers here.
221
     * The match of header names is case-insensitive.
222
     * @see $trustedHosts
223
     * @see $secureHeaders
224
     * @since 2.0.13
225
     */
226
    public $ipHeaders = [
227
        'X-Forwarded-For',
228
    ];
229
    /**
230
     * @var array list of headers to check for determining whether the connection is made via HTTPS.
231
     * The array keys are header names and the array value is a list of header values that indicate a secure connection.
232
     * The match of header names and values is case-insensitive.
233
     * It's not advisable to put insecure headers here.
234
     * @see $trustedHosts
235
     * @see $secureHeaders
236
     * @since 2.0.13
237
     */
238
    public $secureProtocolHeaders = [
239
        'X-Forwarded-Proto' => ['https'],
240
        'Front-End-Https' => ['on'],
241
    ];
242
    /**
243
     * @var CookieCollection Collection of request cookies.
244
     */
245
    private $_cookies;
246
    /**
247
     * @var HeaderCollection Collection of request headers.
248
     */
249
    private $_headers;
250
251
252
    /**
253
     * Resolves the current request into a route and the associated parameters.
254
     * @return array the first element is the route, and the second is the associated parameters.
255
     * @throws NotFoundHttpException if the request cannot be resolved.
256
     */
257 1
    public function resolve()
258
    {
259 1
        $result = Yii::$app->getUrlManager()->parseRequest($this);
260 1
        if ($result !== false) {
261 1
            list($route, $params) = $result;
262 1
            if ($this->_queryParams === null) {
263 1
                $_GET = $params + $_GET; // preserve numeric keys
264
            } else {
265 1
                $this->_queryParams = $params + $this->_queryParams;
266
            }
267 1
            return [$route, $this->getQueryParams()];
268
        }
269
270
        throw new NotFoundHttpException(Yii::t('yii', 'Page not found.'));
271
    }
272
273
    /**
274
     * Filters headers according to the [[trustedHosts]].
275
     * @param HeaderCollection $headerCollection
276
     * @since 2.0.13
277
     */
278 102
    protected function filterHeaders(HeaderCollection $headerCollection)
279
    {
280
        // do not trust any of the [[secureHeaders]] by default
281 102
        $trustedHeaders = [];
282
283
        // check if the client is a trusted host
284 102
        if (!empty($this->trustedHosts)) {
285 19
            $host = $this->getRemoteHost();
286 19
            $ip = $this->getRemoteIP();
287 19
            foreach ($this->trustedHosts as $hostRegex => $headers) {
288 19
                if (!is_array($headers)) {
289 19
                    $hostRegex = $headers;
290 19
                    $headers = $this->secureHeaders;
291
                }
292 19
                if (preg_match($hostRegex, $host) || preg_match($hostRegex, $ip)) {
293 6
                    $trustedHeaders = $headers;
294 19
                    continue;
295
                }
296
            }
297
        }
298
299
        // filter all secure headers unless they are trusted
300 102
        foreach ($this->secureHeaders as $secureHeader) {
301 102
            if (!in_array($secureHeader, $trustedHeaders)) {
302 102
                $headerCollection->remove($secureHeader);
303
            }
304
        }
305 102
    }
306
307
    /**
308
     * Returns the header collection.
309
     * The header collection contains incoming HTTP headers.
310
     * @return HeaderCollection the header collection
311
     */
312 102
    public function getHeaders()
313
    {
314 102
        if ($this->_headers === null) {
315 102
            $this->_headers = new HeaderCollection();
316 102
            if (function_exists('getallheaders')) {
317
                $headers = getallheaders();
318
                foreach ($headers as $name => $value) {
319
                    $this->_headers->add($name, $value);
320
                }
321 102
            } elseif (function_exists('http_get_request_headers')) {
322
                $headers = http_get_request_headers();
323
                foreach ($headers as $name => $value) {
324
                    $this->_headers->add($name, $value);
325
                }
326
            } else {
327 102
                foreach ($_SERVER as $name => $value) {
328 99
                    if (strncmp($name, 'HTTP_', 5) === 0) {
329 37
                        $name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
330 99
                        $this->_headers->add($name, $value);
331
                    }
332
                }
333
            }
334 102
            $this->filterHeaders($this->_headers);
335
        }
336
337 102
        return $this->_headers;
338
    }
339
340
    /**
341
     * Returns the method of the current request (e.g. GET, POST, HEAD, PUT, PATCH, DELETE).
342
     * @return string request method, such as GET, POST, HEAD, PUT, PATCH, DELETE.
343
     * The value returned is turned into upper case.
344
     */
345 22
    public function getMethod()
346
    {
347 22
        if (isset($_POST[$this->methodParam])) {
348 4
            return strtoupper($_POST[$this->methodParam]);
349
        }
350
351 19
        if ($this->headers->has('X-Http-Method-Override')) {
352 1
            return strtoupper($this->headers->get('X-Http-Method-Override'));
353
        }
354
355 18
        if (isset($_SERVER['REQUEST_METHOD'])) {
356 3
            return strtoupper($_SERVER['REQUEST_METHOD']);
357
        }
358
359 16
        return 'GET';
360
    }
361
362
    /**
363
     * Returns whether this is a GET request.
364
     * @return bool whether this is a GET request.
365
     */
366 2
    public function getIsGet()
367
    {
368 2
        return $this->getMethod() === 'GET';
369
    }
370
371
    /**
372
     * Returns whether this is an OPTIONS request.
373
     * @return bool whether this is a OPTIONS request.
374
     */
375
    public function getIsOptions()
376
    {
377
        return $this->getMethod() === 'OPTIONS';
378
    }
379
380
    /**
381
     * Returns whether this is a HEAD request.
382
     * @return bool whether this is a HEAD request.
383
     */
384 9
    public function getIsHead()
385
    {
386 9
        return $this->getMethod() === 'HEAD';
387
    }
388
389
    /**
390
     * Returns whether this is a POST request.
391
     * @return bool whether this is a POST request.
392
     */
393
    public function getIsPost()
394
    {
395
        return $this->getMethod() === 'POST';
396
    }
397
398
    /**
399
     * Returns whether this is a DELETE request.
400
     * @return bool whether this is a DELETE request.
401
     */
402
    public function getIsDelete()
403
    {
404
        return $this->getMethod() === 'DELETE';
405
    }
406
407
    /**
408
     * Returns whether this is a PUT request.
409
     * @return bool whether this is a PUT request.
410
     */
411
    public function getIsPut()
412
    {
413
        return $this->getMethod() === 'PUT';
414
    }
415
416
    /**
417
     * Returns whether this is a PATCH request.
418
     * @return bool whether this is a PATCH request.
419
     */
420
    public function getIsPatch()
421
    {
422
        return $this->getMethod() === 'PATCH';
423
    }
424
425
    /**
426
     * Returns whether this is an AJAX (XMLHttpRequest) request.
427
     *
428
     * Note that jQuery doesn't set the header in case of cross domain
429
     * requests: https://stackoverflow.com/questions/8163703/cross-domain-ajax-doesnt-send-x-requested-with-header
430
     *
431
     * @return bool whether this is an AJAX (XMLHttpRequest) request.
432
     */
433 14
    public function getIsAjax()
434
    {
435 14
        return $this->headers->get('X-Requested-With') === 'XMLHttpRequest';
436
    }
437
438
    /**
439
     * Returns whether this is a PJAX request
440
     * @return bool whether this is a PJAX request
441
     */
442 3
    public function getIsPjax()
443
    {
444 3
        return $this->getIsAjax() && $this->headers->has('X-Pjax');
445
    }
446
447
    /**
448
     * Returns whether this is an Adobe Flash or Flex request.
449
     * @return bool whether this is an Adobe Flash or Adobe Flex request.
450
     */
451
    public function getIsFlash()
452
    {
453
        $userAgent = $this->headers->get('User-Agent', '');
454
        return stripos($userAgent, 'Shockwave') !== false
455
            || stripos($userAgent, 'Flash') !== false;
456
    }
457
458
    private $_rawBody;
459
460
    /**
461
     * Returns the raw HTTP request body.
462
     * @return string the request body
463
     */
464
    public function getRawBody()
465
    {
466
        if ($this->_rawBody === null) {
467
            $this->_rawBody = file_get_contents('php://input');
468
        }
469
470
        return $this->_rawBody;
471
    }
472
473
    /**
474
     * Sets the raw HTTP request body, this method is mainly used by test scripts to simulate raw HTTP requests.
475
     * @param string $rawBody the request body
476
     */
477
    public function setRawBody($rawBody)
478
    {
479
        $this->_rawBody = $rawBody;
480
    }
481
482
    private $_bodyParams;
483
484
    /**
485
     * Returns the request parameters given in the request body.
486
     *
487
     * Request parameters are determined using the parsers configured in [[parsers]] property.
488
     * If no parsers are configured for the current [[contentType]] it uses the PHP function `mb_parse_str()`
489
     * to parse the [[rawBody|request body]].
490
     * @return array the request parameters given in the request body.
491
     * @throws \yii\base\InvalidConfigException if a registered parser does not implement the [[RequestParserInterface]].
492
     * @see getMethod()
493
     * @see getBodyParam()
494
     * @see setBodyParams()
495
     */
496 3
    public function getBodyParams()
497
    {
498 3
        if ($this->_bodyParams === null) {
499 1
            if (isset($_POST[$this->methodParam])) {
500 1
                $this->_bodyParams = $_POST;
501 1
                unset($this->_bodyParams[$this->methodParam]);
502 1
                return $this->_bodyParams;
503
            }
504
505
            $rawContentType = $this->getContentType();
506
            if (($pos = strpos($rawContentType, ';')) !== false) {
507
                // e.g. text/html; charset=UTF-8
508
                $contentType = substr($rawContentType, 0, $pos);
509
            } else {
510
                $contentType = $rawContentType;
511
            }
512
513
            if (isset($this->parsers[$contentType])) {
514
                $parser = Yii::createObject($this->parsers[$contentType]);
515
                if (!($parser instanceof RequestParserInterface)) {
516
                    throw new InvalidConfigException("The '$contentType' request parser is invalid. It must implement the yii\\web\\RequestParserInterface.");
517
                }
518
                $this->_bodyParams = $parser->parse($this->getRawBody(), $rawContentType);
519
            } elseif (isset($this->parsers['*'])) {
520
                $parser = Yii::createObject($this->parsers['*']);
521
                if (!($parser instanceof RequestParserInterface)) {
522
                    throw new InvalidConfigException('The fallback request parser is invalid. It must implement the yii\\web\\RequestParserInterface.');
523
                }
524
                $this->_bodyParams = $parser->parse($this->getRawBody(), $rawContentType);
525
            } elseif ($this->getMethod() === 'POST') {
526
                // PHP has already parsed the body so we have all params in $_POST
527
                $this->_bodyParams = $_POST;
528
            } else {
529
                $this->_bodyParams = [];
530
                mb_parse_str($this->getRawBody(), $this->_bodyParams);
531
            }
532
        }
533
534 3
        return $this->_bodyParams;
535
    }
536
537
    /**
538
     * Sets the request body parameters.
539
     * @param array $values the request body parameters (name-value pairs)
540
     * @see getBodyParam()
541
     * @see getBodyParams()
542
     */
543 2
    public function setBodyParams($values)
544
    {
545 2
        $this->_bodyParams = $values;
546 2
    }
547
548
    /**
549
     * Returns the named request body parameter value.
550
     * If the parameter does not exist, the second parameter passed to this method will be returned.
551
     * @param string $name the parameter name
552
     * @param mixed $defaultValue the default parameter value if the parameter does not exist.
553
     * @return mixed the parameter value
554
     * @see getBodyParams()
555
     * @see setBodyParams()
556
     */
557 3
    public function getBodyParam($name, $defaultValue = null)
558
    {
559 3
        $params = $this->getBodyParams();
560
561 3
        return isset($params[$name]) ? $params[$name] : $defaultValue;
562
    }
563
564
    /**
565
     * Returns POST parameter with a given name. If name isn't specified, returns an array of all POST parameters.
566
     *
567
     * @param string $name the parameter name
568
     * @param mixed $defaultValue the default parameter value if the parameter does not exist.
569
     * @return array|mixed
570
     */
571
    public function post($name = null, $defaultValue = null)
572
    {
573
        if ($name === null) {
574
            return $this->getBodyParams();
575
        }
576
577
        return $this->getBodyParam($name, $defaultValue);
578
    }
579
580
    private $_queryParams;
581
582
    /**
583
     * Returns the request parameters given in the [[queryString]].
584
     *
585
     * This method will return the contents of `$_GET` if params where not explicitly set.
586
     * @return array the request GET parameter values.
587
     * @see setQueryParams()
588
     */
589 26
    public function getQueryParams()
590
    {
591 26
        if ($this->_queryParams === null) {
592 21
            return $_GET;
593
        }
594
595 7
        return $this->_queryParams;
596
    }
597
598
    /**
599
     * Sets the request [[queryString]] parameters.
600
     * @param array $values the request query parameters (name-value pairs)
601
     * @see getQueryParam()
602
     * @see getQueryParams()
603
     */
604 7
    public function setQueryParams($values)
605
    {
606 7
        $this->_queryParams = $values;
607 7
    }
608
609
    /**
610
     * Returns GET parameter with a given name. If name isn't specified, returns an array of all GET parameters.
611
     *
612
     * @param string $name the parameter name
613
     * @param mixed $defaultValue the default parameter value if the parameter does not exist.
614
     * @return array|mixed
615
     */
616 14
    public function get($name = null, $defaultValue = null)
617
    {
618 14
        if ($name === null) {
619
            return $this->getQueryParams();
620
        }
621
622 14
        return $this->getQueryParam($name, $defaultValue);
623
    }
624
625
    /**
626
     * Returns the named GET parameter value.
627
     * If the GET parameter does not exist, the second parameter passed to this method will be returned.
628
     * @param string $name the GET parameter name.
629
     * @param mixed $defaultValue the default parameter value if the GET parameter does not exist.
630
     * @return mixed the GET parameter value
631
     * @see getBodyParam()
632
     */
633 17
    public function getQueryParam($name, $defaultValue = null)
634
    {
635 17
        $params = $this->getQueryParams();
636
637 17
        return isset($params[$name]) ? $params[$name] : $defaultValue;
638
    }
639
640
    private $_hostInfo;
641
    private $_hostName;
642
643
    /**
644
     * Returns the schema and host part of the current request URL.
645
     *
646
     * The returned URL does not have an ending slash.
647
     *
648
     * By default this value is based on the user request information. This method will
649
     * return the value of `$_SERVER['HTTP_HOST']` if it is available or `$_SERVER['SERVER_NAME']` if not.
650
     * You may want to check out the [PHP documentation](http://php.net/manual/en/reserved.variables.server.php)
651
     * for more information on these variables.
652
     *
653
     * You may explicitly specify it by setting the [[setHostInfo()|hostInfo]] property.
654
     *
655
     * > Warning: Dependent on the server configuration this information may not be
656
     * > reliable and [may be faked by the user sending the HTTP request](https://www.acunetix.com/vulnerabilities/web/host-header-attack).
657
     * > If the webserver is configured to serve the same site independent of the value of
658
     * > the `Host` header, this value is not reliable. In such situations you should either
659
     * > fix your webserver configuration or explicitly set the value by setting the [[setHostInfo()|hostInfo]] property.
660
     * > If you don't have access to the server configuration, you can setup [[\yii\filters\HostControl]] filter at
661
     * > application level in order to protect against such kind of attack.
662
     *
663
     * @property string|null schema and hostname part (with port number if needed) of the request URL
664
     * (e.g. `http://www.yiiframework.com`), null if can't be obtained from `$_SERVER` and wasn't set.
665
     * See [[getHostInfo()]] for security related notes on this property.
666
     * @return string|null schema and hostname part (with port number if needed) of the request URL
667
     * (e.g. `http://www.yiiframework.com`), null if can't be obtained from `$_SERVER` and wasn't set.
668
     * @see setHostInfo()
669
     */
670 24
    public function getHostInfo()
671
    {
672 24
        if ($this->_hostInfo === null) {
673 20
            $secure = $this->getIsSecureConnection();
674 20
            $http = $secure ? 'https' : 'http';
675 20
            if ($this->headers->has('Host')) {
676 7
                $this->_hostInfo = $http . '://' . $this->headers->get('Host');
677 13
            } elseif (isset($_SERVER['SERVER_NAME'])) {
678
                $this->_hostInfo = $http . '://' . $_SERVER['SERVER_NAME'];
679
                $port = $secure ? $this->getSecurePort() : $this->getPort();
680
                if (($port !== 80 && !$secure) || ($port !== 443 && $secure)) {
681
                    $this->_hostInfo .= ':' . $port;
682
                }
683
            }
684
        }
685
686 24
        return $this->_hostInfo;
687
    }
688
689
    /**
690
     * Sets the schema and host part of the application URL.
691
     * This setter is provided in case the schema and hostname cannot be determined
692
     * on certain Web servers.
693
     * @param string|null $value the schema and host part of the application URL. The trailing slashes will be removed.
694
     * @see getHostInfo() for security related notes on this property.
695
     */
696 57
    public function setHostInfo($value)
697
    {
698 57
        $this->_hostName = null;
699 57
        $this->_hostInfo = $value === null ? null : rtrim($value, '/');
700 57
    }
701
702
    /**
703
     * Returns the host part of the current request URL.
704
     * Value is calculated from current [[getHostInfo()|hostInfo]] property.
705
     *
706
     * > Warning: The content of this value may not be reliable, dependent on the server
707
     * > configuration. Please refer to [[getHostInfo()]] for more information.
708
     *
709
     * @return string|null hostname part of the request URL (e.g. `www.yiiframework.com`)
710
     * @see getHostInfo()
711
     * @since 2.0.10
712
     */
713 11
    public function getHostName()
714
    {
715 11
        if ($this->_hostName === null) {
716 11
            $this->_hostName = parse_url($this->getHostInfo(), PHP_URL_HOST);
717
        }
718
719 11
        return $this->_hostName;
720
    }
721
722
    private $_baseUrl;
723
724
    /**
725
     * Returns the relative URL for the application.
726
     * This is similar to [[scriptUrl]] except that it does not include the script file name,
727
     * and the ending slashes are removed.
728
     * @return string the relative URL for the application
729
     * @see setScriptUrl()
730
     */
731 250
    public function getBaseUrl()
732
    {
733 250
        if ($this->_baseUrl === null) {
734 249
            $this->_baseUrl = rtrim(dirname($this->getScriptUrl()), '\\/');
735
        }
736
737 250
        return $this->_baseUrl;
738
    }
739
740
    /**
741
     * Sets the relative URL for the application.
742
     * By default the URL is determined based on the entry script URL.
743
     * This setter is provided in case you want to change this behavior.
744
     * @param string $value the relative URL for the application
745
     */
746 1
    public function setBaseUrl($value)
747
    {
748 1
        $this->_baseUrl = $value;
749 1
    }
750
751
    private $_scriptUrl;
752
753
    /**
754
     * Returns the relative URL of the entry script.
755
     * The implementation of this method referenced Zend_Controller_Request_Http in Zend Framework.
756
     * @return string the relative URL of the entry script.
757
     * @throws InvalidConfigException if unable to determine the entry script URL
758
     */
759 251
    public function getScriptUrl()
760
    {
761 251
        if ($this->_scriptUrl === null) {
762 1
            $scriptFile = $this->getScriptFile();
763
            $scriptName = basename($scriptFile);
764
            if (isset($_SERVER['SCRIPT_NAME']) && basename($_SERVER['SCRIPT_NAME']) === $scriptName) {
765
                $this->_scriptUrl = $_SERVER['SCRIPT_NAME'];
766
            } elseif (isset($_SERVER['PHP_SELF']) && basename($_SERVER['PHP_SELF']) === $scriptName) {
767
                $this->_scriptUrl = $_SERVER['PHP_SELF'];
768
            } elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $scriptName) {
769
                $this->_scriptUrl = $_SERVER['ORIG_SCRIPT_NAME'];
770
            } elseif (isset($_SERVER['PHP_SELF']) && ($pos = strpos($_SERVER['PHP_SELF'], '/' . $scriptName)) !== false) {
771
                $this->_scriptUrl = substr($_SERVER['SCRIPT_NAME'], 0, $pos) . '/' . $scriptName;
772
            } elseif (!empty($_SERVER['DOCUMENT_ROOT']) && strpos($scriptFile, $_SERVER['DOCUMENT_ROOT']) === 0) {
773
                $this->_scriptUrl = str_replace('\\', '/', str_replace($_SERVER['DOCUMENT_ROOT'], '', $scriptFile));
774
            } else {
775
                throw new InvalidConfigException('Unable to determine the entry script URL.');
776
            }
777
        }
778
779 250
        return $this->_scriptUrl;
780
    }
781
782
    /**
783
     * Sets the relative URL for the application entry script.
784
     * This setter is provided in case the entry script URL cannot be determined
785
     * on certain Web servers.
786
     * @param string $value the relative URL for the application entry script.
787
     */
788 261
    public function setScriptUrl($value)
789
    {
790 261
        $this->_scriptUrl = $value === null ? null : '/' . trim($value, '/');
791 261
    }
792
793
    private $_scriptFile;
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
794
795
    /**
796
     * Returns the entry script file path.
797
     * The default implementation will simply return `$_SERVER['SCRIPT_FILENAME']`.
798
     * @return string the entry script file path
799
     * @throws InvalidConfigException
800
     */
801 252
    public function getScriptFile()
802
    {
803 252
        if (isset($this->_scriptFile)) {
804 230
            return $this->_scriptFile;
805
        }
806
807 22
        if (isset($_SERVER['SCRIPT_FILENAME'])) {
808 20
            return $_SERVER['SCRIPT_FILENAME'];
809
        }
810
811 2
        throw new InvalidConfigException('Unable to determine the entry script file path.');
812
    }
813
814
    /**
815
     * Sets the entry script file path.
816
     * The entry script file path normally can be obtained from `$_SERVER['SCRIPT_FILENAME']`.
817
     * If your server configuration does not return the correct value, you may configure
818
     * this property to make it right.
819
     * @param string $value the entry script file path.
820
     */
821 230
    public function setScriptFile($value)
822
    {
823 230
        $this->_scriptFile = $value;
824 230
    }
825
826
    private $_pathInfo;
827
828
    /**
829
     * Returns the path info of the currently requested URL.
830
     * A path info refers to the part that is after the entry script and before the question mark (query string).
831
     * The starting and ending slashes are both removed.
832
     * @return string part of the request URL that is after the entry script and before the question mark.
833
     * Note, the returned path info is already URL-decoded.
834
     * @throws InvalidConfigException if the path info cannot be determined due to unexpected server configuration
835
     */
836 18
    public function getPathInfo()
837
    {
838 18
        if ($this->_pathInfo === null) {
839
            $this->_pathInfo = $this->resolvePathInfo();
840
        }
841
842 18
        return $this->_pathInfo;
843
    }
844
845
    /**
846
     * Sets the path info of the current request.
847
     * This method is mainly provided for testing purpose.
848
     * @param string $value the path info of the current request
849
     */
850 19
    public function setPathInfo($value)
851
    {
852 19
        $this->_pathInfo = $value === null ? null : ltrim($value, '/');
853 19
    }
854
855
    /**
856
     * Resolves the path info part of the currently requested URL.
857
     * A path info refers to the part that is after the entry script and before the question mark (query string).
858
     * The starting slashes are both removed (ending slashes will be kept).
859
     * @return string part of the request URL that is after the entry script and before the question mark.
860
     * Note, the returned path info is decoded.
861
     * @throws InvalidConfigException if the path info cannot be determined due to unexpected server configuration
862
     */
863
    protected function resolvePathInfo()
864
    {
865
        $pathInfo = $this->getUrl();
866
867
        if (($pos = strpos($pathInfo, '?')) !== false) {
868
            $pathInfo = substr($pathInfo, 0, $pos);
869
        }
870
871
        $pathInfo = urldecode($pathInfo);
872
873
        // try to encode in UTF8 if not so
874
        // http://w3.org/International/questions/qa-forms-utf-8.html
875
        if (!preg_match('%^(?:
876
            [\x09\x0A\x0D\x20-\x7E]              # ASCII
877
            | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
878
            | \xE0[\xA0-\xBF][\x80-\xBF]         # excluding overlongs
879
            | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
880
            | \xED[\x80-\x9F][\x80-\xBF]         # excluding surrogates
881
            | \xF0[\x90-\xBF][\x80-\xBF]{2}      # planes 1-3
882
            | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
883
            | \xF4[\x80-\x8F][\x80-\xBF]{2}      # plane 16
884
            )*$%xs', $pathInfo)
885
        ) {
886
            $pathInfo = utf8_encode($pathInfo);
887
        }
888
889
        $scriptUrl = $this->getScriptUrl();
890
        $baseUrl = $this->getBaseUrl();
891
        if (strpos($pathInfo, $scriptUrl) === 0) {
892
            $pathInfo = substr($pathInfo, strlen($scriptUrl));
893
        } elseif ($baseUrl === '' || strpos($pathInfo, $baseUrl) === 0) {
894
            $pathInfo = substr($pathInfo, strlen($baseUrl));
895
        } elseif (isset($_SERVER['PHP_SELF']) && strpos($_SERVER['PHP_SELF'], $scriptUrl) === 0) {
896
            $pathInfo = substr($_SERVER['PHP_SELF'], strlen($scriptUrl));
897
        } else {
898
            throw new InvalidConfigException('Unable to determine the path info of the current request.');
899
        }
900
901
        if (substr($pathInfo, 0, 1) === '/') {
902
            $pathInfo = substr($pathInfo, 1);
903
        }
904
905
        return (string) $pathInfo;
906
    }
907
908
    /**
909
     * Returns the currently requested absolute URL.
910
     * This is a shortcut to the concatenation of [[hostInfo]] and [[url]].
911
     * @return string the currently requested absolute URL.
912
     */
913
    public function getAbsoluteUrl()
914
    {
915
        return $this->getHostInfo() . $this->getUrl();
916
    }
917
918
    private $_url;
919
920
    /**
921
     * Returns the currently requested relative URL.
922
     * This refers to the portion of the URL that is after the [[hostInfo]] part.
923
     * It includes the [[queryString]] part if any.
924
     * @return string the currently requested relative URL. Note that the URI returned may be URL-encoded depending on the client.
925
     * @throws InvalidConfigException if the URL cannot be determined due to unusual server configuration
926
     */
927 11
    public function getUrl()
928
    {
929 11
        if ($this->_url === null) {
930 3
            $this->_url = $this->resolveRequestUri();
931
        }
932
933 11
        return $this->_url;
934
    }
935
936
    /**
937
     * Sets the currently requested relative URL.
938
     * The URI must refer to the portion that is after [[hostInfo]].
939
     * Note that the URI should be URL-encoded.
940
     * @param string $value the request URI to be set
941
     */
942 24
    public function setUrl($value)
943
    {
944 24
        $this->_url = $value;
945 24
    }
946
947
    /**
948
     * Resolves the request URI portion for the currently requested URL.
949
     * This refers to the portion that is after the [[hostInfo]] part. It includes the [[queryString]] part if any.
950
     * The implementation of this method referenced Zend_Controller_Request_Http in Zend Framework.
951
     * @return string|bool the request URI portion for the currently requested URL.
952
     * Note that the URI returned may be URL-encoded depending on the client.
953
     * @throws InvalidConfigException if the request URI cannot be determined due to unusual server configuration
954
     */
955 3
    protected function resolveRequestUri()
956
    {
957 3
        if ($this->headers->has('X-Rewrite-Url')) { // IIS
958
            $requestUri = $this->headers->get('X-Rewrite-Url');
959 3
        } elseif (isset($_SERVER['REQUEST_URI'])) {
960 3
            $requestUri = $_SERVER['REQUEST_URI'];
961 3
            if ($requestUri !== '' && $requestUri[0] !== '/') {
962 3
                $requestUri = preg_replace('/^(http|https):\/\/[^\/]+/i', '', $requestUri);
963
            }
964
        } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { // IIS 5.0 CGI
965
            $requestUri = $_SERVER['ORIG_PATH_INFO'];
966
            if (!empty($_SERVER['QUERY_STRING'])) {
967
                $requestUri .= '?' . $_SERVER['QUERY_STRING'];
968
            }
969
        } else {
970
            throw new InvalidConfigException('Unable to determine the request URI.');
971
        }
972
973 3
        return $requestUri;
974
    }
975
976
    /**
977
     * Returns part of the request URL that is after the question mark.
978
     * @return string part of the request URL that is after the question mark
979
     */
980
    public function getQueryString()
981
    {
982
        return isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '';
983
    }
984
985
    /**
986
     * Return if the request is sent via secure channel (https).
987
     * @return bool if the request is sent via secure channel (https)
988
     */
989 37
    public function getIsSecureConnection()
990
    {
991 37
        if (isset($_SERVER['HTTPS']) && (strcasecmp($_SERVER['HTTPS'], 'on') === 0 || $_SERVER['HTTPS'] == 1)) {
992 2
            return true;
993
        }
994 35
        foreach ($this->secureProtocolHeaders as $header => $values) {
995 35
            if (($headerValue = $this->headers->get($header, null)) !== null) {
996 4
                foreach ($values as $value) {
997 4
                    if (strcasecmp($headerValue, $value) === 0) {
998 35
                        return true;
999
                    }
1000
                }
1001
            }
1002
        }
1003 31
        return false;
1004
    }
1005
1006
    /**
1007
     * Returns the server name.
1008
     * @return string server name, null if not available
1009
     */
1010 1
    public function getServerName()
1011
    {
1012 1
        return isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : null;
1013
    }
1014
1015
    /**
1016
     * Returns the server port number.
1017
     * @return int|null server port number, null if not available
1018
     */
1019 1
    public function getServerPort()
1020
    {
1021 1
        return isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : null;
1022
    }
1023
1024
    /**
1025
     * Returns the URL referrer.
1026
     * @return string|null URL referrer, null if not available
1027
     */
1028
    public function getReferrer()
1029
    {
1030
        return $this->headers->get('Referer');
1031
    }
1032
1033
    /**
1034
     * Returns the URL origin of a CORS request.
1035
     *
1036
     * The return value is taken from the `Origin` [[getHeaders()|header]] sent by the browser.
1037
     *
1038
     * Note that the origin request header indicates where a fetch originates from.
1039
     * It doesn't include any path information, but only the server name.
1040
     * It is sent with a CORS requests, as well as with POST requests.
1041
     * It is similar to the referer header, but, unlike this header, it doesn't disclose the whole path.
1042
     * Please refer to <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin> for more information.
1043
     *
1044
     * @return string|null URL origin of a CORS request, `null` if not available.
1045
     * @see getHeaders()
1046
     * @since 2.0.13
1047
     */
1048 1
    public function getOrigin()
1049
    {
1050 1
        return $this->getHeaders()->get('origin');
1051
    }
1052
1053
    /**
1054
     * Returns the user agent.
1055
     * @return string|null user agent, null if not available
1056
     */
1057
    public function getUserAgent()
1058
    {
1059
        return $this->headers->get('User-Agent');
1060
    }
1061
1062
    /**
1063
     * Returns the user IP address.
1064
     * The IP is determined using headers and / or `$_SERVER` variables.
1065
     * @return string|null user IP address, null if not available
1066
     */
1067 32
    public function getUserIP()
1068
    {
1069 32
        foreach ($this->ipHeaders as $ipHeader) {
1070 32
            if ($this->headers->has($ipHeader)) {
1071 32
                return trim(explode(',', $this->headers->get($ipHeader))[0]);
1072
            }
1073
        }
1074 30
        return $this->getRemoteIP();
1075
    }
1076
1077
    /**
1078
     * Returns the user host name.
1079
     * The HOST is determined using headers and / or `$_SERVER` variables.
1080
     * @return string|null user host name, null if not available
1081
     */
1082
    public function getUserHost()
1083
    {
1084
        foreach ($this->ipHeaders as $ipHeader) {
1085
            if ($this->headers->has($ipHeader)) {
1086
                return gethostbyaddr(trim(explode(',', $this->headers->get($ipHeader))[0]));
1087
            }
1088
        }
1089
        return $this->getRemoteHost();
1090
    }
1091
1092
    /**
1093
     * Returns the IP on the other end of this connection.
1094
     * This is always the next hop, any headers are ignored.
1095
     * @return string|null remote IP address, `null` if not available.
1096
     * @since 2.0.13
1097
     */
1098 47
    public function getRemoteIP()
1099
    {
1100 47
        return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null;
1101
    }
1102
1103
    /**
1104
     * Returns the host name of the other end of this connection.
1105
     * This is always the next hop, any headers are ignored.
1106
     * @return string|null remote host name, `null` if not available
1107
     * @see getUserHost()
1108
     * @see getRemoteIP()
1109
     * @since 2.0.13
1110
     */
1111 19
    public function getRemoteHost()
1112
    {
1113 19
        return isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : null;
1114
    }
1115
1116
    /**
1117
     * @return string|null the username sent via HTTP authentication, null if the username is not given
1118
     */
1119 10
    public function getAuthUser()
1120
    {
1121 10
        return isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null;
1122
    }
1123
1124
    /**
1125
     * @return string|null the password sent via HTTP authentication, null if the password is not given
1126
     */
1127 10
    public function getAuthPassword()
1128
    {
1129 10
        return isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : null;
1130
    }
1131
1132
    private $_port;
1133
1134
    /**
1135
     * Returns the port to use for insecure requests.
1136
     * Defaults to 80, or the port specified by the server if the current
1137
     * request is insecure.
1138
     * @return int port number for insecure requests.
1139
     * @see setPort()
1140
     */
1141
    public function getPort()
1142
    {
1143
        if ($this->_port === null) {
1144
            $this->_port = !$this->getIsSecureConnection() && isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : 80;
1145
        }
1146
1147
        return $this->_port;
1148
    }
1149
1150
    /**
1151
     * Sets the port to use for insecure requests.
1152
     * This setter is provided in case a custom port is necessary for certain
1153
     * server configurations.
1154
     * @param int $value port number.
1155
     */
1156
    public function setPort($value)
1157
    {
1158
        if ($value != $this->_port) {
1159
            $this->_port = (int) $value;
1160
            $this->_hostInfo = null;
1161
        }
1162
    }
1163
1164
    private $_securePort;
1165
1166
    /**
1167
     * Returns the port to use for secure requests.
1168
     * Defaults to 443, or the port specified by the server if the current
1169
     * request is secure.
1170
     * @return int port number for secure requests.
1171
     * @see setSecurePort()
1172
     */
1173
    public function getSecurePort()
1174
    {
1175
        if ($this->_securePort === null) {
1176
            $this->_securePort = $this->getIsSecureConnection() && isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : 443;
1177
        }
1178
1179
        return $this->_securePort;
1180
    }
1181
1182
    /**
1183
     * Sets the port to use for secure requests.
1184
     * This setter is provided in case a custom port is necessary for certain
1185
     * server configurations.
1186
     * @param int $value port number.
1187
     */
1188
    public function setSecurePort($value)
1189
    {
1190
        if ($value != $this->_securePort) {
1191
            $this->_securePort = (int) $value;
1192
            $this->_hostInfo = null;
1193
        }
1194
    }
1195
1196
    private $_contentTypes;
1197
1198
    /**
1199
     * Returns the content types acceptable by the end user.
1200
     * This is determined by the `Accept` HTTP header. For example,
1201
     *
1202
     * ```php
1203
     * $_SERVER['HTTP_ACCEPT'] = 'text/plain; q=0.5, application/json; version=1.0, application/xml; version=2.0;';
1204
     * $types = $request->getAcceptableContentTypes();
1205
     * print_r($types);
1206
     * // displays:
1207
     * // [
1208
     * //     'application/json' => ['q' => 1, 'version' => '1.0'],
1209
     * //      'application/xml' => ['q' => 1, 'version' => '2.0'],
1210
     * //           'text/plain' => ['q' => 0.5],
1211
     * // ]
1212
     * ```
1213
     *
1214
     * @return array the content types ordered by the quality score. Types with the highest scores
1215
     * will be returned first. The array keys are the content types, while the array values
1216
     * are the corresponding quality score and other parameters as given in the header.
1217
     */
1218 2
    public function getAcceptableContentTypes()
1219
    {
1220 2
        if ($this->_contentTypes === null) {
1221 2
            if ($this->headers->get('Accept') !== null) {
1222 2
                $this->_contentTypes = $this->parseAcceptHeader($this->headers->get('Accept'));
0 ignored issues
show
Bug introduced by
It seems like $this->headers->get('Accept') targeting yii\web\HeaderCollection::get() can also be of type array; however, yii\web\Request::parseAcceptHeader() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1223
            } else {
1224 1
                $this->_contentTypes = [];
1225
            }
1226
        }
1227
1228 2
        return $this->_contentTypes;
1229
    }
1230
1231
    /**
1232
     * Sets the acceptable content types.
1233
     * Please refer to [[getAcceptableContentTypes()]] on the format of the parameter.
1234
     * @param array $value the content types that are acceptable by the end user. They should
1235
     * be ordered by the preference level.
1236
     * @see getAcceptableContentTypes()
1237
     * @see parseAcceptHeader()
1238
     */
1239
    public function setAcceptableContentTypes($value)
1240
    {
1241
        $this->_contentTypes = $value;
1242
    }
1243
1244
    /**
1245
     * Returns request content-type
1246
     * The Content-Type header field indicates the MIME type of the data
1247
     * contained in [[getRawBody()]] or, in the case of the HEAD method, the
1248
     * media type that would have been sent had the request been a GET.
1249
     * For the MIME-types the user expects in response, see [[acceptableContentTypes]].
1250
     * @return string request content-type. Null is returned if this information is not available.
1251
     * @link https://tools.ietf.org/html/rfc2616#section-14.17
1252
     * HTTP 1.1 header field definitions
1253
     */
1254
    public function getContentType()
1255
    {
1256
        if (isset($_SERVER['CONTENT_TYPE'])) {
1257
            return $_SERVER['CONTENT_TYPE'];
1258
        }
1259
1260
        //fix bug https://bugs.php.net/bug.php?id=66606
1261
        return $this->headers->get('Content-Type');
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->headers->get('Content-Type'); of type string|array adds the type array to the return on line 1261 which is incompatible with the return type documented by yii\web\Request::getContentType of type string.
Loading history...
1262
    }
1263
1264
    private $_languages;
1265
1266
    /**
1267
     * Returns the languages acceptable by the end user.
1268
     * This is determined by the `Accept-Language` HTTP header.
1269
     * @return array the languages ordered by the preference level. The first element
1270
     * represents the most preferred language.
1271
     */
1272 1
    public function getAcceptableLanguages()
1273
    {
1274 1
        if ($this->_languages === null) {
1275
            if ($this->headers->has('Accept-Language')) {
1276
                $this->_languages = array_keys($this->parseAcceptHeader($this->headers->get('Accept-Language')));
0 ignored issues
show
Bug introduced by
It seems like $this->headers->get('Accept-Language') targeting yii\web\HeaderCollection::get() can also be of type array; however, yii\web\Request::parseAcceptHeader() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1277
            } else {
1278
                $this->_languages = [];
1279
            }
1280
        }
1281
1282 1
        return $this->_languages;
1283
    }
1284
1285
    /**
1286
     * @param array $value the languages that are acceptable by the end user. They should
1287
     * be ordered by the preference level.
1288
     */
1289 1
    public function setAcceptableLanguages($value)
1290
    {
1291 1
        $this->_languages = $value;
1292 1
    }
1293
1294
    /**
1295
     * Parses the given `Accept` (or `Accept-Language`) header.
1296
     *
1297
     * This method will return the acceptable values with their quality scores and the corresponding parameters
1298
     * as specified in the given `Accept` header. The array keys of the return value are the acceptable values,
1299
     * while the array values consisting of the corresponding quality scores and parameters. The acceptable
1300
     * values with the highest quality scores will be returned first. For example,
1301
     *
1302
     * ```php
1303
     * $header = 'text/plain; q=0.5, application/json; version=1.0, application/xml; version=2.0;';
1304
     * $accepts = $request->parseAcceptHeader($header);
1305
     * print_r($accepts);
1306
     * // displays:
1307
     * // [
1308
     * //     'application/json' => ['q' => 1, 'version' => '1.0'],
1309
     * //      'application/xml' => ['q' => 1, 'version' => '2.0'],
1310
     * //           'text/plain' => ['q' => 0.5],
1311
     * // ]
1312
     * ```
1313
     *
1314
     * @param string $header the header to be parsed
1315
     * @return array the acceptable values ordered by their quality score. The values with the highest scores
1316
     * will be returned first.
1317
     */
1318 3
    public function parseAcceptHeader($header)
1319
    {
1320 3
        $accepts = [];
1321 3
        foreach (explode(',', $header) as $i => $part) {
1322 3
            $params = preg_split('/\s*;\s*/', trim($part), -1, PREG_SPLIT_NO_EMPTY);
1323 3
            if (empty($params)) {
1324 1
                continue;
1325
            }
1326
            $values = [
1327 3
                'q' => [$i, array_shift($params), 1],
1328
            ];
1329 3
            foreach ($params as $param) {
1330 2
                if (strpos($param, '=') !== false) {
1331 2
                    list($key, $value) = explode('=', $param, 2);
1332 2
                    if ($key === 'q') {
1333 2
                        $values['q'][2] = (float) $value;
1334
                    } else {
1335 2
                        $values[$key] = $value;
1336
                    }
1337
                } else {
1338 2
                    $values[] = $param;
1339
                }
1340
            }
1341 3
            $accepts[] = $values;
1342
        }
1343
1344 3
        usort($accepts, function ($a, $b) {
1345 3
            $a = $a['q']; // index, name, q
1346 3
            $b = $b['q'];
1347 3
            if ($a[2] > $b[2]) {
1348 2
                return -1;
1349
            }
1350
1351 2
            if ($a[2] < $b[2]) {
1352 1
                return 1;
1353
            }
1354
1355 2
            if ($a[1] === $b[1]) {
1356
                return $a[0] > $b[0] ? 1 : -1;
1357
            }
1358
1359 2
            if ($a[1] === '*/*') {
1360
                return 1;
1361
            }
1362
1363 2
            if ($b[1] === '*/*') {
1364
                return -1;
1365
            }
1366
1367 2
            $wa = $a[1][strlen($a[1]) - 1] === '*';
1368 2
            $wb = $b[1][strlen($b[1]) - 1] === '*';
1369 2
            if ($wa xor $wb) {
1370
                return $wa ? 1 : -1;
1371
            }
1372
1373 2
            return $a[0] > $b[0] ? 1 : -1;
1374 3
        });
1375
1376 3
        $result = [];
1377 3
        foreach ($accepts as $accept) {
1378 3
            $name = $accept['q'][1];
1379 3
            $accept['q'] = $accept['q'][2];
1380 3
            $result[$name] = $accept;
1381
        }
1382
1383 3
        return $result;
1384
    }
1385
1386
    /**
1387
     * Returns the user-preferred language that should be used by this application.
1388
     * The language resolution is based on the user preferred languages and the languages
1389
     * supported by the application. The method will try to find the best match.
1390
     * @param array $languages a list of the languages supported by the application. If this is empty, the current
1391
     * application language will be returned without further processing.
1392
     * @return string the language that the application should use.
1393
     */
1394 1
    public function getPreferredLanguage(array $languages = [])
1395
    {
1396 1
        if (empty($languages)) {
1397 1
            return Yii::$app->language;
1398
        }
1399 1
        foreach ($this->getAcceptableLanguages() as $acceptableLanguage) {
1400 1
            $acceptableLanguage = str_replace('_', '-', strtolower($acceptableLanguage));
1401 1
            foreach ($languages as $language) {
1402 1
                $normalizedLanguage = str_replace('_', '-', strtolower($language));
1403
1404
                if (
1405 1
                    $normalizedLanguage === $acceptableLanguage // en-us==en-us
1406 1
                    || strpos($acceptableLanguage, $normalizedLanguage . '-') === 0 // en==en-us
1407 1
                    || strpos($normalizedLanguage, $acceptableLanguage . '-') === 0 // en-us==en
1408
                ) {
1409 1
                    return $language;
1410
                }
1411
            }
1412
        }
1413
1414 1
        return reset($languages);
1415
    }
1416
1417
    /**
1418
     * Gets the Etags.
1419
     *
1420
     * @return array The entity tags
1421
     */
1422
    public function getETags()
1423
    {
1424
        if ($this->headers->has('If-None-Match')) {
1425
            return preg_split('/[\s,]+/', str_replace('-gzip', '', $this->headers->get('If-None-Match')), -1, PREG_SPLIT_NO_EMPTY);
1426
        }
1427
1428
        return [];
1429
    }
1430
1431
    /**
1432
     * Returns the cookie collection.
1433
     * Through the returned cookie collection, you may access a cookie using the following syntax:
1434
     *
1435
     * ```php
1436
     * $cookie = $request->cookies['name']
1437
     * if ($cookie !== null) {
1438
     *     $value = $cookie->value;
1439
     * }
1440
     *
1441
     * // alternatively
1442
     * $value = $request->cookies->getValue('name');
1443
     * ```
1444
     *
1445
     * @return CookieCollection the cookie collection.
1446
     */
1447 31
    public function getCookies()
1448
    {
1449 31
        if ($this->_cookies === null) {
1450 31
            $this->_cookies = new CookieCollection($this->loadCookies(), [
1451 31
                'readOnly' => true,
1452
            ]);
1453
        }
1454
1455 31
        return $this->_cookies;
1456
    }
1457
1458
    /**
1459
     * Converts `$_COOKIE` into an array of [[Cookie]].
1460
     * @return array the cookies obtained from request
1461
     * @throws InvalidConfigException if [[cookieValidationKey]] is not set when [[enableCookieValidation]] is true
1462
     */
1463 31
    protected function loadCookies()
1464
    {
1465 31
        $cookies = [];
1466 31
        if ($this->enableCookieValidation) {
1467 31
            if ($this->cookieValidationKey == '') {
1468
                throw new InvalidConfigException(get_class($this) . '::cookieValidationKey must be configured with a secret key.');
1469
            }
1470 31
            foreach ($_COOKIE as $name => $value) {
1471
                if (!is_string($value)) {
1472
                    continue;
1473
                }
1474
                $data = Yii::$app->getSecurity()->validateData($value, $this->cookieValidationKey);
1475
                if ($data === false) {
1476
                    continue;
1477
                }
1478
                $data = @unserialize($data);
1479
                if (is_array($data) && isset($data[0], $data[1]) && $data[0] === $name) {
1480
                    $cookies[$name] = new Cookie([
1481 31
                        'name' => $name,
1482
                        'value' => $data[1],
1483
                        'expire' => null,
1484
                    ]);
1485
                }
1486
            }
1487
        } else {
1488
            foreach ($_COOKIE as $name => $value) {
1489
                $cookies[$name] = new Cookie([
1490
                    'name' => $name,
1491
                    'value' => $value,
1492
                    'expire' => null,
1493
                ]);
1494
            }
1495
        }
1496
1497 31
        return $cookies;
1498
    }
1499
1500
    private $_csrfToken;
1501
1502
    /**
1503
     * Returns the token used to perform CSRF validation.
1504
     *
1505
     * This token is generated in a way to prevent [BREACH attacks](http://breachattack.com/). It may be passed
1506
     * along via a hidden field of an HTML form or an HTTP header value to support CSRF validation.
1507
     * @param bool $regenerate whether to regenerate CSRF token. When this parameter is true, each time
1508
     * this method is called, a new CSRF token will be generated and persisted (in session or cookie).
1509
     * @return string the token used to perform CSRF validation.
1510
     */
1511 35
    public function getCsrfToken($regenerate = false)
1512
    {
1513 35
        if ($this->_csrfToken === null || $regenerate) {
1514 35
            if ($regenerate || ($token = $this->loadCsrfToken()) === null) {
1515 33
                $token = $this->generateCsrfToken();
1516
            }
1517 35
            $this->_csrfToken = Yii::$app->security->maskToken($token);
1518
        }
1519
1520 35
        return $this->_csrfToken;
1521
    }
1522
1523
    /**
1524
     * Loads the CSRF token from cookie or session.
1525
     * @return string the CSRF token loaded from cookie or session. Null is returned if the cookie or session
1526
     * does not have CSRF token.
1527
     */
1528 35
    protected function loadCsrfToken()
1529
    {
1530 35
        if ($this->enableCsrfCookie) {
1531 31
            return $this->getCookies()->getValue($this->csrfParam);
1532
        }
1533 4
        return Yii::$app->getSession()->get($this->csrfParam);
0 ignored issues
show
Bug introduced by
The method getSession does only exist in yii\web\Application, but not in yii\console\Application.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1534
    }
1535
1536
    /**
1537
     * Generates an unmasked random token used to perform CSRF validation.
1538
     * @return string the random token for CSRF validation.
1539
     */
1540 33
    protected function generateCsrfToken()
1541
    {
1542 33
        $token = Yii::$app->getSecurity()->generateRandomString();
1543 33
        if ($this->enableCsrfCookie) {
1544 31
            $cookie = $this->createCsrfCookie($token);
1545 31
            Yii::$app->getResponse()->getCookies()->add($cookie);
1546
        } else {
1547 2
            Yii::$app->getSession()->set($this->csrfParam, $token);
0 ignored issues
show
Bug introduced by
The method getSession does only exist in yii\web\Application, but not in yii\console\Application.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1548
        }
1549 33
        return $token;
1550
    }
1551
1552
    /**
1553
     * @return string the CSRF token sent via [[CSRF_HEADER]] by browser. Null is returned if no such header is sent.
1554
     */
1555 3
    public function getCsrfTokenFromHeader()
1556
    {
1557 3
        return $this->headers->get(static::CSRF_HEADER);
1558
    }
1559
1560
    /**
1561
     * Creates a cookie with a randomly generated CSRF token.
1562
     * Initial values specified in [[csrfCookie]] will be applied to the generated cookie.
1563
     * @param string $token the CSRF token
1564
     * @return Cookie the generated cookie
1565
     * @see enableCsrfValidation
1566
     */
1567 31
    protected function createCsrfCookie($token)
1568
    {
1569 31
        $options = $this->csrfCookie;
1570 31
        $options['name'] = $this->csrfParam;
1571 31
        $options['value'] = $token;
1572 31
        return new Cookie($options);
1573
    }
1574
1575
    /**
1576
     * Performs the CSRF validation.
1577
     *
1578
     * This method will validate the user-provided CSRF token by comparing it with the one stored in cookie or session.
1579
     * This method is mainly called in [[Controller::beforeAction()]].
1580
     *
1581
     * Note that the method will NOT perform CSRF validation if [[enableCsrfValidation]] is false or the HTTP method
1582
     * is among GET, HEAD or OPTIONS.
1583
     *
1584
     * @param string $clientSuppliedToken the user-provided CSRF token to be validated. If null, the token will be retrieved from
1585
     * the [[csrfParam]] POST field or HTTP header.
1586
     * This parameter is available since version 2.0.4.
1587
     * @return bool whether CSRF token is valid. If [[enableCsrfValidation]] is false, this method will return true.
1588
     */
1589 5
    public function validateCsrfToken($clientSuppliedToken = null)
1590
    {
1591 5
        $method = $this->getMethod();
1592
        // only validate CSRF token on non-"safe" methods https://tools.ietf.org/html/rfc2616#section-9.1.1
1593 5
        if (!$this->enableCsrfValidation || in_array($method, ['GET', 'HEAD', 'OPTIONS'], true)) {
1594 5
            return true;
1595
        }
1596
1597 3
        $trueToken = $this->getCsrfToken();
1598
1599 3
        if ($clientSuppliedToken !== null) {
1600 1
            return $this->validateCsrfTokenInternal($clientSuppliedToken, $trueToken);
1601
        }
1602
1603 3
        return $this->validateCsrfTokenInternal($this->getBodyParam($this->csrfParam), $trueToken)
1604 3
            || $this->validateCsrfTokenInternal($this->getCsrfTokenFromHeader(), $trueToken);
0 ignored issues
show
Bug introduced by
It seems like $this->getCsrfTokenFromHeader() targeting yii\web\Request::getCsrfTokenFromHeader() can also be of type array; however, yii\web\Request::validateCsrfTokenInternal() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1605
    }
1606
1607
    /**
1608
     * Validates CSRF token
1609
     *
1610
     * @param string $clientSuppliedToken The masked client-supplied token.
1611
     * @param string $trueToken The masked true token.
1612
     * @return bool
1613
     */
1614 3
    private function validateCsrfTokenInternal($clientSuppliedToken, $trueToken)
1615
    {
1616 3
        if (!is_string($clientSuppliedToken)) {
1617 3
            return false;
1618
        }
1619
1620 3
        $security = Yii::$app->security;
1621
1622 3
        return $security->unmaskToken($clientSuppliedToken) === $security->unmaskToken($trueToken);
1623
    }
1624
}
1625