Completed
Push — 2.1 ( 89c14b...2e0107 )
by
unknown
11:17
created

Request::setQueryParams()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 3
cts 3
cp 1
cc 1
eloc 2
nc 1
nop 1
crap 1
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 Psr\Http\Message\RequestInterface;
11
use Psr\Http\Message\StreamInterface;
12
use Psr\Http\Message\UriInterface;
13
use Yii;
14
use yii\base\InvalidConfigException;
15
use yii\di\Instance;
16
use yii\http\Cookie;
17
use yii\http\CookieCollection;
18
use yii\http\FileStream;
19
use yii\http\MessageTrait;
20
use yii\http\Uri;
21
22
/**
23
 * The web Request class represents an HTTP request
24
 *
25
 * It encapsulates the $_SERVER variable and resolves its inconsistency among different Web servers.
26
 * Also it provides an interface to retrieve request parameters from $_POST, $_GET, $_COOKIES and REST
27
 * parameters sent via other HTTP methods like PUT or DELETE.
28
 *
29
 * Request is configured as an application component in [[\yii\web\Application]] by default.
30
 * You can access that instance via `Yii::$app->request`.
31
 *
32
 * For more details and usage information on Request, see the [guide article on requests](guide:runtime-requests).
33
 *
34
 * @property string $absoluteUrl The currently requested absolute URL. This property is read-only.
35
 * @property array $acceptableContentTypes The content types ordered by the quality score. Types with the
36
 * highest scores will be returned first. The array keys are the content types, while the array values are the
37
 * corresponding quality score and other parameters as given in the header.
38
 * @property array $acceptableLanguages The languages ordered by the preference level. The first element
39
 * represents the most preferred language.
40
 * @property string|null $authPassword The password sent via HTTP authentication, null if the password is not
41
 * given. This property is read-only.
42
 * @property string|null $authUser The username sent via HTTP authentication, null if the username is not
43
 * given. This property is read-only.
44
 * @property string $baseUrl The relative URL for the application.
45
 * @property array $bodyParams The request parameters given in the request body.
46
 * @property string $contentType Request content-type. Null is returned if this information is not available.
47
 * This property is read-only.
48
 * @property CookieCollection $cookies The cookie collection. This property is read-only.
49
 * @property string $csrfToken The token used to perform CSRF validation. This property is read-only.
50
 * @property string $csrfTokenFromHeader The CSRF token sent via [[CSRF_HEADER]] by browser. Null is returned
51
 * if no such header is sent. This property is read-only.
52
 * @property array $eTags The entity tags. This property is read-only.
53
 * @property string|null $hostInfo Schema and hostname part (with port number if needed) of the request URL
54
 * (e.g. `http://www.yiiframework.com`), null if can't be obtained from `$_SERVER` and wasn't set. See
55
 * [[getHostInfo()]] for security related notes on this property.
56
 * @property string|null $hostName Hostname part of the request URL (e.g. `www.yiiframework.com`). This
57
 * property is read-only.
58
 * @property bool $isAjax Whether this is an AJAX (XMLHttpRequest) request. This property is read-only.
59
 * @property bool $isDelete Whether this is a DELETE request. This property is read-only.
60
 * @property bool $isFlash Whether this is an Adobe Flash or Adobe Flex request. This property is read-only.
61
 * @property bool $isGet Whether this is a GET request. This property is read-only.
62
 * @property bool $isHead Whether this is a HEAD request. This property is read-only.
63
 * @property bool $isOptions Whether this is a OPTIONS request. This property is read-only.
64
 * @property bool $isPatch Whether this is a PATCH request. This property is read-only.
65
 * @property bool $isPjax Whether this is a PJAX request. This property is read-only.
66
 * @property bool $isPost Whether this is a POST request. This property is read-only.
67
 * @property bool $isPut Whether this is a PUT request. This property is read-only.
68
 * @property bool $isSecureConnection If the request is sent via secure channel (https). This property is
69
 * read-only.
70
 * @property string $method Request method, such as GET, POST, HEAD, PUT, PATCH, DELETE. The value returned is
71
 * turned into upper case.
72
 * @property UriInterface $uri the URI instance.
73
 * @property mixed $requestTarget the message's request target.
74
 * @property string $pathInfo Part of the request URL that is after the entry script and before the question
75
 * mark. Note, the returned path info is already URL-decoded.
76
 * @property int $port Port number for insecure requests.
77
 * @property array $queryParams The request GET parameter values.
78
 * @property string $queryString Part of the request URL that is after the question mark. This property is
79
 * read-only.
80
 * @property string $rawBody The request body.
81
 * @property string|null $referrer URL referrer, null if not available. This property is read-only.
82
 * @property string|null $origin URL origin, null if not available. This property is read-only.
83
 * @property string $scriptFile The entry script file path.
84
 * @property string $scriptUrl The relative URL of the entry script.
85
 * @property int $securePort Port number for secure requests.
86
 * @property string $serverName Server name, null if not available. This property is read-only.
87
 * @property int|null $serverPort Server port number, null if not available. This property is read-only.
88
 * @property string $url The currently requested relative URL. Note that the URI returned may be URL-encoded
89
 * depending on the client.
90
 * @property string|null $userAgent User agent, null if not available. This property is read-only.
91
 * @property string|null $userHost User host name, null if not available. This property is read-only.
92
 * @property string|null $userIP User IP address, null if not available. This property is read-only.
93
 *
94
 * @author Qiang Xue <[email protected]>
95
 * @since 2.0
96
 */
