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 ( a81d72...271609 )
by Robert
14:15
created

Request::getHostName()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
ccs 5
cts 5
cp 1
cc 2
eloc 4
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
use yii\helpers\StringHelper;
13
14
/**
15
 * The web Request class represents an HTTP request
16
 *
17
 * It encapsulates the $_SERVER variable and resolves its inconsistency among different Web servers.
18
 * Also it provides an interface to retrieve request parameters from $_POST, $_GET, $_COOKIES and REST
19
 * parameters sent via other HTTP methods like PUT or DELETE.
20
 *
21
 * Request is configured as an application component in [[\yii\web\Application]] by default.
22
 * You can access that instance via `Yii::$app->request`.
23
 *
24
 * @property string $absoluteUrl The currently requested absolute URL. This property is read-only.
25
 * @property array $acceptableContentTypes The content types ordered by the quality score. Types with the
26
 * highest scores will be returned first. The array keys are the content types, while the array values are the
27
 * corresponding quality score and other parameters as given in the header.
28
 * @property array $acceptableLanguages The languages ordered by the preference level. The first element
29
 * represents the most preferred language.
30
 * @property string|null $authPassword The password sent via HTTP authentication, null if the password is not
31
 * given. This property is read-only.
32
 * @property string|null $authUser The username sent via HTTP authentication, null if the username is not
33
 * given. This property is read-only.
34
 * @property string $baseUrl The relative URL for the application.
35
 * @property array $bodyParams The request parameters given in the request body.
36
 * @property string $contentType Request content-type. Null is returned if this information is not available.
37
 * This property is read-only.
38
 * @property CookieCollection $cookies The cookie collection. This property is read-only.
39
 * @property string $csrfToken The token used to perform CSRF validation. This property is read-only.
40
 * @property string $csrfTokenFromHeader The CSRF token sent via [[CSRF_HEADER]] by browser. Null is returned
41
 * if no such header is sent. This property is read-only.
42
 * @property array $eTags The entity tags. This property is read-only.
43
 * @property HeaderCollection $headers The header collection. This property is read-only.
44
 * @property string|null $hostInfo Schema and hostname part (with port number if needed) of the request URL
45
 * (e.g. `http://www.yiiframework.com`), null if can't be obtained from `$_SERVER` and wasn't set.
46
 * @property string|null $hostName Hostname part of the request URL (e.g. `www.yiiframework.com`). This
47
 * property is read-only.
48
 * @property boolean $isAjax Whether this is an AJAX (XMLHttpRequest) request. This property is read-only.
49
 * @property boolean $isDelete Whether this is a DELETE request. This property is read-only.
50
 * @property boolean $isFlash Whether this is an Adobe Flash or Adobe Flex request. This property is
51
 * read-only.
52
 * @property boolean $isGet Whether this is a GET request. This property is read-only.
53
 * @property boolean $isHead Whether this is a HEAD request. This property is read-only.
54
 * @property boolean $isOptions Whether this is a OPTIONS request. This property is read-only.
55
 * @property boolean $isPatch Whether this is a PATCH request. This property is read-only.
56
 * @property boolean $isPjax Whether this is a PJAX request. This property is read-only.
57
 * @property boolean $isPost Whether this is a POST request. This property is read-only.
58
 * @property boolean $isPut Whether this is a PUT request. This property is read-only.
59
 * @property boolean $isSecureConnection If the request is sent via secure channel (https). This property is
60
 * read-only.
61
 * @property string $method Request method, such as GET, POST, HEAD, PUT, PATCH, DELETE. The value returned is
62
 * turned into upper case. This property is read-only.
63
 * @property string $pathInfo Part of the request URL that is after the entry script and before the question
64
 * mark. Note, the returned path info is already URL-decoded.
65
 * @property integer $port Port number for insecure requests.
66
 * @property array $queryParams The request GET parameter values.
67
 * @property string $queryString Part of the request URL that is after the question mark. This property is
68
 * read-only.
69
 * @property string $rawBody The request body.
70
 * @property string|null $referrer URL referrer, null if not available. This property is read-only.
71
 * @property string $scriptFile The entry script file path.
72
 * @property string $scriptUrl The relative URL of the entry script.
73
 * @property integer $securePort Port number for secure requests.
74
 * @property string $serverName Server name, null if not available. This property is read-only.
75
 * @property integer|null $serverPort Server port number, null if not available. This property is read-only.
76
 * @property string $url The currently requested relative URL. Note that the URI returned is URL-encoded.
77
 * @property string|null $userAgent User agent, null if not available. This property is read-only.
78
 * @property string|null $userHost User host name, null if not available. This property is read-only.
79
 * @property string|null $userIP User IP address, null if not available. This property is read-only.
80
 *
81
 * @author Qiang Xue <[email protected]>
82
 * @since 2.0
83
 */
84
class Request extends \yii\base\Request
85
{
86
    /**
87
     * The name of the HTTP header for sending CSRF token.
88
     */
89
    const CSRF_HEADER = 'X-CSRF-Token';
90
    /**
91
     * The length of the CSRF token mask.
92
     */
93
    const CSRF_MASK_LENGTH = 8;
94
95
    /**
96
     * @var boolean whether to enable CSRF (Cross-Site Request Forgery) validation. Defaults to true.
97
     * When CSRF validation is enabled, forms submitted to an Yii Web application must be originated
98
     * from the same application. If not, a 400 HTTP exception will be raised.
99
     *
100
     * Note, this feature requires that the user client accepts cookie. Also, to use this feature,
101
     * forms submitted via POST method must contain a hidden input whose name is specified by [[csrfParam]].
102
     * You may use [[\yii\helpers\Html::beginForm()]] to generate his hidden input.
103
     *
104
     * In JavaScript, you may get the values of [[csrfParam]] and [[csrfToken]] via `yii.getCsrfParam()` and
105
     * `yii.getCsrfToken()`, respectively. The [[\yii\web\YiiAsset]] asset must be registered.
106
     * You also need to include CSRF meta tags in your pages by using [[\yii\helpers\Html::csrfMetaTags()]].
107
     *
108
     * @see Controller::enableCsrfValidation
109
     * @see http://en.wikipedia.org/wiki/Cross-site_request_forgery
110
     */
111
    public $enableCsrfValidation = true;
112
    /**
113
     * @var string the name of the token used to prevent CSRF. Defaults to '_csrf'.
114
     * This property is used only when [[enableCsrfValidation]] is true.
115
     */
116
    public $csrfParam = '_csrf';
117
    /**
118
     * @var array the configuration for creating the CSRF [[Cookie|cookie]]. This property is used only when
119
     * both [[enableCsrfValidation]] and [[enableCsrfCookie]] are true.
120
     */
121
    public $csrfCookie = ['httpOnly' => true];
122
    /**
123
     * @var boolean whether to use cookie to persist CSRF token. If false, CSRF token will be stored
124
     * in session under the name of [[csrfParam]]. Note that while storing CSRF tokens in session increases
125
     * security, it requires starting a session for every page, which will degrade your site performance.
126
     */
127
    public $enableCsrfCookie = true;
128
    /**
129
     * @var boolean whether cookies should be validated to ensure they are not tampered. Defaults to true.
130
     */
131
    public $enableCookieValidation = true;
132
    /**
133
     * @var string a secret key used for cookie validation. This property must be set if [[enableCookieValidation]] is true.
134
     */
135
    public $cookieValidationKey;
136
    /**
137
     * @var string the name of the POST parameter that is used to indicate if a request is a PUT, PATCH or DELETE
138
     * request tunneled through POST. Defaults to '_method'.
139
     * @see getMethod()
140
     * @see getBodyParams()
141
     */
142
    public $methodParam = '_method';
143
    /**
144
     * @var array the parsers for converting the raw HTTP request body into [[bodyParams]].
145
     * The array keys are the request `Content-Types`, and the array values are the
146
     * corresponding configurations for [[Yii::createObject|creating the parser objects]].
147
     * A parser must implement the [[RequestParserInterface]].
148
     *
149
     * To enable parsing for JSON requests you can use the [[JsonParser]] class like in the following example:
150
     *
151
     * ```
152
     * [
153
     *     'application/json' => 'yii\web\JsonParser',
154
     * ]
155
     * ```
156
     *
157
     * To register a parser for parsing all request types you can use `'*'` as the array key.
158
     * This one will be used as a fallback in case no other types match.
159
     *
160
     * @see getBodyParams()
161
     */
162
    public $parsers = [];
163
164
    /**
165
     * @var CookieCollection Collection of request cookies.
166
     */
167
    private $_cookies;
168
    /**
169
     * @var HeaderCollection Collection of request headers.
170
     */
171
    private $_headers;
172
173
174
    /**
175
     * Resolves the current request into a route and the associated parameters.
176
     * @return array the first element is the route, and the second is the associated parameters.
177
     * @throws NotFoundHttpException if the request cannot be resolved.
178
     */
179 1
    public function resolve()
180
    {
181 1
        $result = Yii::$app->getUrlManager()->parseRequest($this);
182 1
        if ($result !== false) {
183 1
            list ($route, $params) = $result;
184 1
            if ($this->_queryParams === null) {
185 1
                $_GET = $params + $_GET; // preserve numeric keys
186 1
            } else {
187 1
                $this->_queryParams = $params + $this->_queryParams;
188
            }
189 1
            return [$route, $this->getQueryParams()];
190
        } else {
191
            throw new NotFoundHttpException(Yii::t('yii', 'Page not found.'));
192
        }
193
    }
194
195
    /**
196
     * Returns the header collection.
197
     * The header collection contains incoming HTTP headers.
198
     * @return HeaderCollection the header collection
199
     */
200 6
    public function getHeaders()
201
    {
202 6
        if ($this->_headers === null) {
203 6
            $this->_headers = new HeaderCollection;
204 6
            if (function_exists('getallheaders')) {
205
                $headers = getallheaders();
206 6
            } elseif (function_exists('http_get_request_headers')) {
207
                $headers = http_get_request_headers();
208
            } else {
209 6
                foreach ($_SERVER as $name => $value) {
210 6
                    if (strncmp($name, 'HTTP_', 5) === 0) {
211 1
                        $name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
212 1
                        $this->_headers->add($name, $value);
213 1
                    }
214 6
                }
215
216 6
                return $this->_headers;
217
            }
218
            foreach ($headers as $name => $value) {
219
                $this->_headers->add($name, $value);
220
            }
221
        }
222
223 6
        return $this->_headers;
224
    }
225
226
    /**
227
     * Returns the method of the current request (e.g. GET, POST, HEAD, PUT, PATCH, DELETE).
228
     * @return string request method, such as GET, POST, HEAD, PUT, PATCH, DELETE.
229
     * The value returned is turned into upper case.
230
     */
231 18
    public function getMethod()
232
    {
233 18
        if (isset($_POST[$this->methodParam])) {
234 4
            return strtoupper($_POST[$this->methodParam]);
235
        }
236
237 15
        if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
238
            return strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']);
239
        }
240
241 15
        if (isset($_SERVER['REQUEST_METHOD'])) {
242 2
            return strtoupper($_SERVER['REQUEST_METHOD']);
243
        }
244
245 14
        return 'GET';
246
    }
247
248
    /**
249
     * Returns whether this is a GET request.
250
     * @return boolean whether this is a GET request.
251
     */
252 2
    public function getIsGet()
253
    {
254 2
        return $this->getMethod() === 'GET';
255
    }
256
257
    /**
258
     * Returns whether this is an OPTIONS request.
259
     * @return boolean whether this is a OPTIONS request.
260
     */
261
    public function getIsOptions()
262
    {
263
        return $this->getMethod() === 'OPTIONS';
264
    }
265
266
    /**
267
     * Returns whether this is a HEAD request.
268
     * @return boolean whether this is a HEAD request.
269
     */
270 9
    public function getIsHead()
271
    {
272 9
        return $this->getMethod() === 'HEAD';
273
    }
274
275
    /**
276
     * Returns whether this is a POST request.
277
     * @return boolean whether this is a POST request.
278
     */
279
    public function getIsPost()
280
    {
281
        return $this->getMethod() === 'POST';
282
    }
283
284
    /**
285
     * Returns whether this is a DELETE request.
286
     * @return boolean whether this is a DELETE request.
287
     */
288
    public function getIsDelete()
289
    {
290
        return $this->getMethod() === 'DELETE';
291
    }
292
293
    /**
294
     * Returns whether this is a PUT request.
295
     * @return boolean whether this is a PUT request.
296
     */
297
    public function getIsPut()
298
    {
299
        return $this->getMethod() === 'PUT';
300
    }
301
302
    /**
303
     * Returns whether this is a PATCH request.
304
     * @return boolean whether this is a PATCH request.
305
     */
306
    public function getIsPatch()
307
    {
308
        return $this->getMethod() === 'PATCH';
309
    }
310
311
    /**
312
     * Returns whether this is an AJAX (XMLHttpRequest) request.
313
     *
314
     * Note that jQuery doesn't set the header in case of cross domain
315
     * requests: https://stackoverflow.com/questions/8163703/cross-domain-ajax-doesnt-send-x-requested-with-header
316
     *
317
     * @return boolean whether this is an AJAX (XMLHttpRequest) request.
318
     */
319 2
    public function getIsAjax()
320
    {
321 2
        return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
322
    }
323
324
    /**
325
     * Returns whether this is a PJAX request
326
     * @return boolean whether this is a PJAX request
327
     */
328 1
    public function getIsPjax()
329
    {
330 1
        return $this->getIsAjax() && !empty($_SERVER['HTTP_X_PJAX']);
331
    }
332
333
    /**
334
     * Returns whether this is an Adobe Flash or Flex request.
335
     * @return boolean whether this is an Adobe Flash or Adobe Flex request.
336
     */
337
    public function getIsFlash()
338
    {
339
        return isset($_SERVER['HTTP_USER_AGENT']) &&
340
            (stripos($_SERVER['HTTP_USER_AGENT'], 'Shockwave') !== false || stripos($_SERVER['HTTP_USER_AGENT'], 'Flash') !== false);
341
    }