97
class Request extends \yii\base\Request implements RequestInterface
98
{
99
    use MessageTrait;
100
101
    /**
102
     * The name of the HTTP header for sending CSRF token.
103
     */
104
    const CSRF_HEADER = 'X-CSRF-Token';
105
    /**
106
     * The length of the CSRF token mask.
107
     * @deprecated 2.0.12 The mask length is now equal to the token length.
108
     */
109
    const CSRF_MASK_LENGTH = 8;
110
111
    /**
112
     * @var bool whether to enable CSRF (Cross-Site Request Forgery) validation. Defaults to true.
113
     * When CSRF validation is enabled, forms submitted to an Yii Web application must be originated
114
     * from the same application. If not, a 400 HTTP exception will be raised.
115
     *
116
     * Note, this feature requires that the user client accepts cookie. Also, to use this feature,
117
     * forms submitted via POST method must contain a hidden input whose name is specified by [[csrfParam]].
118
     * You may use [[\yii\helpers\Html::beginForm()]] to generate his hidden input.
119
     *
120
     * In JavaScript, you may get the values of [[csrfParam]] and [[csrfToken]] via `yii.getCsrfParam()` and
121
     * `yii.getCsrfToken()`, respectively. The [[\yii\web\YiiAsset]] asset must be registered.
122
     * You also need to include CSRF meta tags in your pages by using [[\yii\helpers\Html::csrfMetaTags()]].
123
     *
124
     * @see Controller::enableCsrfValidation
125
     * @see http://en.wikipedia.org/wiki/Cross-site_request_forgery
126
     */
127
    public $enableCsrfValidation = true;
128
    /**
129
     * @var string the name of the token used to prevent CSRF. Defaults to '_csrf'.
130
     * This property is used only when [[enableCsrfValidation]] is true.
131
     */
132
    public $csrfParam = '_csrf';
133
    /**
134
     * @var array the configuration for creating the CSRF [[Cookie|cookie]]. This property is used only when
135
     * both [[enableCsrfValidation]] and [[enableCsrfCookie]] are true.
136
     */
137
    public $csrfCookie = ['httpOnly' => true];
138
    /**
139
     * @var bool whether to use cookie to persist CSRF token. If false, CSRF token will be stored
140
     * in session under the name of [[csrfParam]]. Note that while storing CSRF tokens in session increases
141
     * security, it requires starting a session for every page, which will degrade your site performance.
142
     */
143
    public $enableCsrfCookie = true;
144
    /**
145
     * @var bool whether cookies should be validated to ensure they are not tampered. Defaults to true.
146
     */
147
    public $enableCookieValidation = true;
148
    /**
149
     * @var string a secret key used for cookie validation. This property must be set if [[enableCookieValidation]] is true.
150
     */
151
    public $cookieValidationKey;
152
    /**
153
     * @var string the name of the POST parameter that is used to indicate if a request is a PUT, PATCH or DELETE
154
     * request tunneled through POST. Defaults to '_method'.
155
     * @see getMethod()
156
     * @see getBodyParams()
157
     */
158
    public $methodParam = '_method';
159
    /**
160
     * @var array the parsers for converting the raw HTTP request body into [[bodyParams]].
161
     * The array keys are the request `Content-Types`, and the array values are the
162
     * corresponding configurations for [[Yii::createObject|creating the parser objects]].
163
     * A parser must implement the [[RequestParserInterface]].
164
     *
165
     * To enable parsing for JSON requests you can use the [[JsonParser]] class like in the following example:
166
     *
167
     * ```
168
     * [
169
     *     'application/json' => \yii\web\JsonParser::class,
170
     * ]
171
     * ```
172
     *
173
     * To register a parser for parsing all request types you can use `'*'` as the array key.
174
     * This one will be used as a fallback in case no other types match.
175
     *
176
     * @see getBodyParams()
177
     */
178
    public $parsers = [];
179
180
    /**
181
     * @var CookieCollection Collection of request cookies.
182
     */
183
    private $_cookies;
184
    /**
185
     * @var string the HTTP method of the request.
186
     */
187
    private $_method;
188
    /**
189
     * @var UriInterface the URI instance associated with request.
190
     */
191
    private $_uri;
192
    /**
193
     * @var mixed the message's request target.
194
     */
195
    private $_requestTarget;
196
197
198
    /**
199
     * Resolves the current request into a route and the associated parameters.
200
     * @return array the first element is the route, and the second is the associated parameters.
201
     * @throws NotFoundHttpException if the request cannot be resolved.
202
     */
203 1
    public function resolve()
204
    {
205 1
        $result = Yii::$app->getUrlManager()->parseRequest($this);
206 1
        if ($result !== false) {
207 1
            [$route, $params] = $result;
0 ignored issues
show
Bug introduced by
The variable $route does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $params does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
208 1
            if ($this->_queryParams === null) {
209 1
                $_GET = $params + $_GET; // preserve numeric keys
210
            } else {
211 1
                $this->_queryParams = $params + $this->_queryParams;
212
            }
213 1
            return [$route, $this->getQueryParams()];
214
        }
215
216
        throw new NotFoundHttpException(Yii::t('yii', 'Page not found.'));
217
    }
218
219
    /**
220
     * Returns default message's headers, which should be present once [[headerCollection]] is instantiated.
221
     * @return string[][] an associative array of the message's headers.
222
     */
223 11
    protected function defaultHeaders()
224
    {
225 11
        if (function_exists('getallheaders')) {
226
            $headers = getallheaders();
227 11
        } elseif (function_exists('http_get_request_headers')) {
228
            $headers = http_get_request_headers();
229
        } else {
230 11
            $headers = [];
231 11
            foreach ($_SERVER as $name => $value) {
232 11
                if (strncmp($name, 'HTTP_', 5) === 0) {
233 2
                    $name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
234 11
                    $headers[$name] = $value;
235
                }
236
            }
237
        }
238
239 11
        foreach ($headers as $name => $value) {
240 2
            $headers[strtolower($name)] = (array)$value;
241
        }
242
243 11
        return $headers;
244
    }
245
246
    /**
247
     * {@inheritdoc}
248
     * @since 2.1.0
249
     */
250
    public function getRequestTarget()
251
    {
252
        if ($this->_requestTarget === null) {
253
            $this->_requestTarget = $this->getUri()->__toString();
254
        }
255
        return $this->_requestTarget;
256
    }
257
258
    /**
259
     * Specifies the message's request target
260
     * @param mixed $requestTarget the message's request target.
261
     * @since 2.1.0
262
     */
263
    public function setRequestTarget($requestTarget)
264
    {
265
        $this->_requestTarget = $requestTarget;
266
    }
267
268
    /**
269
     * {@inheritdoc}
270
     * @since 2.1.0
271
     */
272
    public function withRequestTarget($requestTarget)
273
    {
274
        if ($this->getRequestTarget() === $requestTarget) {
275
            return $this;
276
        }
277
278
        $newInstance = clone $this;
279
        $newInstance->setRequestTarget($requestTarget);
280
        return $newInstance;
281
    }
282
283
    /**
284
     * {@inheritdoc}
285
     */
286 21
    public function getMethod()
287
    {
288 21
        if ($this->_method === null) {
289 17
            if (isset($_POST[$this->methodParam])) {
290 1
                $this->_method = $_POST[$this->methodParam];
291 16
            } elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
292
                $this->_method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
293 16
            } elseif (isset($_SERVER['REQUEST_METHOD'])) {
294 1
                $this->_method = $_SERVER['REQUEST_METHOD'];
295
            } else {
296 16
                $this->_method = 'GET';
297
            }
298
        }
299 21
        return $this->_method;
300
    }
301
302
    /**
303
     * Specifies request HTTP method.
304
     * @param string $method case-sensitive HTTP method.
305
     * @since 2.1.0
306
     */
307 6
    public function setMethod($method)
308
    {
309 6
        $this->_method =  $method;
310 6
    }
311
312
    /**
313
     * {@inheritdoc}
314
     * @since 2.1.0
315
     */
316
    public function withMethod($method)
317
    {
318
        if ($this->getMethod() === $method) {
319
            return $this;
320
        }
321
322
        $newInstance = clone $this;
323
        $newInstance->setMethod($method);
324
        return $newInstance;
325
    }
326
327
    /**
328
     * {@inheritdoc}
329
     * @since 2.1.0
330
     */
331
    public function getUri()
332
    {
333
        if (!$this->_uri instanceof UriInterface) {
334
            if ($this->_uri === null) {
335
                $uri = new Uri(['string' => $this->getAbsoluteUrl()]);
336
            } elseif ($this->_uri instanceof \Closure) {
337
                $uri = call_user_func($this->_uri, $this);
338
            } else {
339
                $uri = $this->_uri;
340
            }
341
342
            $this->_uri = Instance::ensure($uri, UriInterface::class);
343
        }
344
        return $this->_uri;
345
    }
346
347
    /**
348
     * Specifies the URI instance.
349
     * @param UriInterface|\Closure|array $uri URI instance or its DI compatible configuration.
350
     * @since 2.1.0
351
     */
352
    public function setUri($uri)
353
    {
354
        $this->_uri = $uri;
0 ignored issues
show
Documentation Bug introduced by
It seems like $uri can also be of type object<Closure> or array. However, the property $_uri is declared as type object<Psr\Http\Message\UriInterface>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
355
    }
356
357
    /**
358
     * {@inheritdoc}
359
     * @since 2.1.0
360
     */
361
    public function withUri(UriInterface $uri, $preserveHost = false)
362
    {
363
        if ($this->getUri() === $uri) {
364
            return $this;
365
        }
366
367
        $newInstance = clone $this;
368
369
        $newInstance->setUri($uri);
370
        if (!$preserveHost) {
371
            return $newInstance->withHeader('host', $uri->getHost());
372
        }
373
        return $newInstance;
374
    }
375
376
    /**
377
     * Returns whether this is a GET request.
378
     * @return bool whether this is a GET request.
379
     */
380 2
    public function getIsGet()
381
    {
382 2
        return $this->getMethod() === 'GET';
383
    }
384
385
    /**
386
     * Returns whether this is an OPTIONS request.
387
     * @return bool whether this is a OPTIONS request.
388
     */