342
343
    private $_rawBody;
344
345
    /**
346
     * Returns the raw HTTP request body.
347
     * @return string the request body
348
     */
349
    public function getRawBody()
350
    {
351
        if ($this->_rawBody === null) {
352
            $this->_rawBody = file_get_contents('php://input');
353
        }
354
355
        return $this->_rawBody;
356
    }
357
358
    /**
359
     * Sets the raw HTTP request body, this method is mainly used by test scripts to simulate raw HTTP requests.
360
     * @param string $rawBody the request body
361
     */
362
    public function setRawBody($rawBody)
363
    {
364
        $this->_rawBody = $rawBody;
365
    }
366
367
    private $_bodyParams;
368
369
    /**
370
     * Returns the request parameters given in the request body.
371
     *
372
     * Request parameters are determined using the parsers configured in [[parsers]] property.
373
     * If no parsers are configured for the current [[contentType]] it uses the PHP function `mb_parse_str()`
374
     * to parse the [[rawBody|request body]].
375
     * @return array the request parameters given in the request body.
376
     * @throws \yii\base\InvalidConfigException if a registered parser does not implement the [[RequestParserInterface]].
377
     * @see getMethod()
378
     * @see getBodyParam()
379
     * @see setBodyParams()
380
     */
381 3
    public function getBodyParams()
382
    {
383 3
        if ($this->_bodyParams === null) {
384 1
            if (isset($_POST[$this->methodParam])) {
385 1
                $this->_bodyParams = $_POST;
386 1
                unset($this->_bodyParams[$this->methodParam]);
387 1
                return $this->_bodyParams;
388
            }
389
390
            $rawContentType = $this->getContentType();
391
            if (($pos = strpos($rawContentType, ';')) !== false) {
392
                // e.g. application/json; charset=UTF-8
393
                $contentType = substr($rawContentType, 0, $pos);
394
            } else {
395
                $contentType = $rawContentType;
396
            }
397
398
            if (isset($this->parsers[$contentType])) {
399
                $parser = Yii::createObject($this->parsers[$contentType]);
400
                if (!($parser instanceof RequestParserInterface)) {
401
                    throw new InvalidConfigException("The '$contentType' request parser is invalid. It must implement the yii\\web\\RequestParserInterface.");
402
                }
403
                $this->_bodyParams = $parser->parse($this->getRawBody(), $rawContentType);
404
            } elseif (isset($this->parsers['*'])) {
405
                $parser = Yii::createObject($this->parsers['*']);
406
                if (!($parser instanceof RequestParserInterface)) {
407
                    throw new InvalidConfigException("The fallback request parser is invalid. It must implement the yii\\web\\RequestParserInterface.");
408
                }
409
                $this->_bodyParams = $parser->parse($this->getRawBody(), $rawContentType);
410
            } elseif ($this->getMethod() === 'POST') {
411
                // PHP has already parsed the body so we have all params in $_POST
412
                $this->_bodyParams = $_POST;
413
            } else {
414
                $this->_bodyParams = [];
415
                mb_parse_str($this->getRawBody(), $this->_bodyParams);
416
            }
417
        }
418
419 3
        return $this->_bodyParams;
420
    }
421
422
    /**
423
     * Sets the request body parameters.
424
     * @param array $values the request body parameters (name-value pairs)
425
     * @see getBodyParam()
426
     * @see getBodyParams()
427
     */
428 2
    public function setBodyParams($values)
429
    {
430 2
        $this->_bodyParams = $values;
431 2
    }
432
433
    /**
434
     * Returns the named request body parameter value.
435
     * If the parameter does not exist, the second parameter passed to this method will be returned.
436
     * @param string $name the parameter name
437
     * @param mixed $defaultValue the default parameter value if the parameter does not exist.
438
     * @return mixed the parameter value
439
     * @see getBodyParams()
440
     * @see setBodyParams()
441
     */
442 3
    public function getBodyParam($name, $defaultValue = null)
443
    {
444 3
        $params = $this->getBodyParams();
445
446 3
        return isset($params[$name]) ? $params[$name] : $defaultValue;
447
    }
448
449
    /**
450
     * Returns POST parameter with a given name. If name isn't specified, returns an array of all POST parameters.
451
     *
452
     * @param string $name the parameter name
453
     * @param mixed $defaultValue the default parameter value if the parameter does not exist.
454
     * @return array|mixed
455
     */
456
    public function post($name = null, $defaultValue = null)
457
    {
458
        if ($name === null) {
459
            return $this->getBodyParams();
460
        } else {
461
            return $this->getBodyParam($name, $defaultValue);
462
        }
463
    }
464
465
    private $_queryParams;
466
467
    /**
468
     * Returns the request parameters given in the [[queryString]].
469
     *
470
     * This method will return the contents of `$_GET` if params where not explicitly set.
471
     * @return array the request GET parameter values.
472
     * @see setQueryParams()
473
     */
474 23
    public function getQueryParams()
475
    {
476 23
        if ($this->_queryParams === null) {
477 21
            return $_GET;
478
        }
479
480 4
        return $this->_queryParams;
481
    }
482
483
    /**
484
     * Sets the request [[queryString]] parameters.
485
     * @param array $values the request query parameters (name-value pairs)
486
     * @see getQueryParam()
487
     * @see getQueryParams()
488
     */
489 4
    public function setQueryParams($values)
490
    {
491 4
        $this->_queryParams = $values;
492 4
    }
493
494
    /**
495
     * Returns GET parameter with a given name. If name isn't specified, returns an array of all GET parameters.
496
     *
497
     * @param string $name the parameter name
498
     * @param mixed $defaultValue the default parameter value if the parameter does not exist.
499
     * @return array|mixed
500
     */
501 14
    public function get($name = null, $defaultValue = null)
502
    {
503 14
        if ($name === null) {
504
            return $this->getQueryParams();
505
        } else {
506 14
            return $this->getQueryParam($name, $defaultValue);
507
        }
508
    }
509
510
    /**
511
     * Returns the named GET parameter value.
512
     * If the GET parameter does not exist, the second parameter passed to this method will be returned.
513
     * @param string $name the GET parameter name.
514
     * @param mixed $defaultValue the default parameter value if the GET parameter does not exist.
515
     * @return mixed the GET parameter value
516
     * @see getBodyParam()
517
     */
518 15
    public function getQueryParam($name, $defaultValue = null)
519
    {
520 15
        $params = $this->getQueryParams();
521
522 15
        return isset($params[$name]) ? $params[$name] : $defaultValue;
523
    }
524
525
    private $_hostInfo;
526
    private $_hostName;
527
528
    /**
529
     * Returns the schema and host part of the current request URL.
530
     * The returned URL does not have an ending slash.
531
     * By default this is determined based on the user request information.
532
     * You may explicitly specify it by setting the [[setHostInfo()|hostInfo]] property.
533
     * @return string|null schema and hostname part (with port number if needed) of the request URL
534
     * (e.g. `http://www.yiiframework.com`), null if can't be obtained from `$_SERVER` and wasn't set.
535
     * @see setHostInfo()
536
     */
537 12
    public function getHostInfo()
538
    {
539 12
        if ($this->_hostInfo === null) {
540 8
            $secure = $this->getIsSecureConnection();
541 8
            $http = $secure ? 'https' : 'http';
542 8
            if (isset($_SERVER['HTTP_HOST'])) {
543
                $this->_hostInfo = $http . '://' . $_SERVER['HTTP_HOST'];
544 8
            } elseif (isset($_SERVER['SERVER_NAME'])) {
545
                $this->_hostInfo = $http . '://' . $_SERVER['SERVER_NAME'];
546
                $port = $secure ? $this->getSecurePort() : $this->getPort();
547
                if (($port !== 80 && !$secure) || ($port !== 443 && $secure)) {
548
                    $this->_hostInfo .= ':' . $port;
549
                }
550
            }
551 8
        }
552
553 12
        return $this->_hostInfo;
554
    }
555
556
    /**
557
     * Sets the schema and host part of the application URL.
558
     * This setter is provided in case the schema and hostname cannot be determined
559
     * on certain Web servers.
560
     * @param string|null $value the schema and host part of the application URL. The trailing slashes will be removed.
561
     */
562 22
    public function setHostInfo($value)
563
    {
564 22
        $this->_hostName = null;
565 22
        $this->_hostInfo = $value === null ? null : rtrim($value, '/');
566 22
    }
567
568
    /**
569
     * Returns the host part of the current request URL.
570
     * Value is calculated from current [[getHostInfo()|hostInfo]] property.
571
     * @return string|null hostname part of the request URL (e.g. `www.yiiframework.com`)
572
     * @see getHostInfo()
573
     * @since 2.0.10
574
     */
575 1
    public function getHostName()
576
    {
577 1
        if ($this->_hostName === null) {
578 1
            $this->_hostName = parse_url($this->getHostInfo(), PHP_URL_HOST);
579 1
        }
580
581 1
        return $this->_hostName;
582
    }
583
584
    private $_baseUrl;
585
586
    /**
587
     * Returns the relative URL for the application.
588
     * This is similar to [[scriptUrl]] except that it does not include the script file name,
589
     * and the ending slashes are removed.
590
     * @return string the relative URL for the application
591
     * @see setScriptUrl()
592
     */
593 121
    public function getBaseUrl()
594
    {
595 121
        if ($this->_baseUrl === null) {
596 120
            $this->_baseUrl = rtrim(dirname($this->getScriptUrl()), '\\/');
597 120
        }
598
599 121
        return $this->_baseUrl;
600
    }
601
602
    /**
603
     * Sets the relative URL for the application.
604
     * By default the URL is determined based on the entry script URL.
605
     * This setter is provided in case you want to change this behavior.
606
     * @param string $value the relative URL for the application
607
     */
608 1
    public function setBaseUrl($value)
609
    {
610 1
        $this->_baseUrl = $value;
611 1
    }
612
613
    private $_scriptUrl;
614
615
    /**
616
     * Returns the relative URL of the entry script.
617
     * The implementation of this method referenced Zend_Controller_Request_Http in Zend Framework.
618
     * @return string the relative URL of the entry script.
619
     * @throws InvalidConfigException if unable to determine the entry script URL
620
     */
621 122
    public function getScriptUrl()
622
    {
623 122
        if ($this->_scriptUrl === null) {
624 1
            $scriptFile = $this->getScriptFile();
625
            $scriptName = basename($scriptFile);
626
            if (isset($_SERVER['SCRIPT_NAME']) && basename($_SERVER['SCRIPT_NAME']) === $scriptName) {
627
                $this->_scriptUrl = $_SERVER['SCRIPT_NAME'];
628
            } elseif (isset($_SERVER['PHP_SELF']) && basename($_SERVER['PHP_SELF']) === $scriptName) {
629
                $this->_scriptUrl = $_SERVER['PHP_SELF'];
630
            } elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $scriptName) {
631
                $this->_scriptUrl = $_SERVER['ORIG_SCRIPT_NAME'];
632
            } elseif (isset($_SERVER['PHP_SELF']) && ($pos = strpos($_SERVER['PHP_SELF'], '/' . $scriptName)) !== false) {
633
                $this->_scriptUrl = substr($_SERVER['SCRIPT_NAME'], 0, $pos) . '/' . $scriptName;
634
            } elseif (!empty($_SERVER['DOCUMENT_ROOT']) && strpos($scriptFile, $_SERVER['DOCUMENT_ROOT']) === 0) {
635
                $this->_scriptUrl = str_replace('\\', '/', str_replace($_SERVER['DOCUMENT_ROOT'], '', $scriptFile));
636
            } else {
637
                throw new InvalidConfigException('Unable to determine the entry script URL.');
638
            }
639
        }
640
641 121
        return $this->_scriptUrl;
642
    }
643
644
    /**
645
     * Sets the relative URL for the application entry script.
646
     * This setter is provided in case the entry script URL cannot be determined
647
     * on certain Web servers.
648
     * @param string $value the relative URL for the application entry script.
649
     */
650 126
    public function setScriptUrl($value)
651
    {
652 126
        $this->_scriptUrl = $value === null ? null : '/' . trim($value, '/');
653 126
    }
654
655
    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...
656
657
    /**
658
     * Returns the entry script file path.
659
     * The default implementation will simply return `$_SERVER['SCRIPT_FILENAME']`.
660
     * @return string the entry script file path
661
     * @throws InvalidConfigException
662
     */
663 123
    public function getScriptFile()
664
    {
665 123
        if (isset($this->_scriptFile)) {
666 102
            return $this->_scriptFile;
667 21
        } elseif (isset($_SERVER['SCRIPT_FILENAME'])) {
668 19
            return $_SERVER['SCRIPT_FILENAME'];
669
        } else {
670 2
            throw new InvalidConfigException('Unable to determine the entry script file path.');
671
        }
672
    }
673
674
    /**
675
     * Sets the entry script file path.
676
     * The entry script file path normally can be obtained from `$_SERVER['SCRIPT_FILENAME']`.
677
     * If your server configuration does not return the correct value, you may configure
678
     * this property to make it right.
679
     * @param string $value the entry script file path.
680
     */
681 102
    public function setScriptFile($value)
682
    {
683 102
        $this->_scriptFile = $value;
684 102
    }
685
686
    private $_pathInfo;
687
688
    /**
689
     * Returns the path info of the currently requested URL.
690
     * A path info refers to the part that is after the entry script and before the question mark (query string).
691
     * The starting and ending slashes are both removed.
692
     * @return string part of the request URL that is after the entry script and before the question mark.
693
     * Note, the returned path info is already URL-decoded.
694
     * @throws InvalidConfigException if the path info cannot be determined due to unexpected server configuration
695
     */
696 9
    public function getPathInfo()
697
    {
698 9
        if ($this->_pathInfo === null) {
699
            $this->_pathInfo = $this->resolvePathInfo();
700
        }
701
702 9
        return $this->_pathInfo;
703
    }