389
    public function getIsOptions()
390
    {
391
        return $this->getMethod() === 'OPTIONS';
392
    }
393
394
    /**
395
     * Returns whether this is a HEAD request.
396
     * @return bool whether this is a HEAD request.
397
     */
398 9
    public function getIsHead()
399
    {
400 9
        return $this->getMethod() === 'HEAD';
401
    }
402
403
    /**
404
     * Returns whether this is a POST request.
405
     * @return bool whether this is a POST request.
406
     */
407
    public function getIsPost()
408
    {
409
        return $this->getMethod() === 'POST';
410
    }
411
412
    /**
413
     * Returns whether this is a DELETE request.
414
     * @return bool whether this is a DELETE request.
415
     */
416
    public function getIsDelete()
417
    {
418
        return $this->getMethod() === 'DELETE';
419
    }
420
421
    /**
422
     * Returns whether this is a PUT request.
423
     * @return bool whether this is a PUT request.
424
     */
425
    public function getIsPut()
426
    {
427
        return $this->getMethod() === 'PUT';
428
    }
429
430
    /**
431
     * Returns whether this is a PATCH request.
432
     * @return bool whether this is a PATCH request.
433
     */
434
    public function getIsPatch()
435
    {
436
        return $this->getMethod() === 'PATCH';
437
    }
438
439
    /**
440
     * Returns whether this is an AJAX (XMLHttpRequest) request.
441
     *
442
     * Note that jQuery doesn't set the header in case of cross domain
443
     * requests: https://stackoverflow.com/questions/8163703/cross-domain-ajax-doesnt-send-x-requested-with-header
444
     *
445
     * @return bool whether this is an AJAX (XMLHttpRequest) request.
446
     */
447 10
    public function getIsAjax()
448
    {
449 10
        return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
450
    }
451
452
    /**
453
     * Returns whether this is a PJAX request
454
     * @return bool whether this is a PJAX request
455
     */
456 1
    public function getIsPjax()
457
    {
458 1
        return $this->getIsAjax() && !empty($_SERVER['HTTP_X_PJAX']);
459
    }
460
461
    /**
462
     * Returns whether this is an Adobe Flash or Flex request.
463
     * @return bool whether this is an Adobe Flash or Adobe Flex request.
464
     */
465
    public function getIsFlash()
466
    {
467
        return isset($_SERVER['HTTP_USER_AGENT']) &&
468
            (stripos($_SERVER['HTTP_USER_AGENT'], 'Shockwave') !== false || stripos($_SERVER['HTTP_USER_AGENT'], 'Flash') !== false);
469
    }
470
471
    /**
472
     * Returns default message body to be used in case it is not explicitly set.
473
     * @return StreamInterface default body instance.
474
     */
475
    protected function defaultBody()
476
    {
477
        return new FileStream([
478
            'filename' => 'php://input',
479
            'mode' => 'r',
480
        ]);
481
    }
482
483
    private $_rawBody;
484
485
    /**
486
     * Returns the raw HTTP request body.
487
     * @return string the request body
488
     */
489
    public function getRawBody()
490
    {
491
        if ($this->_rawBody === null) {
492
            $this->_rawBody = $this->getBody()->__toString();
493
        }
494
495
        return $this->_rawBody;
496
    }
497
498
    /**
499
     * Sets the raw HTTP request body, this method is mainly used by test scripts to simulate raw HTTP requests.
500
     * @param string $rawBody the request body
501
     */
502
    public function setRawBody($rawBody)
503
    {
504
        $this->_rawBody = $rawBody;
505
    }
506
507
    private $_bodyParams;
508
509
    /**
510
     * Returns the request parameters given in the request body.
511
     *
512
     * Request parameters are determined using the parsers configured in [[parsers]] property.
513
     * If no parsers are configured for the current [[contentType]] it uses the PHP function `mb_parse_str()`
514
     * to parse the [[rawBody|request body]].
515
     * @return array the request parameters given in the request body.
516
     * @throws \yii\base\InvalidConfigException if a registered parser does not implement the [[RequestParserInterface]].
517
     * @see getMethod()
518
     * @see getBodyParam()
519
     * @see setBodyParams()
520
     */
521 3
    public function getBodyParams()
522
    {
523 3
        if ($this->_bodyParams === null) {
524 1
            if (isset($_POST[$this->methodParam])) {
525
                $this->_bodyParams = $_POST;
526
                unset($this->_bodyParams[$this->methodParam]);
527
                return $this->_bodyParams;
528
            }
529
530 1
            $rawContentType = $this->getContentType();
531 1
            if (($pos = strpos($rawContentType, ';')) !== false) {
532
                // e.g. text/html; charset=UTF-8
533
                $contentType = substr($rawContentType, 0, $pos);
534
            } else {
535 1
                $contentType = $rawContentType;
536
            }
537
538 1
            if (isset($this->parsers[$contentType])) {
539
                $parser = Yii::createObject($this->parsers[$contentType]);
540
                if (!($parser instanceof RequestParserInterface)) {
541
                    throw new InvalidConfigException("The '$contentType' request parser is invalid. It must implement the yii\\web\\RequestParserInterface.");
542
                }
543
                $this->_bodyParams = $parser->parse($this->getRawBody(), $rawContentType);
544 1
            } elseif (isset($this->parsers['*'])) {
545
                $parser = Yii::createObject($this->parsers['*']);
546
                if (!($parser instanceof RequestParserInterface)) {
547
                    throw new InvalidConfigException('The fallback request parser is invalid. It must implement the yii\\web\\RequestParserInterface.');
548
                }
549
                $this->_bodyParams = $parser->parse($this->getRawBody(), $rawContentType);
550 1
            } elseif ($this->getMethod() === 'POST') {
551
                // PHP has already parsed the body so we have all params in $_POST
552 1
                $this->_bodyParams = $_POST;
553
            } else {
554
                $this->_bodyParams = [];
555
                mb_parse_str($this->getRawBody(), $this->_bodyParams);
556
            }
557
        }
558
559 3
        return $this->_bodyParams;
560
    }
561
562
    /**
563
     * Sets the request body parameters.
564
     * @param array $values the request body parameters (name-value pairs)
565
     * @see getBodyParam()
566
     * @see getBodyParams()
567
     */
568 2
    public function setBodyParams($values)
569
    {
570 2
        $this->_bodyParams = $values;
571 2
    }
572
573
    /**
574
     * Returns the named request body parameter value.
575
     * If the parameter does not exist, the second parameter passed to this method will be returned.
576
     * @param string $name the parameter name
577
     * @param mixed $defaultValue the default parameter value if the parameter does not exist.
578
     * @return mixed the parameter value
579
     * @see getBodyParams()
580
     * @see setBodyParams()
581
     */
582 3
    public function getBodyParam($name, $defaultValue = null)
583
    {
584 3
        $params = $this->getBodyParams();
585
586 3
        return isset($params[$name]) ? $params[$name] : $defaultValue;
587
    }
588
589
    /**
590
     * Returns POST parameter with a given name. If name isn't specified, returns an array of all POST parameters.
591
     *
592
     * @param string $name the parameter name
593
     * @param mixed $defaultValue the default parameter value if the parameter does not exist.
594
     * @return array|mixed
595
     */
596
    public function post($name = null, $defaultValue = null)
597
    {
598
        if ($name === null) {
599
            return $this->getBodyParams();
600
        }
601
602
        return $this->getBodyParam($name, $defaultValue);
603
    }
604
605
    private $_queryParams;
606
607
    /**
608
     * Returns the request parameters given in the [[queryString]].
609
     *
610
     * This method will return the contents of `$_GET` if params where not explicitly set.
611
     * @return array the request GET parameter values.
612
     * @see setQueryParams()
613
     */
614 29
    public function getQueryParams()