704
705
    /**
706
     * Sets the path info of the current request.
707
     * This method is mainly provided for testing purpose.
708
     * @param string $value the path info of the current request
709
     */
710 9
    public function setPathInfo($value)
711
    {
712 9
        $this->_pathInfo = $value === null ? null : ltrim($value, '/');
713 9
    }
714
715
    /**
716
     * Resolves the path info part of the currently requested URL.
717
     * A path info refers to the part that is after the entry script and before the question mark (query string).
718
     * The starting slashes are both removed (ending slashes will be kept).
719
     * @return string part of the request URL that is after the entry script and before the question mark.
720
     * Note, the returned path info is decoded.
721
     * @throws InvalidConfigException if the path info cannot be determined due to unexpected server configuration
722
     */
723
    protected function resolvePathInfo()
724
    {
725
        $pathInfo = $this->getUrl();
726
727
        if (($pos = strpos($pathInfo, '?')) !== false) {
728
            $pathInfo = substr($pathInfo, 0, $pos);
729
        }
730
731
        $pathInfo = urldecode($pathInfo);
732
733
        // try to encode in UTF8 if not so
734
        // http://w3.org/International/questions/qa-forms-utf-8.html
735
        if (!preg_match('%^(?:
736
            [\x09\x0A\x0D\x20-\x7E]              # ASCII
737
            | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
738
            | \xE0[\xA0-\xBF][\x80-\xBF]         # excluding overlongs
739
            | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
740
            | \xED[\x80-\x9F][\x80-\xBF]         # excluding surrogates
741
            | \xF0[\x90-\xBF][\x80-\xBF]{2}      # planes 1-3
742
            | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
743
            | \xF4[\x80-\x8F][\x80-\xBF]{2}      # plane 16
744
            )*$%xs', $pathInfo)
745
        ) {
746
            $pathInfo = utf8_encode($pathInfo);
747
        }
748
749
        $scriptUrl = $this->getScriptUrl();
750
        $baseUrl = $this->getBaseUrl();
751
        if (strpos($pathInfo, $scriptUrl) === 0) {
752
            $pathInfo = substr($pathInfo, strlen($scriptUrl));
753
        } elseif ($baseUrl === '' || strpos($pathInfo, $baseUrl) === 0) {
754
            $pathInfo = substr($pathInfo, strlen($baseUrl));
755
        } elseif (isset($_SERVER['PHP_SELF']) && strpos($_SERVER['PHP_SELF'], $scriptUrl) === 0) {
756
            $pathInfo = substr($_SERVER['PHP_SELF'], strlen($scriptUrl));
757
        } else {
758
            throw new InvalidConfigException('Unable to determine the path info of the current request.');
759
        }
760
761
        if (substr($pathInfo, 0, 1) === '/') {
762
            $pathInfo = substr($pathInfo, 1);
763
        }
764
765
        return (string) $pathInfo;
766
    }
767
768
    /**
769
     * Returns the currently requested absolute URL.
770
     * This is a shortcut to the concatenation of [[hostInfo]] and [[url]].
771
     * @return string the currently requested absolute URL.
772
     */
773
    public function getAbsoluteUrl()
774
    {
775
        return $this->getHostInfo() . $this->getUrl();
776
    }
777
778
    private $_url;
779
780
    /**
781
     * Returns the currently requested relative URL.
782
     * This refers to the portion of the URL that is after the [[hostInfo]] part.
783
     * It includes the [[queryString]] part if any.
784
     * @return string the currently requested relative URL. Note that the URI returned is URL-encoded.
785
     * @throws InvalidConfigException if the URL cannot be determined due to unusual server configuration
786
     */
787 7
    public function getUrl()
788
    {
789 7
        if ($this->_url === null) {
790
            $this->_url = $this->resolveRequestUri();
791
        }
792
793 7
        return $this->_url;
794
    }
795
796
    /**
797
     * Sets the currently requested relative URL.
798
     * The URI must refer to the portion that is after [[hostInfo]].
799
     * Note that the URI should be URL-encoded.
800
     * @param string $value the request URI to be set
801
     */
802 16
    public function setUrl($value)
803
    {
804 16
        $this->_url = $value;
805 16
    }
806
807
    /**
808
     * Resolves the request URI portion for the currently requested URL.
809
     * This refers to the portion that is after the [[hostInfo]] part. It includes the [[queryString]] part if any.
810
     * The implementation of this method referenced Zend_Controller_Request_Http in Zend Framework.
811
     * @return string|boolean the request URI portion for the currently requested URL.
812
     * Note that the URI returned is URL-encoded.
813
     * @throws InvalidConfigException if the request URI cannot be determined due to unusual server configuration
814
     */
815
    protected function resolveRequestUri()
816
    {
817
        if (isset($_SERVER['HTTP_X_REWRITE_URL'])) { // IIS
818
            $requestUri = $_SERVER['HTTP_X_REWRITE_URL'];
819
        } elseif (isset($_SERVER['REQUEST_URI'])) {
820
            $requestUri = $_SERVER['REQUEST_URI'];
821
            if ($requestUri !== '' && $requestUri[0] !== '/') {
822
                $requestUri = preg_replace('/^(http|https):\/\/[^\/]+/i', '', $requestUri);
823
            }
824
        } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { // IIS 5.0 CGI
825
            $requestUri = $_SERVER['ORIG_PATH_INFO'];
826
            if (!empty($_SERVER['QUERY_STRING'])) {
827
                $requestUri .= '?' . $_SERVER['QUERY_STRING'];
828
            }
829
        } else {
830
            throw new InvalidConfigException('Unable to determine the request URI.');
831
        }
832
833
        return $requestUri;
834
    }
835
836
    /**
837
     * Returns part of the request URL that is after the question mark.
838
     * @return string part of the request URL that is after the question mark
839
     */
840
    public function getQueryString()
841
    {
842
        return isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '';
843
    }
844
845
    /**
846
     * Return if the request is sent via secure channel (https).
847
     * @return boolean if the request is sent via secure channel (https)
848
     */
849 8
    public function getIsSecureConnection()
850
    {
851 8
        return isset($_SERVER['HTTPS']) && (strcasecmp($_SERVER['HTTPS'], 'on') === 0 || $_SERVER['HTTPS'] == 1)
852 8
            || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0;
853
    }
854
855
    /**
856
     * Returns the server name.
857
     * @return string server name, null if not available
858
     */
859 1
    public function getServerName()
860
    {
861 1
        return isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : null;
862
    }
863
864
    /**
865
     * Returns the server port number.
866
     * @return integer|null server port number, null if not available
867
     */
868 1
    public function getServerPort()
869
    {
870 1
        return isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : null;
871
    }
872
873
    /**
874
     * Returns the URL referrer.
875
     * @return string|null URL referrer, null if not available
876
     */
877
    public function getReferrer()
878
    {
879
        return isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : null;
880
    }
881
882
    /**
883
     * Returns the user agent.
884
     * @return string|null user agent, null if not available
885
     */
886
    public function getUserAgent()
887
    {
888
        return isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null;
889
    }