615
    {
616 29
        if ($this->_queryParams === null) {
617 23
            return $_GET;
618
        }
619
620 8
        return $this->_queryParams;
621
    }
622
623
    /**
624
     * Sets the request [[queryString]] parameters.
625
     * @param array $values the request query parameters (name-value pairs)
626
     * @see getQueryParam()
627
     * @see getQueryParams()
628
     */
629 8
    public function setQueryParams($values)
630
    {
631 8
        $this->_queryParams = $values;
632 8
    }
633
634
    /**
635
     * Returns GET parameter with a given name. If name isn't specified, returns an array of all GET parameters.
636
     *
637
     * @param string $name the parameter name
638
     * @param mixed $defaultValue the default parameter value if the parameter does not exist.
639
     * @return array|mixed
640
     */
641 15
    public function get($name = null, $defaultValue = null)
642
    {
643 15
        if ($name === null) {
644
            return $this->getQueryParams();
645
        }
646
647 15
        return $this->getQueryParam($name, $defaultValue);
648
    }
649
650
    /**
651
     * Returns the named GET parameter value.
652
     * If the GET parameter does not exist, the second parameter passed to this method will be returned.
653
     * @param string $name the GET parameter name.
654
     * @param mixed $defaultValue the default parameter value if the GET parameter does not exist.
655
     * @return mixed the GET parameter value
656
     * @see getBodyParam()
657
     */
658 20
    public function getQueryParam($name, $defaultValue = null)
659
    {
660 20
        $params = $this->getQueryParams();
661
662 20
        return isset($params[$name]) ? $params[$name] : $defaultValue;
663
    }
664
665
    private $_hostInfo;
666
    private $_hostName;
667
668
    /**
669
     * Returns the schema and host part of the current request URL.
670
     *
671
     * The returned URL does not have an ending slash.
672
     *
673
     * By default this value is based on the user request information. This method will
674
     * return the value of `$_SERVER['HTTP_HOST']` if it is available or `$_SERVER['SERVER_NAME']` if not.
675
     * You may want to check out the [PHP documentation](http://php.net/manual/en/reserved.variables.server.php)
676
     * for more information on these variables.
677
     *
678
     * You may explicitly specify it by setting the [[setHostInfo()|hostInfo]] property.
679
     *
680
     * > Warning: Dependent on the server configuration this information may not be
681
     * > reliable and [may be faked by the user sending the HTTP request](https://www.acunetix.com/vulnerabilities/web/host-header-attack).
682
     * > If the webserver is configured to serve the same site independent of the value of
683
     * > the `Host` header, this value is not reliable. In such situations you should either
684
     * > fix your webserver configuration or explicitly set the value by setting the [[setHostInfo()|hostInfo]] property.
685
     * > If you don't have access to the server configuration, you can setup [[\yii\filters\HostControl]] filter at
686
     * > application level in order to protect against such kind of attack.
687
     *
688
     * @property string|null schema and hostname part (with port number if needed) of the request URL
689
     * (e.g. `http://www.yiiframework.com`), null if can't be obtained from `$_SERVER` and wasn't set.
690
     * See [[getHostInfo()]] for security related notes on this property.
691
     * @return string|null schema and hostname part (with port number if needed) of the request URL
692
     * (e.g. `http://www.yiiframework.com`), null if can't be obtained from `$_SERVER` and wasn't set.
693
     * @see setHostInfo()
694
     */
695 24
    public function getHostInfo()
696
    {
697 24
        if ($this->_hostInfo === null) {
698 20
            $secure = $this->getIsSecureConnection();
699 20
            $http = $secure ? 'https' : 'http';
700 20
            if (isset($_SERVER['HTTP_HOST'])) {
701 7
                $this->_hostInfo = $http . '://' . $_SERVER['HTTP_HOST'];
702 13
            } elseif (isset($_SERVER['SERVER_NAME'])) {
703
                $this->_hostInfo = $http . '://' . $_SERVER['SERVER_NAME'];
704
                $port = $secure ? $this->getSecurePort() : $this->getPort();
705
                if (($port !== 80 && !$secure) || ($port !== 443 && $secure)) {
706
                    $this->_hostInfo .= ':' . $port;
707
                }
708
            }
709
        }
710
711 24
        return $this->_hostInfo;
712
    }
713
714
    /**
715
     * Sets the schema and host part of the application URL.
716
     * This setter is provided in case the schema and hostname cannot be determined
717
     * on certain Web servers.
718
     * @param string|null $value the schema and host part of the application URL. The trailing slashes will be removed.
719
     * @see getHostInfo() for security related notes on this property.
720
     */
721 57
    public function setHostInfo($value)
722
    {
723 57
        $this->_hostName = null;
724 57
        $this->_hostInfo = $value === null ? null : rtrim($value, '/');
725 57
    }
726
727
    /**
728
     * Returns the host part of the current request URL.
729
     * Value is calculated from current [[getHostInfo()|hostInfo]] property.
730
     *
731
     * > Warning: The content of this value may not be reliable, dependent on the server
732
     * > configuration. Please refer to [[getHostInfo()]] for more information.
733
     *
734
     * @return string|null hostname part of the request URL (e.g. `www.yiiframework.com`)
735
     * @see getHostInfo()
736
     * @since 2.0.10
737
     */
738 11
    public function getHostName()
739
    {
740 11
        if ($this->_hostName === null) {
741 11
            $this->_hostName = parse_url($this->getHostInfo(), PHP_URL_HOST);
742
        }
743
744 11
        return $this->_hostName;
745
    }
746
747
    private $_baseUrl;
748
749
    /**
750
     * Returns the relative URL for the application.
751
     * This is similar to [[scriptUrl]] except that it does not include the script file name,
752
     * and the ending slashes are removed.
753
     * @return string the relative URL for the application
754
     * @see setScriptUrl()
755
     */
756 253
    public function getBaseUrl()
757
    {
758 253
        if ($this->_baseUrl === null) {
759 252
            $this->_baseUrl = rtrim(dirname($this->getScriptUrl()), '\\/');
760
        }
761
762 253
        return $this->_baseUrl;
763
    }
764
765
    /**
766
     * Sets the relative URL for the application.
767
     * By default the URL is determined based on the entry script URL.
768
     * This setter is provided in case you want to change this behavior.
769
     * @param string $value the relative URL for the application
770
     */
771 1
    public function setBaseUrl($value)
772
    {
773 1
        $this->_baseUrl = $value;
774 1
    }
775
776
    private $_scriptUrl;
777
778
    /**
779
     * Returns the relative URL of the entry script.
780
     * The implementation of this method referenced Zend_Controller_Request_Http in Zend Framework.
781
     * @return string the relative URL of the entry script.
782
     * @throws InvalidConfigException if unable to determine the entry script URL
783
     */
784 254
    public function getScriptUrl()
785
    {
786 254
        if ($this->_scriptUrl === null) {
787 2
            $scriptFile = $this->getScriptFile();
788 1
            $scriptName = basename($scriptFile);
789 1
            if (isset($_SERVER['SCRIPT_NAME']) && basename($_SERVER['SCRIPT_NAME']) === $scriptName) {
790 1
                $this->_scriptUrl = $_SERVER['SCRIPT_NAME'];
791
            } elseif (isset($_SERVER['PHP_SELF']) && basename($_SERVER['PHP_SELF']) === $scriptName) {
792
                $this->_scriptUrl = $_SERVER['PHP_SELF'];
793
            } elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $scriptName) {
794
                $this->_scriptUrl = $_SERVER['ORIG_SCRIPT_NAME'];
795
            } elseif (isset($_SERVER['PHP_SELF']) && ($pos = strpos($_SERVER['PHP_SELF'], '/' . $scriptName)) !== false) {
796
                $this->_scriptUrl = substr($_SERVER['SCRIPT_NAME'], 0, $pos) . '/' . $scriptName;
797
            } elseif (!empty($_SERVER['DOCUMENT_ROOT']) && strpos($scriptFile, $_SERVER['DOCUMENT_ROOT']) === 0) {
798
                $this->_scriptUrl = str_replace('\\', '/', str_replace($_SERVER['DOCUMENT_ROOT'], '', $scriptFile));
799
            } else {
800
                throw new InvalidConfigException('Unable to determine the entry script URL.');
801
            }
802
        }
803
804 253
        return $this->_scriptUrl;
805
    }
806
807
    /**
808
     * Sets the relative URL for the application entry script.
809
     * This setter is provided in case the entry script URL cannot be determined
810
     * on certain Web servers.
811
     * @param string $value the relative URL for the application entry script.
812
     */
813 264
    public function setScriptUrl($value)
814
    {
815 264
        $this->_scriptUrl = $value === null ? null : '/' . trim($value, '/');
816 264
    }
817
818
    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...
819
820
    /**
821
     * Returns the entry script file path.
822
     * The default implementation will simply return `$_SERVER['SCRIPT_FILENAME']`.
823
     * @return string the entry script file path
824
     * @throws InvalidConfigException
825
     */
826 255
    public function getScriptFile()
827
    {
828 255
        if (isset($this->_scriptFile)) {
829 233
            return $this->_scriptFile;
830
        }
831
832 23
        if (isset($_SERVER['SCRIPT_FILENAME'])) {
833 21
            return $_SERVER['SCRIPT_FILENAME'];
834
        }
835
836 2
        throw new InvalidConfigException('Unable to determine the entry script file path.');
837
    }
838
839
    /**
840
     * Sets the entry script file path.
841
     * The entry script file path normally can be obtained from `$_SERVER['SCRIPT_FILENAME']`.
842
     * If your server configuration does not return the correct value, you may configure
843
     * this property to make it right.
844
     * @param string $value the entry script file path.
845
     */
846 233
    public function setScriptFile($value)
847
    {
848 233
        $this->_scriptFile = $value;
849 233
    }
850
851
    private $_pathInfo;
852
853
    /**
854
     * Returns the path info of the currently requested URL.
855
     * A path info refers to the part that is after the entry script and before the question mark (query string).
856
     * The starting and ending slashes are both removed.
857
     * @return string part of the request URL that is after the entry script and before the question mark.
858
     * Note, the returned path info is already URL-decoded.
859
     * @throws InvalidConfigException if the path info cannot be determined due to unexpected server configuration
860
     */
861 18
    public function getPathInfo()
862
    {
863 18
        if ($this->_pathInfo === null) {
864
            $this->_pathInfo = $this->resolvePathInfo();
865
        }
866
867 18
        return $this->_pathInfo;
868
    }
869
870
    /**
871
     * Sets the path info of the current request.
872
     * This method is mainly provided for testing purpose.
873
     * @param string $value the path info of the current request
874
     */
875 19
    public function setPathInfo($value)
876
    {
877 19
        $this->_pathInfo = $value === null ? null : ltrim($value, '/');
878 19
    }
879
880
    /**
881
     * Resolves the path info part of the currently requested URL.
882
     * A path info refers to the part that is after the entry script and before the question mark (query string).
883
     * The starting slashes are both removed (ending slashes will be kept).
884
     * @return string part of the request URL that is after the entry script and before the question mark.
885
     * Note, the returned path info is decoded.
886
     * @throws InvalidConfigException if the path info cannot be determined due to unexpected server configuration
887
     */
888
    protected function resolvePathInfo()
889
    {
890
        $pathInfo = $this->getUrl();
891
892
        if (($pos = strpos($pathInfo, '?')) !== false) {
893
            $pathInfo = substr($pathInfo, 0, $pos);
894
        }
895
896
        $pathInfo = urldecode($pathInfo);
897
898
        // try to encode in UTF8 if not so
899
        // http://w3.org/International/questions/qa-forms-utf-8.html
900
        if (!preg_match('%^(?:
901
            [\x09\x0A\x0D\x20-\x7E]              # ASCII
902
            | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
903
            | \xE0[\xA0-\xBF][\x80-\xBF]         # excluding overlongs
904
            | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
905
            | \xED[\x80-\x9F][\x80-\xBF]         # excluding surrogates
906
            | \xF0[\x90-\xBF][\x80-\xBF]{2}      # planes 1-3
907
            | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
908
            | \xF4[\x80-\x8F][\x80-\xBF]{2}      # plane 16
909
            )*$%xs', $pathInfo)
910
        ) {
911
            $pathInfo = utf8_encode($pathInfo);
912
        }
913
914
        $scriptUrl = $this->getScriptUrl();
915
        $baseUrl = $this->getBaseUrl();
916
        if (strpos($pathInfo, $scriptUrl) === 0) {
917
            $pathInfo = substr($pathInfo, strlen($scriptUrl));
918
        } elseif ($baseUrl === '' || strpos($pathInfo, $baseUrl) === 0) {
919
            $pathInfo = substr($pathInfo, strlen($baseUrl));
920
        } elseif (isset($_SERVER['PHP_SELF']) && strpos($_SERVER['PHP_SELF'], $scriptUrl) === 0) {
921
            $pathInfo = substr($_SERVER['PHP_SELF'], strlen($scriptUrl));
922
        } else {
923
            throw new InvalidConfigException('Unable to determine the path info of the current request.');
924
        }
925
926
        if (substr($pathInfo, 0, 1) === '/') {
927
            $pathInfo = substr($pathInfo, 1);
928
        }
929
930
        return (string) $pathInfo;
931
    }
932
933
    /**
934
     * Returns the currently requested absolute URL.
935
     * This is a shortcut to the concatenation of [[hostInfo]] and [[url]].
936
     * @return string the currently requested absolute URL.
937
     */
938
    public function getAbsoluteUrl()
939
    {
940
        return $this->getHostInfo() . $this->getUrl();
941
    }
942
943
    private $_url;
944
945
    /**
946
     * Returns the currently requested relative URL.
947
     * This refers to the portion of the URL that is after the [[hostInfo]] part.
948
     * It includes the [[queryString]] part if any.
949
     * @return string the currently requested relative URL. Note that the URI returned may be URL-encoded depending on the client.
950
     * @throws InvalidConfigException if the URL cannot be determined due to unusual server configuration
951
     */
952 11
    public function getUrl()
953
    {
954 11
        if ($this->_url === null) {
955 3
            $this->_url = $this->resolveRequestUri();
956
        }
957
958 11
        return $this->_url;
959
    }
960
961
    /**
962
     * Sets the currently requested relative URL.
963
     * The URI must refer to the portion that is after [[hostInfo]].
964
     * Note that the URI should be URL-encoded.
965
     * @param string $value the request URI to be set
966
     */
967 24
    public function setUrl($value)
968
    {
969 24
        $this->_url = $value;
970 24
    }
971
972
    /**
973
     * Resolves the request URI portion for the currently requested URL.
974
     * This refers to the portion that is after the [[hostInfo]] part. It includes the [[queryString]] part if any.
975
     * The implementation of this method referenced Zend_Controller_Request_Http in Zend Framework.
976
     * @return string|bool the request URI portion for the currently requested URL.
977
     * Note that the URI returned may be URL-encoded depending on the client.
978
     * @throws InvalidConfigException if the request URI cannot be determined due to unusual server configuration
979
     */
980 3
    protected function resolveRequestUri()
981
    {
982 3
        if (isset($_SERVER['HTTP_X_REWRITE_URL'])) { // IIS
983
            $requestUri = $_SERVER['HTTP_X_REWRITE_URL'];
984 3
        } elseif (isset($_SERVER['REQUEST_URI'])) {
985 3
            $requestUri = $_SERVER['REQUEST_URI'];
986 3
            if ($requestUri !== '' && $requestUri[0] !== '/') {
987 3
                $requestUri = preg_replace('/^(http|https):\/\/[^\/]+/i', '', $requestUri);
988
            }
989
        } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { // IIS 5.0 CGI
990
            $requestUri = $_SERVER['ORIG_PATH_INFO'];
991
            if (!empty($_SERVER['QUERY_STRING'])) {
992
                $requestUri .= '?' . $_SERVER['QUERY_STRING'];
993
            }
994
        } else {
995
            throw new InvalidConfigException('Unable to determine the request URI.');
996
        }
997
998 3
        return $requestUri;
999
    }