890
891
    /**
892
     * Returns the user IP address.
893
     * @return string|null user IP address, null if not available
894
     */
895 14
    public function getUserIP()
896
    {
897 14
        return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null;
898
    }
899
900
    /**
901
     * Returns the user host name.
902
     * @return string|null user host name, null if not available
903
     */
904
    public function getUserHost()
905
    {
906
        return isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : null;
907
    }
908
909
    /**
910
     * @return string|null the username sent via HTTP authentication, null if the username is not given
911
     */
912 10
    public function getAuthUser()
913
    {
914 10
        return isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null;
915
    }
916
917
    /**
918
     * @return string|null the password sent via HTTP authentication, null if the password is not given
919
     */
920 10
    public function getAuthPassword()
921
    {
922 10
        return isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : null;
923
    }
924
925
    private $_port;
926
927
    /**
928
     * Returns the port to use for insecure requests.
929
     * Defaults to 80, or the port specified by the server if the current
930
     * request is insecure.
931
     * @return integer port number for insecure requests.
932
     * @see setPort()
933
     */
934
    public function getPort()
935
    {
936
        if ($this->_port === null) {
937
            $this->_port = !$this->getIsSecureConnection() && isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : 80;
938
        }
939
940
        return $this->_port;
941
    }
942
943
    /**
944
     * Sets the port to use for insecure requests.
945
     * This setter is provided in case a custom port is necessary for certain
946
     * server configurations.
947
     * @param integer $value port number.
948
     */
949
    public function setPort($value)
950
    {
951
        if ($value != $this->_port) {
952
            $this->_port = (int) $value;
953
            $this->_hostInfo = null;
954
        }
955
    }
956
957
    private $_securePort;
958
959
    /**
960
     * Returns the port to use for secure requests.
961
     * Defaults to 443, or the port specified by the server if the current
962
     * request is secure.
963
     * @return integer port number for secure requests.
964
     * @see setSecurePort()
965
     */
966
    public function getSecurePort()
967
    {
968
        if ($this->_securePort === null) {
969
            $this->_securePort = $this->getIsSecureConnection() && isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : 443;
970
        }
971
972
        return $this->_securePort;
973
    }
974
975
    /**
976
     * Sets the port to use for secure requests.
977
     * This setter is provided in case a custom port is necessary for certain
978
     * server configurations.
979
     * @param integer $value port number.
980
     */
981
    public function setSecurePort($value)
982
    {
983
        if ($value != $this->_securePort) {
984
            $this->_securePort = (int) $value;
985
            $this->_hostInfo = null;
986
        }
987
    }
988
989
    private $_contentTypes;
990
991
    /**
992
     * Returns the content types acceptable by the end user.
993
     * This is determined by the `Accept` HTTP header. For example,
994
     *
995
     * ```php
996
     * $_SERVER['HTTP_ACCEPT'] = 'text/plain; q=0.5, application/json; version=1.0, application/xml; version=2.0;';
997
     * $types = $request->getAcceptableContentTypes();
998
     * print_r($types);
999
     * // displays:
1000
     * // [
1001
     * //     'application/json' => ['q' => 1, 'version' => '1.0'],
1002
     * //      'application/xml' => ['q' => 1, 'version' => '2.0'],
1003
     * //           'text/plain' => ['q' => 0.5],
1004
     * // ]
1005
     * ```
1006
     *
1007
     * @return array the content types ordered by the quality score. Types with the highest scores
1008
     * will be returned first. The array keys are the content types, while the array values
1009
     * are the corresponding quality score and other parameters as given in the header.
1010
     */
1011 2
    public function getAcceptableContentTypes()
1012
    {
1013 2
        if ($this->_contentTypes === null) {
1014 2
            if (isset($_SERVER['HTTP_ACCEPT'])) {
1015 2
                $this->_contentTypes = $this->parseAcceptHeader($_SERVER['HTTP_ACCEPT']);
1016 2
            } else {
1017 1
                $this->_contentTypes = [];
1018
            }
1019 2
        }
1020
1021 2
        return $this->_contentTypes;
1022
    }
1023
1024
    /**
1025
     * Sets the acceptable content types.
1026
     * Please refer to [[getAcceptableContentTypes()]] on the format of the parameter.
1027
     * @param array $value the content types that are acceptable by the end user. They should
1028
     * be ordered by the preference level.
1029
     * @see getAcceptableContentTypes()
1030
     * @see parseAcceptHeader()
1031
     */
1032
    public function setAcceptableContentTypes($value)
1033
    {
1034
        $this->_contentTypes = $value;
1035
    }
1036
1037
    /**
1038
     * Returns request content-type
1039
     * The Content-Type header field indicates the MIME type of the data
1040
     * contained in [[getRawBody()]] or, in the case of the HEAD method, the
1041
     * media type that would have been sent had the request been a GET.
1042
     * For the MIME-types the user expects in response, see [[acceptableContentTypes]].
1043
     * @return string request content-type. Null is returned if this information is not available.
1044
     * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17
1045
     * HTTP 1.1 header field definitions
1046
     */
1047
    public function getContentType()
1048
    {
1049
        if (isset($_SERVER['CONTENT_TYPE'])) {
1050
            return $_SERVER['CONTENT_TYPE'];
1051
        } elseif (isset($_SERVER['HTTP_CONTENT_TYPE'])) {
1052
            //fix bug https://bugs.php.net/bug.php?id=66606
1053
            return $_SERVER['HTTP_CONTENT_TYPE'];
1054
        }
1055
1056
        return null;
1057
    }
1058
1059
    private $_languages;
1060
1061
    /**
1062
     * Returns the languages acceptable by the end user.
1063
     * This is determined by the `Accept-Language` HTTP header.
1064
     * @return array the languages ordered by the preference level. The first element
1065
     * represents the most preferred language.
1066
     */
1067 1
    public function getAcceptableLanguages()
1068
    {
1069 1
        if ($this->_languages === null) {
1070
            if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
1071
                $this->_languages = array_keys($this->parseAcceptHeader($_SERVER['HTTP_ACCEPT_LANGUAGE']));
1072
            } else {
1073
                $this->_languages = [];
1074
            }
1075
        }
1076
1077 1
        return $this->_languages;
1078
    }
1079
1080
    /**
1081
     * @param array $value the languages that are acceptable by the end user. They should
1082
     * be ordered by the preference level.
1083
     */
1084 1
    public function setAcceptableLanguages($value)
1085
    {
1086 1
        $this->_languages = $value;
1087 1
    }
1088
1089
    /**
1090
     * Parses the given `Accept` (or `Accept-Language`) header.
1091
     *
1092
     * This method will return the acceptable values with their quality scores and the corresponding parameters
1093
     * as specified in the given `Accept` header. The array keys of the return value are the acceptable values,
1094
     * while the array values consisting of the corresponding quality scores and parameters. The acceptable
1095
     * values with the highest quality scores will be returned first. For example,
1096
     *
1097
     * ```php
1098
     * $header = 'text/plain; q=0.5, application/json; version=1.0, application/xml; version=2.0;';
1099
     * $accepts = $request->parseAcceptHeader($header);
1100
     * print_r($accepts);
1101
     * // displays:
1102
     * // [
1103
     * //     'application/json' => ['q' => 1, 'version' => '1.0'],
1104
     * //      'application/xml' => ['q' => 1, 'version' => '2.0'],
1105
     * //           'text/plain' => ['q' => 0.5],
1106
     * // ]
1107
     * ```
1108
     *
1109
     * @param string $header the header to be parsed
1110
     * @return array the acceptable values ordered by their quality score. The values with the highest scores
1111
     * will be returned first.
1112
     */