1000
1001
    /**
1002
     * Returns part of the request URL that is after the question mark.
1003
     * @return string part of the request URL that is after the question mark
1004
     */
1005
    public function getQueryString()
1006
    {
1007
        return isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '';
1008
    }
1009
1010
    /**
1011
     * Return if the request is sent via secure channel (https).
1012
     * @return bool if the request is sent via secure channel (https)
1013
     */
1014 20
    public function getIsSecureConnection()
1015
    {
1016 20
        return isset($_SERVER['HTTPS']) && (strcasecmp($_SERVER['HTTPS'], 'on') === 0 || $_SERVER['HTTPS'] == 1)
1017 20
            || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0;
1018
    }
1019
1020
    /**
1021
     * Returns the server name.
1022
     * @return string server name, null if not available
1023
     */
1024 1
    public function getServerName()
1025
    {
1026 1
        return isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : null;
1027
    }
1028
1029
    /**
1030
     * Returns the server port number.
1031
     * @return int|null server port number, null if not available
1032
     */
1033 1
    public function getServerPort()
1034
    {
1035 1
        return isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : null;
1036
    }
1037
1038
    /**
1039
     * Returns the URL referrer.
1040
     * @return string|null URL referrer, null if not available
1041
     */
1042
    public function getReferrer()
1043
    {
1044
        return isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : null;
1045
    }
1046
1047
    /**
1048
     * Returns the URL origin of a CORS request.
1049
     *
1050
     * The return value is taken from the `Origin` [[getHeaders()|header]] sent by the browser.
1051
     *
1052
     * Note that the origin request header indicates where a fetch originates from.
1053
     * It doesn't include any path information, but only the server name.
1054
     * It is sent with a CORS requests, as well as with POST requests.
1055
     * It is similar to the referer header, but, unlike this header, it doesn't disclose the whole path.
1056
     * Please refer to <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin> for more information.
1057
     *
1058
     * @return string|null URL origin of a CORS request, `null` if not available.
1059
     * @see getHeaders()
1060
     * @since 2.0.13
1061
     */
1062 1
    public function getOrigin()
1063
    {
1064 1
        return $this->getHeaderLine('origin');
1065
    }
1066
1067
    /**
1068
     * Returns the user agent.
1069
     * @return string|null user agent, null if not available
1070
     */
1071
    public function getUserAgent()
1072
    {
1073
        return isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null;
1074
    }
1075
1076
    /**
1077
     * Returns the user IP address.
1078
     * @return string|null user IP address, null if not available
1079
     */
1080 27
    public function getUserIP()
1081
    {
1082 27
        return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null;
1083
    }
1084
1085
    /**
1086
     * Returns the user host name.
1087
     * @return string|null user host name, null if not available
1088
     */
1089
    public function getUserHost()
1090
    {
1091
        return isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : null;
1092
    }
1093
1094
    /**
1095
     * @return string|null the username sent via HTTP authentication, null if the username is not given
1096
     */
1097 10
    public function getAuthUser()
1098
    {
1099 10
        return isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null;
1100
    }
1101
1102
    /**
1103
     * @return string|null the password sent via HTTP authentication, null if the password is not given
1104
     */
1105 10
    public function getAuthPassword()
1106
    {
1107 10
        return isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : null;
1108
    }
1109
1110
    private $_port;
1111
1112
    /**
1113
     * Returns the port to use for insecure requests.
1114
     * Defaults to 80, or the port specified by the server if the current
1115
     * request is insecure.
1116
     * @return int port number for insecure requests.
1117
     * @see setPort()
1118
     */
1119
    public function getPort()
1120
    {
1121
        if ($this->_port === null) {
1122
            $this->_port = !$this->getIsSecureConnection() && isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : 80;
1123
        }
1124
1125
        return $this->_port;
1126
    }
1127
1128
    /**
1129
     * Sets the port to use for insecure requests.
1130
     * This setter is provided in case a custom port is necessary for certain
1131
     * server configurations.
1132
     * @param int $value port number.
1133
     */
1134
    public function setPort($value)
1135
    {
1136
        if ($value != $this->_port) {
1137
            $this->_port = (int) $value;
1138
            $this->_hostInfo = null;
1139
        }
1140
    }
1141
1142
    private $_securePort;
1143
1144
    /**
1145
     * Returns the port to use for secure requests.
1146
     * Defaults to 443, or the port specified by the server if the current
1147
     * request is secure.
1148
     * @return int port number for secure requests.
1149
     * @see setSecurePort()
1150
     */
1151
    public function getSecurePort()
1152
    {
1153
        if ($this->_securePort === null) {
1154
            $this->_securePort = $this->getIsSecureConnection() && isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : 443;
1155
        }
1156
1157
        return $this->_securePort;
1158
    }
1159
1160
    /**
1161
     * Sets the port to use for secure requests.
1162
     * This setter is provided in case a custom port is necessary for certain
1163
     * server configurations.
1164
     * @param int $value port number.
1165
     */
1166
    public function setSecurePort($value)
1167
    {
1168
        if ($value != $this->_securePort) {
1169
            $this->_securePort = (int) $value;
1170
            $this->_hostInfo = null;
1171
        }
1172
    }
1173
1174
    private $_contentTypes;
1175
1176
    /**
1177
     * Returns the content types acceptable by the end user.
1178
     * This is determined by the `Accept` HTTP header. For example,
1179
     *
1180
     * ```php
1181
     * $_SERVER['HTTP_ACCEPT'] = 'text/plain; q=0.5, application/json; version=1.0, application/xml; version=2.0;';
1182
     * $types = $request->getAcceptableContentTypes();
1183
     * print_r($types);
1184
     * // displays:
1185
     * // [
1186
     * //     'application/json' => ['q' => 1, 'version' => '1.0'],
1187
     * //      'application/xml' => ['q' => 1, 'version' => '2.0'],
1188
     * //           'text/plain' => ['q' => 0.5],
1189
     * // ]
1190
     * ```
1191
     *
1192
     * @return array the content types ordered by the quality score. Types with the highest scores
1193
     * will be returned first. The array keys are the content types, while the array values
1194
     * are the corresponding quality score and other parameters as given in the header.
1195
     */
1196 3
    public function getAcceptableContentTypes()
1197
    {
1198 3
        if ($this->_contentTypes === null) {
1199 2
            if (isset($_SERVER['HTTP_ACCEPT'])) {
1200 2
                $this->_contentTypes = $this->parseAcceptHeader($_SERVER['HTTP_ACCEPT']);
1201
            } else {
1202 1
                $this->_contentTypes = [];
1203
            }
1204
        }
1205
1206 3
        return $this->_contentTypes;
1207
    }
1208
1209
    /**
1210
     * Sets the acceptable content types.
1211
     * Please refer to [[getAcceptableContentTypes()]] on the format of the parameter.
1212
     * @param array $value the content types that are acceptable by the end user. They should
1213
     * be ordered by the preference level.
1214
     * @see getAcceptableContentTypes()
1215
     * @see parseAcceptHeader()
1216
     */
1217 1
    public function setAcceptableContentTypes($value)
1218
    {
1219 1
        $this->_contentTypes = $value;
1220 1
    }