1113 3
    public function parseAcceptHeader($header)
1114
    {
1115 3
        $accepts = [];
1116 3
        foreach (explode(',', $header) as $i => $part) {
1117 3
            $params = preg_split('/\s*;\s*/', trim($part), -1, PREG_SPLIT_NO_EMPTY);
1118 3
            if (empty($params)) {
1119 1
                continue;
1120
            }
1121
            $values = [
1122 3
                'q' => [$i, array_shift($params), 1],
1123 3
            ];
1124 3
            foreach ($params as $param) {
1125 2
                if (strpos($param, '=') !== false) {
1126 2
                    list ($key, $value) = explode('=', $param, 2);
1127 2
                    if ($key === 'q') {
1128 2
                        $values['q'][2] = (double) $value;
1129 2
                    } else {
1130 1
                        $values[$key] = $value;
1131
                    }
1132 2
                } else {
1133 1
                    $values[] = $param;
1134
                }
1135 3
            }
1136 3
            $accepts[] = $values;
1137 3
        }
1138
1139 3
        usort($accepts, function ($a, $b) {
1140 3
            $a = $a['q']; // index, name, q
1141 3
            $b = $b['q'];
1142 3
            if ($a[2] > $b[2]) {
1143 1
                return -1;
1144 3
            } elseif ($a[2] < $b[2]) {
1145 2
                return 1;
1146 2
            } elseif ($a[1] === $b[1]) {
1147
                return $a[0] > $b[0] ? 1 : -1;
1148 2
            } elseif ($a[1] === '*/*') {
1149
                return 1;
1150 2
            } elseif ($b[1] === '*/*') {
1151
                return -1;
1152
            } else {
1153 2
                $wa = $a[1][strlen($a[1]) - 1] === '*';
1154 2
                $wb = $b[1][strlen($b[1]) - 1] === '*';
1155 2
                if ($wa xor $wb) {
1156
                    return $wa ? 1 : -1;
1157
                } else {
1158 2
                    return $a[0] > $b[0] ? 1 : -1;
1159
                }
1160
            }
1161 3
        });
1162
1163 3
        $result = [];
1164 3
        foreach ($accepts as $accept) {
1165 3
            $name = $accept['q'][1];
1166 3
            $accept['q'] = $accept['q'][2];
1167 3
            $result[$name] = $accept;
1168 3
        }
1169
1170 3
        return $result;
1171
    }
1172
1173
    /**
1174
     * Returns the user-preferred language that should be used by this application.
1175
     * The language resolution is based on the user preferred languages and the languages
1176
     * supported by the application. The method will try to find the best match.
1177
     * @param array $languages a list of the languages supported by the application. If this is empty, the current
1178
     * application language will be returned without further processing.
1179
     * @return string the language that the application should use.
1180
     */
1181 1
    public function getPreferredLanguage(array $languages = [])
1182
    {
1183 1
        if (empty($languages)) {
1184 1
            return Yii::$app->language;
1185
        }
1186 1
        foreach ($this->getAcceptableLanguages() as $acceptableLanguage) {
1187 1
            $acceptableLanguage = str_replace('_', '-', strtolower($acceptableLanguage));
1188 1
            foreach ($languages as $language) {
1189 1
                $normalizedLanguage = str_replace('_', '-', strtolower($language));
1190
1191 1
                if ($normalizedLanguage === $acceptableLanguage || // en-us==en-us
1192 1
                    strpos($acceptableLanguage, $normalizedLanguage . '-') === 0 || // en==en-us
1193 1
                    strpos($normalizedLanguage, $acceptableLanguage . '-') === 0) { // en-us==en
1194
1195 1
                    return $language;
1196
                }
1197 1
            }
1198 1
        }
1199
1200 1
        return reset($languages);
1201
    }
1202
1203
    /**
1204
     * Gets the Etags.
1205
     *
1206
     * @return array The entity tags
1207
     */
1208
    public function getETags()
1209
    {
1210
        if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
1211
            return preg_split('/[\s,]+/', str_replace('-gzip', '', $_SERVER['HTTP_IF_NONE_MATCH']), -1, PREG_SPLIT_NO_EMPTY);
1212
        } else {
1213
            return [];
1214
        }
1215
    }
1216
1217
    /**
1218
     * Returns the cookie collection.
1219
     * Through the returned cookie collection, you may access a cookie using the following syntax:
1220
     *
1221
     * ```php
1222
     * $cookie = $request->cookies['name']
1223
     * if ($cookie !== null) {
1224
     *     $value = $cookie->value;
1225
     * }
1226
     *
1227
     * // alternatively
1228
     * $value = $request->cookies->getValue('name');
1229
     * ```
1230
     *
1231
     * @return CookieCollection the cookie collection.
1232
     */
1233 24
    public function getCookies()
1234
    {
1235 24
        if ($this->_cookies === null) {
1236 24
            $this->_cookies = new CookieCollection($this->loadCookies(), [
1237 24
                'readOnly' => true,
1238 24
            ]);
1239 24
        }
1240
1241 24
        return $this->_cookies;
1242
    }
1243
1244
    /**
1245
     * Converts `$_COOKIE` into an array of [[Cookie]].
1246
     * @return array the cookies obtained from request
1247
     * @throws InvalidConfigException if [[cookieValidationKey]] is not set when [[enableCookieValidation]] is true
1248
     */
1249 24
    protected function loadCookies()
1250
    {
1251 24
        $cookies = [];
1252 24
        if ($this->enableCookieValidation) {
1253 24
            if ($this->cookieValidationKey == '') {
1254
                throw new InvalidConfigException(get_class($this) . '::cookieValidationKey must be configured with a secret key.');
1255
            }
1256 24
            foreach ($_COOKIE as $name => $value) {
1257
                if (!is_string($value)) {
1258
                    continue;
1259
                }
1260
                $data = Yii::$app->getSecurity()->validateData($value, $this->cookieValidationKey);
1261
                if ($data === false) {
1262
                    continue;
1263
                }
1264
                $data = @unserialize($data);
1265
                if (is_array($data) && isset($data[0], $data[1]) && $data[0] === $name) {
1266
                    $cookies[$name] = new Cookie([
1267
                        'name' => $name,
1268
                        'value' => $data[1],
1269
                        'expire' => null,
1270
                    ]);
1271
                }
1272 24
            }
1273 24
        } else {
1274
            foreach ($_COOKIE as $name => $value) {
1275
                $cookies[$name] = new Cookie([
1276
                    'name' => $name,
1277
                    'value' => $value,
1278
                    'expire' => null,
1279
                ]);
1280
            }
1281
        }
1282
1283 24
        return $cookies;
1284
    }