1221
1222
    /**
1223
     * Returns request content-type
1224
     * The Content-Type header field indicates the MIME type of the data
1225
     * contained in [[getRawBody()]] or, in the case of the HEAD method, the
1226
     * media type that would have been sent had the request been a GET.
1227
     * For the MIME-types the user expects in response, see [[acceptableContentTypes]].
1228
     * @return string request content-type. Null is returned if this information is not available.
1229
     * @link https://tools.ietf.org/html/rfc2616#section-14.17
1230
     * HTTP 1.1 header field definitions
1231
     */
1232 1
    public function getContentType()
1233
    {
1234 1
        if (isset($_SERVER['CONTENT_TYPE'])) {
1235
            return $_SERVER['CONTENT_TYPE'];
1236
        }
1237
1238 1
        if (isset($_SERVER['HTTP_CONTENT_TYPE'])) {
1239
            //fix bug https://bugs.php.net/bug.php?id=66606
1240
            return $_SERVER['HTTP_CONTENT_TYPE'];
1241
        }
1242
1243 1
        return null;
1244
    }
1245
1246
    private $_languages;
1247
1248
    /**
1249
     * Returns the languages acceptable by the end user.
1250
     * This is determined by the `Accept-Language` HTTP header.
1251
     * @return array the languages ordered by the preference level. The first element
1252
     * represents the most preferred language.
1253
     */
1254 1
    public function getAcceptableLanguages()
1255
    {
1256 1
        if ($this->_languages === null) {
1257
            if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
1258
                $this->_languages = array_keys($this->parseAcceptHeader($_SERVER['HTTP_ACCEPT_LANGUAGE']));
1259
            } else {
1260
                $this->_languages = [];
1261
            }
1262
        }
1263
1264 1
        return $this->_languages;
1265
    }
1266
1267
    /**
1268
     * @param array $value the languages that are acceptable by the end user. They should
1269
     * be ordered by the preference level.
1270
     */
1271 1
    public function setAcceptableLanguages($value)
1272
    {
1273 1
        $this->_languages = $value;
1274 1
    }
1275
1276
    /**
1277
     * Parses the given `Accept` (or `Accept-Language`) header.
1278
     *
1279
     * This method will return the acceptable values with their quality scores and the corresponding parameters
1280
     * as specified in the given `Accept` header. The array keys of the return value are the acceptable values,
1281
     * while the array values consisting of the corresponding quality scores and parameters. The acceptable
1282
     * values with the highest quality scores will be returned first. For example,
1283
     *
1284
     * ```php
1285
     * $header = 'text/plain; q=0.5, application/json; version=1.0, application/xml; version=2.0;';
1286
     * $accepts = $request->parseAcceptHeader($header);
1287
     * print_r($accepts);
1288
     * // displays:
1289
     * // [
1290
     * //     'application/json' => ['q' => 1, 'version' => '1.0'],
1291
     * //      'application/xml' => ['q' => 1, 'version' => '2.0'],
1292
     * //           'text/plain' => ['q' => 0.5],
1293
     * // ]
1294
     * ```
1295
     *
1296
     * @param string $header the header to be parsed
1297
     * @return array the acceptable values ordered by their quality score. The values with the highest scores
1298
     * will be returned first.
1299
     */
1300 3
    public function parseAcceptHeader($header)
1301
    {
1302 3
        $accepts = [];
1303 3
        foreach (explode(',', $header) as $i => $part) {
1304 3
            $params = preg_split('/\s*;\s*/', trim($part), -1, PREG_SPLIT_NO_EMPTY);
1305 3
            if (empty($params)) {
1306 1
                continue;
1307
            }
1308
            $values = [
1309 3
                'q' => [$i, array_shift($params), 1],
1310
            ];
1311 3
            foreach ($params as $param) {
1312 2
                if (strpos($param, '=') !== false) {
1313 2
                    [$key, $value] = explode('=', $param, 2);
0 ignored issues
show
Bug introduced by
The variable $key does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $value does not exist. Did you mean $values?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
1314 2
                    if ($key === 'q') {
1315 2
                        $values['q'][2] = (float) $value;
0 ignored issues
show
Bug introduced by
The variable $value does not exist. Did you mean $values?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
1316
                    } else {
1317 2
                        $values[$key] = $value;
0 ignored issues
show
Bug introduced by
The variable $value does not exist. Did you mean $values?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
1318
                    }
1319
                } else {
1320 2
                    $values[] = $param;
1321
                }
1322
            }
1323 3
            $accepts[] = $values;
1324
        }
1325
1326 3
        usort($accepts, function ($a, $b) {
1327 3
            $a = $a['q']; // index, name, q
1328 3
            $b = $b['q'];
1329 3
            if ($a[2] > $b[2]) {
1330 2
                return -1;
1331
            }
1332
1333 2
            if ($a[2] < $b[2]) {
1334 1
                return 1;
1335
            }
1336
1337 2
            if ($a[1] === $b[1]) {
1338
                return $a[0] > $b[0] ? 1 : -1;
1339
            }
1340
1341 2
            if ($a[1] === '*/*') {
1342
                return 1;
1343
            }
1344
1345 2
            if ($b[1] === '*/*') {
1346
                return -1;
1347
            }
1348
1349 2
            $wa = $a[1][strlen($a[1]) - 1] === '*';
1350 2
            $wb = $b[1][strlen($b[1]) - 1] === '*';
1351 2
            if ($wa xor $wb) {
1352
                return $wa ? 1 : -1;
1353
            }
1354
1355 2
            return $a[0] > $b[0] ? 1 : -1;
1356 3
        });
1357
1358 3
        $result = [];
1359 3
        foreach ($accepts as $accept) {
1360 3
            $name = $accept['q'][1];
1361 3
            $accept['q'] = $accept['q'][2];
1362 3
            $result[$name] = $accept;
1363
        }
1364
1365 3
        return $result;
1366
    }
1367
1368
    /**
1369
     * Returns the user-preferred language that should be used by this application.
1370
     * The language resolution is based on the user preferred languages and the languages
1371
     * supported by the application. The method will try to find the best match.
1372
     * @param array $languages a list of the languages supported by the application. If this is empty, the current
1373
     * application language will be returned without further processing.
1374
     * @return string the language that the application should use.
1375
     */
1376 1
    public function getPreferredLanguage(array $languages = [])
1377
    {
1378 1
        if (empty($languages)) {
1379 1
            return Yii::$app->language;
1380
        }
1381 1
        foreach ($this->getAcceptableLanguages() as $acceptableLanguage) {
1382 1
            $acceptableLanguage = str_replace('_', '-', strtolower($acceptableLanguage));
1383 1
            foreach ($languages as $language) {
1384 1
                $normalizedLanguage = str_replace('_', '-', strtolower($language));
1385
1386
                if (
1387 1
                    $normalizedLanguage === $acceptableLanguage // en-us==en-us
1388 1
                    || strpos($acceptableLanguage, $normalizedLanguage . '-') === 0 // en==en-us
1389 1
                    || strpos($normalizedLanguage, $acceptableLanguage . '-') === 0 // en-us==en
1390
                ) {
1391 1
                    return $language;
1392
                }
1393
            }
1394
        }
1395
1396 1
        return reset($languages);
1397
    }
1398
1399
    /**
1400
     * Gets the Etags.
1401
     *
1402
     * @return array The entity tags
1403
     */
1404
    public function getETags()
1405
    {
1406
        if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
1407
            return preg_split('/[\s,]+/', str_replace('-gzip', '', $_SERVER['HTTP_IF_NONE_MATCH']), -1, PREG_SPLIT_NO_EMPTY);
1408
        }
1409
1410
        return [];
1411
    }
1412
1413
    /**
1414
     * Returns the cookie collection.
1415
     * Through the returned cookie collection, you may access a cookie using the following syntax:
1416
     *
1417
     * ```php
1418
     * $cookie = $request->cookies['name']
1419
     * if ($cookie !== null) {
1420
     *     $value = $cookie->value;
1421
     * }
1422
     *
1423
     * // alternatively
1424
     * $value = $request->cookies->getValue('name');
1425
     * ```
1426
     *
1427
     * @return CookieCollection the cookie collection.
1428
     */
1429 32
    public function getCookies()
1430
    {
1431 32
        if ($this->_cookies === null) {
1432 32
            $this->_cookies = new CookieCollection($this->loadCookies(), [
1433 32
                'readOnly' => true,
1434
            ]);
1435
        }
1436
1437 32
        return $this->_cookies;
1438
    }
1439
1440
    /**
1441
     * Converts `$_COOKIE` into an array of [[Cookie]].
1442
     * @return array the cookies obtained from request
1443
     * @throws InvalidConfigException if [[cookieValidationKey]] is not set when [[enableCookieValidation]] is true
1444
     */
1445 32
    protected function loadCookies()
1446
    {
1447 32
        $cookies = [];
1448 32
        if ($this->enableCookieValidation) {
1449 31
            if ($this->cookieValidationKey == '') {
1450
                throw new InvalidConfigException(get_class($this) . '::cookieValidationKey must be configured with a secret key.');
1451
            }
1452 31
            foreach ($_COOKIE as $name => $value) {
1453
                if (!is_string($value)) {
1454
                    continue;
1455
                }
1456
                $data = Yii::$app->getSecurity()->validateData($value, $this->cookieValidationKey);
1457
                if ($data === false) {
1458
                    continue;
1459
                }
1460
                $data = @unserialize($data);
1461
                if (is_array($data) && isset($data[0], $data[1]) && $data[0] === $name) {
1462
                    $cookies[$name] = new Cookie([
1463 31
                        'name' => $name,
1464
                        'value' => $data[1],
1465
                        'expire' => null,
1466
                    ]);
1467
                }
1468
            }
1469
        } else {
1470 1
            foreach ($_COOKIE as $name => $value) {
1471
                $cookies[$name] = new Cookie([
1472
                    'name' => $name,
1473
                    'value' => $value,
1474
                    'expire' => null,
1475
                ]);
1476
            }
1477
        }
1478
1479 32
        return $cookies;
1480
    }
1481
1482
    private $_csrfToken;
1483
1484
    /**
1485
     * Returns the token used to perform CSRF validation.
1486
     *
1487
     * This token is generated in a way to prevent [BREACH attacks](http://breachattack.com/). It may be passed
1488
     * along via a hidden field of an HTML form or an HTTP header value to support CSRF validation.
1489
     * @param bool $regenerate whether to regenerate CSRF token. When this parameter is true, each time
1490
     * this method is called, a new CSRF token will be generated and persisted (in session or cookie).
1491
     * @return string the token used to perform CSRF validation.
1492
     */
1493 35
    public function getCsrfToken($regenerate = false)
1494
    {
1495 35
        if ($this->_csrfToken === null || $regenerate) {
1496 35
            if ($regenerate || ($token = $this->loadCsrfToken()) === null) {
1497 34
                $token = $this->generateCsrfToken();
1498
            }
1499 35
            $this->_csrfToken = Yii::$app->security->maskToken($token);
1500
        }
1501
1502 35
        return $this->_csrfToken;
1503
    }
1504
1505
    /**
1506
     * Loads the CSRF token from cookie or session.
1507
     * @return string the CSRF token loaded from cookie or session. Null is returned if the cookie or session
1508
     * does not have CSRF token.
1509
     */
1510 35
    protected function loadCsrfToken()
1511
    {
1512 35
        if ($this->enableCsrfCookie) {
1513 32
            return $this->getCookies()->getValue($this->csrfParam);
1514
        }
1515 3
        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...
1516
    }
1517
1518
    /**
1519
     * Generates an unmasked random token used to perform CSRF validation.
1520
     * @return string the random token for CSRF validation.
1521
     */
1522 34
    protected function generateCsrfToken()
1523
    {
1524 34
        $token = Yii::$app->getSecurity()->generateRandomKey();
1525 34
        if ($this->enableCsrfCookie) {
1526 32
            $cookie = $this->createCsrfCookie($token);
1527 32
            Yii::$app->getResponse()->getCookies()->add($cookie);
1528
        } else {
1529 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...
1530
        }
1531 34
        return $token;
1532
    }
1533
1534
    /**
1535
     * @return string the CSRF token sent via [[CSRF_HEADER]] by browser. Null is returned if no such header is sent.
1536
     */
1537 3
    public function getCsrfTokenFromHeader()
1538
    {
1539 3
        return $this->getHeaderLine(static::CSRF_HEADER);
1540
    }
1541
1542
    /**
1543
     * Creates a cookie with a randomly generated CSRF token.
1544
     * Initial values specified in [[csrfCookie]] will be applied to the generated cookie.
1545
     * @param string $token the CSRF token
1546
     * @return Cookie the generated cookie
1547
     * @see enableCsrfValidation
1548
     */
1549 32
    protected function createCsrfCookie($token)
1550
    {
1551 32
        $options = $this->csrfCookie;
1552 32
        $options['name'] = $this->csrfParam;
1553 32
        $options['value'] = $token;
1554 32
        return new Cookie($options);
1555
    }
1556
1557
    /**
1558
     * Performs the CSRF validation.
1559
     *
1560
     * This method will validate the user-provided CSRF token by comparing it with the one stored in cookie or session.
1561
     * This method is mainly called in [[Controller::beforeAction()]].
1562
     *
1563
     * Note that the method will NOT perform CSRF validation if [[enableCsrfValidation]] is false or the HTTP method
1564
     * is among GET, HEAD or OPTIONS.
1565
     *
1566
     * @param string $clientSuppliedToken the user-provided CSRF token to be validated. If null, the token will be retrieved from
1567
     * the [[csrfParam]] POST field or HTTP header.
1568
     * This parameter is available since version 2.0.4.
1569
     * @return bool whether CSRF token is valid. If [[enableCsrfValidation]] is false, this method will return true.
1570
     */
1571 5
    public function validateCsrfToken($clientSuppliedToken = null)
1572
    {
1573 5
        $method = $this->getMethod();
1574
        // only validate CSRF token on non-"safe" methods https://tools.ietf.org/html/rfc2616#section-9.1.1
1575 5
        if (!$this->enableCsrfValidation || in_array($method, ['GET', 'HEAD', 'OPTIONS'], true)) {
1576 5
            return true;
1577
        }
1578
1579
1580 3
        $trueToken = $this->getCsrfToken();
1581
1582 3
        if ($clientSuppliedToken !== null) {
1583 1
            return $this->validateCsrfTokenInternal($clientSuppliedToken, $trueToken);
1584
        }
1585
1586 3
        return $this->validateCsrfTokenInternal($this->getBodyParam($this->csrfParam), $trueToken)
1587 3
            || $this->validateCsrfTokenInternal($this->getCsrfTokenFromHeader(), $trueToken);
1588
    }
1589
1590
    /**
1591
     * Validates CSRF token
1592
     *
1593
     * @param string $clientSuppliedToken The masked client-supplied token.
1594
     * @param string $trueToken The masked true token.
1595
     * @return bool
1596
     */
1597 3
    private function validateCsrfTokenInternal($clientSuppliedToken, $trueToken)
1598
    {
1599 3
        if (!is_string($clientSuppliedToken)) {
1600 3
            return false;
1601
        }
1602
1603 3
        $security = Yii::$app->security;
1604
1605 3
        return $security->unmaskToken($clientSuppliedToken) === $security->unmaskToken($trueToken);
1606
    }
1607
1608
    /**
1609
     * {@inheritdoc}
1610
     */
1611 1
    public function __clone()
1612
    {
1613 1
        parent::__clone();
1614
1615 1
        $this->cloneHttpMessageInternals();
1616
1617 1
        if (is_object($this->_cookies)) {
1618
            $this->_cookies = clone $this->_cookies;
1619
        }
1620 1
    }
1621
}
1622