1285
1286
    private $_csrfToken;
1287
1288
    /**
1289
     * Returns the token used to perform CSRF validation.
1290
     *
1291
     * This token is generated in a way to prevent [BREACH attacks](http://breachattack.com/). It may be passed
1292
     * along via a hidden field of an HTML form or an HTTP header value to support CSRF validation.
1293
     * @param boolean $regenerate whether to regenerate CSRF token. When this parameter is true, each time
1294
     * this method is called, a new CSRF token will be generated and persisted (in session or cookie).
1295
     * @return string the token used to perform CSRF validation.
1296
     */
1297 27
    public function getCsrfToken($regenerate = false)
1298
    {
1299 27
        if ($this->_csrfToken === null || $regenerate) {
1300 27
            if ($regenerate || ($token = $this->loadCsrfToken()) === null) {
1301 25
                $token = $this->generateCsrfToken();
1302 25
            }
1303
            // the mask doesn't need to be very random
1304 27
            $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.';
1305 27
            $mask = substr(str_shuffle(str_repeat($chars, 5)), 0, static::CSRF_MASK_LENGTH);
1306
            // The + sign may be decoded as blank space later, which will fail the validation
1307 27
            $this->_csrfToken = str_replace('+', '.', base64_encode($mask . $this->xorTokens($token, $mask)));
1308 27
        }
1309
1310 27
        return $this->_csrfToken;
1311
    }
1312
1313
    /**
1314
     * Loads the CSRF token from cookie or session.
1315
     * @return string the CSRF token loaded from cookie or session. Null is returned if the cookie or session
1316
     * does not have CSRF token.
1317
     */
1318 27
    protected function loadCsrfToken()
1319
    {
1320 27
        if ($this->enableCsrfCookie) {
1321 24
            return $this->getCookies()->getValue($this->csrfParam);
1322
        } else {
1323 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...
1324
        }
1325
    }
1326
1327
    /**
1328
     * Generates  an unmasked random token used to perform CSRF validation.
1329
     * @return string the random token for CSRF validation.
1330
     */
1331 25
    protected function generateCsrfToken()
1332
    {
1333 25
        $token = Yii::$app->getSecurity()->generateRandomString();
1334 25
        if ($this->enableCsrfCookie) {
1335 24
            $cookie = $this->createCsrfCookie($token);
1336 24
            Yii::$app->getResponse()->getCookies()->add($cookie);
1337 24
        } else {
1338 1
            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...
1339
        }
1340 25
        return $token;
1341
    }
1342
1343
    /**
1344
     * Returns the XOR result of two strings.
1345
     * If the two strings are of different lengths, the shorter one will be padded to the length of the longer one.
1346
     * @param string $token1
1347
     * @param string $token2
1348
     * @return string the XOR result
1349
     */
1350 27
    private function xorTokens($token1, $token2)
1351
    {
1352 27
        $n1 = StringHelper::byteLength($token1);
1353 27
        $n2 = StringHelper::byteLength($token2);
1354 27
        if ($n1 > $n2) {
1355 27
            $token2 = str_pad($token2, $n1, $token2);
1356 27
        } elseif ($n1 < $n2) {
1357 3
            $token1 = str_pad($token1, $n2, $n1 === 0 ? ' ' : $token1);
1358 3
        }
1359
1360 27
        return $token1 ^ $token2;
1361
    }
1362
1363
    /**
1364
     * @return string the CSRF token sent via [[CSRF_HEADER]] by browser. Null is returned if no such header is sent.
1365
     */
1366 3
    public function getCsrfTokenFromHeader()
1367
    {
1368 3
        $key = 'HTTP_' . str_replace('-', '_', strtoupper(static::CSRF_HEADER));
1369 3
        return isset($_SERVER[$key]) ? $_SERVER[$key] : null;
1370
    }
1371
1372
    /**
1373
     * Creates a cookie with a randomly generated CSRF token.
1374
     * Initial values specified in [[csrfCookie]] will be applied to the generated cookie.
1375
     * @param string $token the CSRF token
1376
     * @return Cookie the generated cookie
1377
     * @see enableCsrfValidation
1378
     */
1379 24
    protected function createCsrfCookie($token)
1380
    {
1381 24
        $options = $this->csrfCookie;
1382 24
        $options['name'] = $this->csrfParam;
1383 24
        $options['value'] = $token;
1384 24
        return new Cookie($options);
1385
    }
1386
1387
    /**
1388
     * Performs the CSRF validation.
1389
     *
1390
     * This method will validate the user-provided CSRF token by comparing it with the one stored in cookie or session.
1391
     * This method is mainly called in [[Controller::beforeAction()]].
1392
     *
1393
     * Note that the method will NOT perform CSRF validation if [[enableCsrfValidation]] is false or the HTTP method
1394
     * is among GET, HEAD or OPTIONS.
1395
     *
1396
     * @param string $token the user-provided CSRF token to be validated. If null, the token will be retrieved from
1397
     * the [[csrfParam]] POST field or HTTP header.
1398
     * This parameter is available since version 2.0.4.
1399
     * @return boolean whether CSRF token is valid. If [[enableCsrfValidation]] is false, this method will return true.
1400
     */
1401 3
    public function validateCsrfToken($token = null)
1402
    {
1403 3
        $method = $this->getMethod();
1404
        // only validate CSRF token on non-"safe" methods http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
1405 3
        if (!$this->enableCsrfValidation || in_array($method, ['GET', 'HEAD', 'OPTIONS'], true)) {
1406 3
            return true;
1407
        }
1408
1409 3
        $trueToken = $this->loadCsrfToken();
1410
1411 3
        if ($token !== null) {
1412 1
            return $this->validateCsrfTokenInternal($token, $trueToken);
1413
        } else {
1414 3
            return $this->validateCsrfTokenInternal($this->getBodyParam($this->csrfParam), $trueToken)
1415 3
                || $this->validateCsrfTokenInternal($this->getCsrfTokenFromHeader(), $trueToken);
1416
        }
1417
    }
1418
1419
    /**
1420
     * Validates CSRF token
1421
     *
1422
     * @param string $token
1423
     * @param string $trueToken
1424
     * @return boolean
1425
     */
1426 3
    private function validateCsrfTokenInternal($token, $trueToken)
1427
    {
1428 3
        if (!is_string($token)) {
1429 3
            return false;
1430
        }
1431
1432 3
        $token = base64_decode(str_replace('.', '+', $token));
1433 3
        $n = StringHelper::byteLength($token);
1434 3
        if ($n <= static::CSRF_MASK_LENGTH) {
1435
            return false;
1436
        }
1437 3
        $mask = StringHelper::byteSubstr($token, 0, static::CSRF_MASK_LENGTH);
1438 3
        $token = StringHelper::byteSubstr($token, static::CSRF_MASK_LENGTH, $n - static::CSRF_MASK_LENGTH);
1439 3
        $token = $this->xorTokens($mask, $token);
1440
1441 3
        return $token === $trueToken;
1442
    }
1443
}
1444