Completed
Push — 2.1 ( a136f4...497a07 )
by
unknown
11:16
created

Request::getServerPort()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 2
cts 2
cp 1
cc 2
eloc 2
nc 2
nop 0
crap 2
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\web;
9
10
use Psr\Http\Message\RequestInterface;
11
use Psr\Http\Message\StreamInterface;
12
use Psr\Http\Message\UploadedFileInterface;
13
use Psr\Http\Message\UriInterface;
14
use Yii;
15
use yii\base\InvalidConfigException;
16
use yii\di\Instance;
17
use yii\helpers\ArrayHelper;
18
use yii\helpers\Html;
19
use yii\http\Cookie;
20
use yii\http\CookieCollection;
21
use yii\http\FileStream;
22
use yii\http\MemoryStream;
23
use yii\http\MessageTrait;
24
use yii\http\UploadedFile;
25
use yii\http\Uri;
26
use yii\validators\IpValidator;
27
28
/**
29
 * The web Request class represents an HTTP request.
30
 *
31
 * It encapsulates the $_SERVER variable and resolves its inconsistency among different Web servers.
32
 * Also it provides an interface to retrieve request parameters from $_POST, $_GET, $_COOKIES and REST
33
 * parameters sent via other HTTP methods like PUT or DELETE.
34
 *
35
 * Request is configured as an application component in [[\yii\web\Application]] by default.
36
 * You can access that instance via `Yii::$app->request`.
37
 *
38
 * For more details and usage information on Request, see the [guide article on requests](guide:runtime-requests).
39
 *
40
 * @property string $absoluteUrl The currently requested absolute URL. This property is read-only.
41
 * @property array $acceptableContentTypes The content types ordered by the quality score. Types with the
42
 * highest scores will be returned first. The array keys are the content types, while the array values are the
43
 * corresponding quality score and other parameters as given in the header.
44
 * @property array $acceptableLanguages The languages ordered by the preference level. The first element
45
 * represents the most preferred language.
46
 * @property string|null $authPassword The password sent via HTTP authentication, null if the password is not
47
 * given. This property is read-only.
48
 * @property string|null $authUser The username sent via HTTP authentication, null if the username is not
49
 * given. This property is read-only.
50
 * @property string $baseUrl The relative URL for the application.
51
 * @property array $bodyParams The request parameters given in the request body.
52
 * @property string $contentType Request content-type. Null is returned if this information is not available.
53
 * This property is read-only.
54
 * @property CookieCollection $cookies The cookie collection. This property is read-only.
55
 * @property string $csrfToken The token used to perform CSRF validation. This property is read-only.
56
 * @property string $csrfTokenFromHeader The CSRF token sent via [[CSRF_HEADER]] by browser. Null is returned
57
 * if no such header is sent. This property is read-only.
58
 * @property array $eTags The entity tags. This property is read-only.
59
 * @property string|null $hostInfo Schema and hostname part (with port number if needed) of the request URL
60
 * (e.g. `http://www.yiiframework.com`), null if can't be obtained from `$_SERVER` and wasn't set. See
61
 * [[getHostInfo()]] for security related notes on this property.
62
 * @property string|null $hostName Hostname part of the request URL (e.g. `www.yiiframework.com`). This
63
 * property is read-only.
64
 * @property bool $isAjax Whether this is an AJAX (XMLHttpRequest) request. This property is read-only.
65
 * @property bool $isDelete Whether this is a DELETE request. This property is read-only.
66
 * @property bool $isFlash Whether this is an Adobe Flash or Adobe Flex request. This property is read-only.
67
 * @property bool $isGet Whether this is a GET request. This property is read-only.
68
 * @property bool $isHead Whether this is a HEAD request. This property is read-only.
69
 * @property bool $isOptions Whether this is a OPTIONS request. This property is read-only.
70
 * @property bool $isPatch Whether this is a PATCH request. This property is read-only.
71
 * @property bool $isPjax Whether this is a PJAX request. This property is read-only.
72
 * @property bool $isPost Whether this is a POST request. This property is read-only.
73
 * @property bool $isPut Whether this is a PUT request. This property is read-only.
74
 * @property bool $isSecureConnection If the request is sent via secure channel (https). This property is
75
 * read-only.
76
 * @property string $method Request method, such as GET, POST, HEAD, PUT, PATCH, DELETE. The value returned is
77
 * turned into upper case.
78
 * @property UriInterface $uri the URI instance.
79
 * @property mixed $requestTarget the message's request target.
80
 * @property string $pathInfo Part of the request URL that is after the entry script and before the question
81
 * mark. Note, the returned path info is already URL-decoded.
82
 * @property int $port Port number for insecure requests.
83
 * @property array $queryParams The request GET parameter values.
84
 * @property string $queryString Part of the request URL that is after the question mark. This property is
85
 * read-only.
86
 * @property string $rawBody The request body.
87
 * @property string|null $referrer URL referrer, null if not available. This property is read-only.
88
 * @property string|null $origin URL origin, null if not available. This property is read-only.
89
 * @property string $scriptFile The entry script file path.
90
 * @property string $scriptUrl The relative URL of the entry script.
91
 * @property int $securePort Port number for secure requests.
92
 * @property string $serverName Server name, null if not available. This property is read-only.
93
 * @property int|null $serverPort Server port number, null if not available. This property is read-only.
94
 * @property string $url The currently requested relative URL. Note that the URI returned may be URL-encoded
95
 * depending on the client.
96
 * @property array $uploadedFiles Uploaded files for this request. See [[getUploadedFiles()]] for details.
97
 * @property string|null $userAgent User agent, null if not available. This property is read-only.
98
 * @property string|null $userHost User host name, null if not available. This property is read-only.
99
 * @property string|null $userIP User IP address, null if not available. This property is read-only.
100
 *
101
 * @author Qiang Xue <[email protected]>
102
 * @since 2.0
103
 * @SuppressWarnings(PHPMD.SuperGlobals)
104
 */
105
class Request extends \yii\base\Request implements RequestInterface
106
{
107
    use MessageTrait;
108
109
    /**
110
     * The name of the HTTP header for sending CSRF token.
111
     */
112
    const CSRF_HEADER = 'X-CSRF-Token';
113
    /**
114
     * The length of the CSRF token mask.
115
     * @deprecated 2.0.12 The mask length is now equal to the token length.
116
     */
117
    const CSRF_MASK_LENGTH = 8;
118
119
    /**
120
     * @var bool whether to enable CSRF (Cross-Site Request Forgery) validation. Defaults to true.
121
     * When CSRF validation is enabled, forms submitted to an Yii Web application must be originated
122
     * from the same application. If not, a 400 HTTP exception will be raised.
123
     *
124
     * Note, this feature requires that the user client accepts cookie. Also, to use this feature,
125
     * forms submitted via POST method must contain a hidden input whose name is specified by [[csrfParam]].
126
     * You may use [[\yii\helpers\Html::beginForm()]] to generate his hidden input.
127
     *
128
     * In JavaScript, you may get the values of [[csrfParam]] and [[csrfToken]] via `yii.getCsrfParam()` and
129
     * `yii.getCsrfToken()`, respectively. The [[\yii\web\YiiAsset]] asset must be registered.
130
     * You also need to include CSRF meta tags in your pages by using [[\yii\helpers\Html::csrfMetaTags()]].
131
     *
132
     * @see Controller::enableCsrfValidation
133
     * @see http://en.wikipedia.org/wiki/Cross-site_request_forgery
134
     */
135
    public $enableCsrfValidation = true;
136
    /**
137
     * @var string the name of the token used to prevent CSRF. Defaults to '_csrf'.
138
     * This property is used only when [[enableCsrfValidation]] is true.
139
     */
140
    public $csrfParam = '_csrf';
141
    /**
142
     * @var array the configuration for creating the CSRF [[Cookie|cookie]]. This property is used only when
143
     * both [[enableCsrfValidation]] and [[enableCsrfCookie]] are true.
144
     */
145
    public $csrfCookie = ['httpOnly' => true];
146
    /**
147
     * @var bool whether to use cookie to persist CSRF token. If false, CSRF token will be stored
148
     * in session under the name of [[csrfParam]]. Note that while storing CSRF tokens in session increases
149
     * security, it requires starting a session for every page, which will degrade your site performance.
150
     */
151
    public $enableCsrfCookie = true;
152
    /**
153
     * @var bool whether cookies should be validated to ensure they are not tampered. Defaults to true.
154
     */
155
    public $enableCookieValidation = true;
156
    /**
157
     * @var string a secret key used for cookie validation. This property must be set if [[enableCookieValidation]] is true.
158
     */
159
    public $cookieValidationKey;
160
    /**
161
     * @var string the name of the POST parameter that is used to indicate if a request is a PUT, PATCH or DELETE
162
     * request tunneled through POST. Defaults to '_method'.
163
     * @see getMethod()
164
     * @see getBodyParams()
165
     */
166
    public $methodParam = '_method';
167
    /**
168
     * @var array the parsers for converting the raw HTTP request body into [[bodyParams]].
169
     * The array keys are the request `Content-Types`, and the array values are the
170
     * corresponding configurations for [[Yii::createObject|creating the parser objects]].
171
     * A parser must implement the [[RequestParserInterface]].
172
     *
173
     * To enable parsing for JSON requests you can use the [[JsonParser]] class like in the following example:
174
     *
175
     * ```
176
     * [
177
     *     'application/json' => \yii\web\JsonParser::class,
178
     * ]
179
     * ```
180
     *
181
     * To register a parser for parsing all request types you can use `'*'` as the array key.
182
     * This one will be used as a fallback in case no other types match.
183
     *
184
     * @see getBodyParams()
185
     */
186
    public $parsers = [];
187
    /**
188
     * @var string name of the class to be used for uploaded file instantiation.
189
     * This class should implement [[UploadedFileInterface]].
190
     * @since 2.1.0
191
     */
192
    public $uploadedFileClass = UploadedFile::class;
193
    /**
194
     * @var array the configuration for trusted security related headers.
195
     *
196
     * An array key is an IPv4 or IPv6 IP address in CIDR notation for matching a client.
197
     *
198
     * An array value is a list of headers to trust. These will be matched against
199
     * [[secureHeaders]] to determine which headers are allowed to be sent by a specified host.
200
     * The case of the header names must be the same as specified in [[secureHeaders]].
201
     *
202
     * For example, to trust all headers listed in [[secureHeaders]] for IP addresses
203
     * in range `192.168.0.0-192.168.0.254` write the following:
204
     *
205
     * ```php
206
     * [
207
     *     '192.168.0.0/24',
208
     * ]
209
     * ```
210
     *
211
     * To trust just the `X-Forwarded-For` header from `10.0.0.1`, use:
212
     *
213
     * ```
214
     * [
215
     *     '10.0.0.1' => ['X-Forwarded-For']
216
     * ]
217
     * ```
218
     *
219
     * Default is to trust all headers except those listed in [[secureHeaders]] from all hosts.
220
     * Matches are tried in order and searching is stopped when IP matches.
221
     *
222
     * > Info: Matching is performed using [[IpValidator]].
223
     *   See [[IpValidator::::setRanges()|IpValidator::setRanges()]]
224
     *   and [[IpValidator::networks]] for advanced matching.
225
     *
226
     * @see $secureHeaders
227
     * @since 2.0.13
228
     */
229
    public $trustedHosts = [];
230
    /**
231
     * @var array lists of headers that are, by default, subject to the trusted host configuration.
232
     * These headers will be filtered unless explicitly allowed in [[trustedHosts]].
233
     * The match of header names is case-insensitive.
234
     * @see https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
235
     * @see $trustedHosts
236
     * @since 2.0.13
237
     */
238
    public $secureHeaders = [
239
        'X-Forwarded-For',
240
        'X-Forwarded-Host',
241
        'X-Forwarded-Proto',
242
        'Front-End-Https',
243
        'X-Rewrite-Url',
244
    ];
245
    /**
246
     * @var string[] List of headers where proxies store the real client IP.
247
     * It's not advisable to put insecure headers here.
248
     * The match of header names is case-insensitive.
249
     * @see $trustedHosts
250
     * @see $secureHeaders
251
     * @since 2.0.13
252
     */
253
    public $ipHeaders = [
254
        'X-Forwarded-For',
255
    ];
256
    /**
257
     * @var array list of headers to check for determining whether the connection is made via HTTPS.
258
     * The array keys are header names and the array value is a list of header values that indicate a secure connection.
259
     * The match of header names and values is case-insensitive.
260
     * It's not advisable to put insecure headers here.
261
     * @see $trustedHosts
262
     * @see $secureHeaders
263
     * @since 2.0.13
264
     */
265
    public $secureProtocolHeaders = [
266
        'X-Forwarded-Proto' => ['https'],
267
        'Front-End-Https' => ['on'],
268
    ];
269
270
    /**
271
     * @var CookieCollection Collection of request cookies.
272
     */
273
    private $_cookies;
274
    /**
275
     * @var string the HTTP method of the request.
276
     */
277
    private $_method;
278
    /**
279
     * @var UriInterface the URI instance associated with request.
280
     */
281
    private $_uri;
282
    /**
283
     * @var mixed the message's request target.
284
     */
285
    private $_requestTarget;
286
    /**
287
     * @var array uploaded files.
288
     * @since 2.1.0
289
     */
290
    private $_uploadedFiles;
291
292
293
    /**
294
     * Resolves the current request into a route and the associated parameters.
295
     * @return array the first element is the route, and the second is the associated parameters.
296
     * @throws NotFoundHttpException if the request cannot be resolved.
297
     */
298 1
    public function resolve()
299
    {
300 1
        $result = Yii::$app->getUrlManager()->parseRequest($this);
301 1
        if ($result !== false) {
302 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...
303 1
            if ($this->_queryParams === null) {
304 1
                $_GET = $params + $_GET; // preserve numeric keys
305
            } else {
306 1
                $this->_queryParams = $params + $this->_queryParams;
307
            }
308
309 1
            return [$route, $this->getQueryParams()];
310
        }
311
312
        throw new NotFoundHttpException(Yii::t('yii', 'Page not found.'));
313
    }
314
315
    /**
316
     * Filters headers according to the [[trustedHosts]].
317
     * @param array $rawHeaders
318
     * @return array filtered headers
319
     * @since 2.0.13
320
     */
321 113
    protected function filterHeaders($rawHeaders)
322
    {
323
        // do not trust any of the [[secureHeaders]] by default
324 113
        $trustedHeaders = [];
325
326
        // check if the client is a trusted host
327 113
        if (!empty($this->trustedHosts)) {
328 19
            $validator = $this->getIpValidator();
329 19
            $ip = $this->getRemoteIP();
330 19
            foreach ($this->trustedHosts as $cidr => $headers) {
331 19
                if (!is_array($headers)) {
332 19
                    $cidr = $headers;
333 19
                    $headers = $this->secureHeaders;
334
                }
335 19
                $validator->setRanges($cidr);
336 19
                if ($validator->validate($ip)) {
337 3
                    $trustedHeaders = $headers;
338 19
                    break;
339
                }
340
            }
341
        }
342
343 113
        $rawHeaders = array_change_key_case($rawHeaders, CASE_LOWER);
344
345
        // filter all secure headers unless they are trusted
346 113
        foreach ($this->secureHeaders as $secureHeader) {
347 113
            if (!in_array($secureHeader, $trustedHeaders)) {
348 113
                unset($rawHeaders[strtolower($secureHeader)]);
349
            }
350
        }
351
352 113
        return $rawHeaders;
353
    }
354
355
    /**
356
     * Creates instance of [[IpValidator]].
357
     * You can override this method to adjust validator or implement different matching strategy.
358
     *
359
     * @return IpValidator
360
     * @since 2.0.13
361
     */
362 19
    protected function getIpValidator()
363
    {
364 19
        return new IpValidator();
365
    }
366
367
    /**
368
     * Returns default message's headers, which should be present once [[headerCollection]] is instantiated.
369
     * @return string[][] an associative array of the message's headers.
370
     */
371 113
    protected function defaultHeaders()
372
    {
373 113
        if (function_exists('getallheaders')) {
374
            $headers = getallheaders();
375 113
        } elseif (function_exists('http_get_request_headers')) {
376
            $headers = http_get_request_headers();
377
        } else {
378 113
            $headers = [];
379 113
            foreach ($_SERVER as $name => $value) {
380 110
                if (strncmp($name, 'HTTP_', 5) === 0) {
381 30
                    $name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
382 110
                    $headers[$name] = $value;
383
                }
384
            }
385
        }
386
387 113
        return $this->filterHeaders($headers);
388
    }
389
390
    /**
391
     * {@inheritdoc}
392
     * @since 2.1.0
393
     */
394
    public function getRequestTarget()
395
    {
396
        if ($this->_requestTarget === null) {
397
            $this->_requestTarget = $this->getUri()->__toString();
398
        }
399
        return $this->_requestTarget;
400
    }
401
402
    /**
403
     * Specifies the message's request target
404
     * @param mixed $requestTarget the message's request target.
405
     * @since 2.1.0
406
     */
407
    public function setRequestTarget($requestTarget)
408
    {
409
        $this->_requestTarget = $requestTarget;
410
    }
411
412
    /**
413
     * {@inheritdoc}
414
     * @since 2.1.0
415
     */
416
    public function withRequestTarget($requestTarget)
417
    {
418
        if ($this->getRequestTarget() === $requestTarget) {
419
            return $this;
420
        }
421
422
        $newInstance = clone $this;
423
        $newInstance->setRequestTarget($requestTarget);
424
        return $newInstance;
425
    }
426
427
    /**
428
     * {@inheritdoc}
429
     */
430 28
    public function getMethod()
431
    {
432 28
        if ($this->_method === null) {
433 19
            if (isset($_POST[$this->methodParam])) {
434 1
                $this->_method = $_POST[$this->methodParam];
435 18
            } elseif ($this->hasHeader('x-http-method-override')) {
436 1
                $this->_method = $this->getHeaderLine('x-http-method-override');
437 17
            } elseif (isset($_SERVER['REQUEST_METHOD'])) {
438 2
                $this->_method = $_SERVER['REQUEST_METHOD'];
439
            } else {
440 16
                $this->_method = 'GET';
441
            }
442
        }
443 28
        return $this->_method;
444
    }
445
446
    /**
447
     * Specifies request HTTP method.
448
     * @param string $method case-sensitive HTTP method.
449
     * @since 2.1.0
450
     */
451 11
    public function setMethod($method)
452
    {
453 11
        $this->_method =  $method;
454 11
    }
455
456
    /**
457
     * {@inheritdoc}
458
     * @since 2.1.0
459
     */
460 1
    public function withMethod($method)
461
    {
462 1
        if ($this->getMethod() === $method) {
463
            return $this;
464
        }
465
466 1
        $newInstance = clone $this;
467 1
        $newInstance->setMethod($method);
468 1
        return $newInstance;
469
    }
470
471
    /**
472
     * {@inheritdoc}
473
     * @since 2.1.0
474
     */
475
    public function getUri()
476
    {
477
        if (!$this->_uri instanceof UriInterface) {
478
            if ($this->_uri === null) {
479
                $uri = new Uri(['string' => $this->getAbsoluteUrl()]);
480
            } elseif ($this->_uri instanceof \Closure) {
481
                $uri = call_user_func($this->_uri, $this);
482
            } else {
483
                $uri = $this->_uri;
484
            }
485
486
            $this->_uri = Instance::ensure($uri, UriInterface::class);
487
        }
488
        return $this->_uri;
489
    }
490
491
    /**
492
     * Specifies the URI instance.
493
     * @param UriInterface|\Closure|array $uri URI instance or its DI compatible configuration.
494
     * @since 2.1.0
495
     */
496
    public function setUri($uri)
497
    {
498
        $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...
499
    }
500
501
    /**
502
     * {@inheritdoc}
503
     * @since 2.1.0
504
     */
505
    public function withUri(UriInterface $uri, $preserveHost = false)
506
    {
507
        if ($this->getUri() === $uri) {
508
            return $this;
509
        }
510
511
        $newInstance = clone $this;
512
513
        $newInstance->setUri($uri);
514
        if (!$preserveHost) {
515
            return $newInstance->withHeader('host', $uri->getHost());
516
        }
517
        return $newInstance;
518
    }
519
520
    /**
521
     * Returns whether this is a GET request.
522
     * @return bool whether this is a GET request.
523
     */
524 2
    public function getIsGet()
525
    {
526 2
        return $this->getMethod() === 'GET';
527
    }
528
529
    /**
530
     * Returns whether this is an OPTIONS request.
531
     * @return bool whether this is a OPTIONS request.
532
     */
533
    public function getIsOptions()
534
    {
535
        return $this->getMethod() === 'OPTIONS';
536
    }
537
538
    /**
539
     * Returns whether this is a HEAD request.
540
     * @return bool whether this is a HEAD request.
541
     */
542 9
    public function getIsHead()
543
    {
544 9
        return $this->getMethod() === 'HEAD';
545
    }
546
547
    /**
548
     * Returns whether this is a POST request.
549
     * @return bool whether this is a POST request.
550
     */
551
    public function getIsPost()
552
    {
553
        return $this->getMethod() === 'POST';
554
    }
555
556
    /**
557
     * Returns whether this is a DELETE request.
558
     * @return bool whether this is a DELETE request.
559
     */
560
    public function getIsDelete()
561
    {
562
        return $this->getMethod() === 'DELETE';
563
    }
564
565
    /**
566
     * Returns whether this is a PUT request.
567
     * @return bool whether this is a PUT request.
568
     */
569
    public function getIsPut()
570
    {
571
        return $this->getMethod() === 'PUT';
572
    }
573
574
    /**
575
     * Returns whether this is a PATCH request.
576
     * @return bool whether this is a PATCH request.
577
     */
578
    public function getIsPatch()
579
    {
580
        return $this->getMethod() === 'PATCH';
581
    }
582
583
    /**
584
     * Returns whether this is an AJAX (XMLHttpRequest) request.
585
     *
586
     * Note that jQuery doesn't set the header in case of cross domain
587
     * requests: https://stackoverflow.com/questions/8163703/cross-domain-ajax-doesnt-send-x-requested-with-header
588
     *
589
     * @return bool whether this is an AJAX (XMLHttpRequest) request.
590
     */
591 14
    public function getIsAjax()
592
    {
593 14
        return $this->getHeaderLine('x-requested-with') === 'XMLHttpRequest';
594
    }
595
596
    /**
597
     * Returns whether this is a PJAX request
598
     * @return bool whether this is a PJAX request
599
     */
600 3
    public function getIsPjax()
601
    {
602 3
        return $this->getIsAjax() && $this->hasHeader('x-pjax');
603
    }
604
605
    /**
606
     * Returns whether this is an Adobe Flash or Flex request.
607
     * @return bool whether this is an Adobe Flash or Adobe Flex request.
608
     */
609
    public function getIsFlash()
610
    {
611
        $userAgent = $this->getUserAgent();
612
        if ($userAgent === null) {
613
            return false;
614
        }
615
        return (stripos($userAgent, 'Shockwave') !== false || stripos($userAgent, 'Flash') !== false);
616
    }
617
618
    /**
619
     * Returns default message body to be used in case it is not explicitly set.
620
     * @return StreamInterface default body instance.
621
     */
622
    protected function defaultBody()
623
    {
624
        return new FileStream([
625
            'filename' => 'php://input',
626
            'mode' => 'r',
627
        ]);
628
    }
629
630
    /**
631
     * Returns the raw HTTP request body.
632
     * @return string the request body
633
     */
634
    public function getRawBody()
635
    {
636
        return $this->getBody()->__toString();
637
    }
638
639
    /**
640
     * Sets the raw HTTP request body, this method is mainly used by test scripts to simulate raw HTTP requests.
641
     * @param string $rawBody the request body
642
     */
643 6
    public function setRawBody($rawBody)
644
    {
645 6
        $body = new MemoryStream();
646 6
        $body->write($rawBody);
647 6
        $this->setBody($body);
648 6
    }
649
650
    private $_bodyParams;
651
652
    /**
653
     * Returns the request parameters given in the request body.
654
     *
655
     * Request parameters are determined using the parsers configured in [[parsers]] property.
656
     * If no parsers are configured for the current [[contentType]] it uses the PHP function `mb_parse_str()`
657
     * to parse the [[rawBody|request body]].
658
     *
659
     * Since 2.1.0 body params also include result of [[getUploadedFiles()]].
660
     *
661
     * @return array the request parameters given in the request body.
662
     * @throws InvalidConfigException if a registered parser does not implement the [[RequestParserInterface]].
663
     * @throws UnsupportedMediaTypeHttpException if unable to parse raw body.
664
     * @see getMethod()
665
     * @see getBodyParam()
666
     * @see setBodyParams()
667
     */
668 11
    public function getBodyParams()
669
    {
670 11
        if ($this->_bodyParams === null) {
671 8
            if (isset($_POST[$this->methodParam])) {
672
                $this->_bodyParams = $_POST;
673
                unset($this->_bodyParams[$this->methodParam]);
674
                return $this->_bodyParams;
675
            }
676
677 8
            $contentType = $this->getContentType();
678 8
            if (($pos = strpos($contentType, ';')) !== false) {
679
                // e.g. text/html; charset=UTF-8
680 2
                $contentType = trim(substr($contentType, 0, $pos));
681
            }
682
683 8
            if (isset($this->parsers[$contentType])) {
684 2
                $parser = Yii::createObject($this->parsers[$contentType]);
685 2
                if (!($parser instanceof RequestParserInterface)) {
686
                    throw new InvalidConfigException("The '$contentType' request parser is invalid. It must implement the yii\\web\\RequestParserInterface.");
687
                }
688 2
                $this->_bodyParams = $parser->parse($this);
689 6
            } elseif (isset($this->parsers['*'])) {
690
                $parser = Yii::createObject($this->parsers['*']);
691
                if (!($parser instanceof RequestParserInterface)) {
692
                    throw new InvalidConfigException('The fallback request parser is invalid. It must implement the yii\\web\\RequestParserInterface.');
693
                }
694
                $this->_bodyParams = $parser->parse($this);
695 6
            } elseif ($this->getMethod() === 'POST') {
696 6
                if ($contentType !== 'application/x-www-form-urlencoded' && $contentType !== 'multipart/form-data') {
697 1
                    throw new UnsupportedMediaTypeHttpException();
698
                }
699
                // PHP has already parsed the body so we have all params in $_POST
700 6
                $this->_bodyParams = $_POST;
701
702 6
                if ($contentType === 'multipart/form-data') {
703 6
                    $this->_bodyParams = ArrayHelper::merge($this->_bodyParams, $this->getUploadedFiles());
704
                }
705
            } else {
706 1
                if ($contentType !== 'application/x-www-form-urlencoded') {
707 1
                    throw new UnsupportedMediaTypeHttpException();
708
                }
709 1
                $this->_bodyParams = [];
710 1
                mb_parse_str($this->getBody()->__toString(), $this->_bodyParams);
711
            }
712
        }
713
714 11
        return $this->_bodyParams;
715
    }
716
717
    /**
718
     * Sets the request body parameters.
719
     * @param array $values the request body parameters (name-value pairs)
720
     * @see getBodyParam()
721
     * @see getBodyParams()
722
     */
723 3
    public function setBodyParams($values)
724
    {
725 3
        $this->_bodyParams = $values;
726 3
    }
727
728
    /**
729
     * Returns the named request body parameter value.
730
     * If the parameter does not exist, the second parameter passed to this method will be returned.
731
     * @param string $name the parameter name
732
     * @param mixed $defaultValue the default parameter value if the parameter does not exist.
733
     * @return mixed the parameter value
734
     * @see getBodyParams()
735
     * @see setBodyParams()
736
     */
737 4
    public function getBodyParam($name, $defaultValue = null)
738
    {
739 4
        $params = $this->getBodyParams();
740
741 4
        return isset($params[$name]) ? $params[$name] : $defaultValue;
742
    }
743
744
    /**
745
     * Returns POST parameter with a given name. If name isn't specified, returns an array of all POST parameters.
746
     *
747
     * @param string $name the parameter name
748
     * @param mixed $defaultValue the default parameter value if the parameter does not exist.
749
     * @return array|mixed
750
     */
751
    public function post($name = null, $defaultValue = null)
752
    {
753
        if ($name === null) {
754
            return $this->getBodyParams();
755
        }
756
757
        return $this->getBodyParam($name, $defaultValue);
758
    }
759
760
    private $_queryParams;
761
762
    /**
763
     * Returns the request parameters given in the [[queryString]].
764
     *
765
     * This method will return the contents of `$_GET` if params where not explicitly set.
766
     * @return array the request GET parameter values.
767
     * @see setQueryParams()
768
     */
769 29
    public function getQueryParams()
770
    {
771 29
        if ($this->_queryParams === null) {
772 23
            return $_GET;
773
        }
774
775 8
        return $this->_queryParams;
776
    }
777
778
    /**
779
     * Sets the request [[queryString]] parameters.
780
     * @param array $values the request query parameters (name-value pairs)
781
     * @see getQueryParam()
782
     * @see getQueryParams()
783
     */
784 8
    public function setQueryParams($values)
785
    {
786 8
        $this->_queryParams = $values;
787 8
    }
788
789
    /**
790
     * Returns GET parameter with a given name. If name isn't specified, returns an array of all GET parameters.
791
     *
792
     * @param string $name the parameter name
793
     * @param mixed $defaultValue the default parameter value if the parameter does not exist.
794
     * @return array|mixed
795
     */
796 15
    public function get($name = null, $defaultValue = null)
797
    {
798 15
        if ($name === null) {
799
            return $this->getQueryParams();
800
        }
801
802 15
        return $this->getQueryParam($name, $defaultValue);
803
    }
804
805
    /**
806
     * Returns the named GET parameter value.
807
     * If the GET parameter does not exist, the second parameter passed to this method will be returned.
808
     * @param string $name the GET parameter name.
809
     * @param mixed $defaultValue the default parameter value if the GET parameter does not exist.
810
     * @return mixed the GET parameter value
811
     * @see getBodyParam()
812
     */
813 20
    public function getQueryParam($name, $defaultValue = null)
814
    {
815 20
        $params = $this->getQueryParams();
816
817 20
        return isset($params[$name]) ? $params[$name] : $defaultValue;
818
    }
819
820
    private $_hostInfo;
821
    private $_hostName;
822
823
    /**
824
     * Returns the schema and host part of the current request URL.
825
     *
826
     * The returned URL does not have an ending slash.
827
     *
828
     * By default this value is based on the user request information. This method will
829
     * return the value of `$_SERVER['HTTP_HOST']` if it is available or `$_SERVER['SERVER_NAME']` if not.
830
     * You may want to check out the [PHP documentation](http://php.net/manual/en/reserved.variables.server.php)
831
     * for more information on these variables.
832
     *
833
     * You may explicitly specify it by setting the [[setHostInfo()|hostInfo]] property.
834
     *
835
     * > Warning: Dependent on the server configuration this information may not be
836
     * > reliable and [may be faked by the user sending the HTTP request](https://www.acunetix.com/vulnerabilities/web/host-header-attack).
837
     * > If the webserver is configured to serve the same site independent of the value of
838
     * > the `Host` header, this value is not reliable. In such situations you should either
839
     * > fix your webserver configuration or explicitly set the value by setting the [[setHostInfo()|hostInfo]] property.
840
     * > If you don't have access to the server configuration, you can setup [[\yii\filters\HostControl]] filter at
841
     * > application level in order to protect against such kind of attack.
842
     *
843
     * @property string|null schema and hostname part (with port number if needed) of the request URL
844
     * (e.g. `http://www.yiiframework.com`), null if can't be obtained from `$_SERVER` and wasn't set.
845
     * See [[getHostInfo()]] for security related notes on this property.
846
     * @return string|null schema and hostname part (with port number if needed) of the request URL
847
     * (e.g. `http://www.yiiframework.com`), null if can't be obtained from `$_SERVER` and wasn't set.
848
     * @see setHostInfo()
849
     */
850 24
    public function getHostInfo()
851
    {
852 24
        if ($this->_hostInfo === null) {
853 20
            $secure = $this->getIsSecureConnection();
854 20
            $http = $secure ? 'https' : 'http';
855 20
            if ($this->hasHeader('Host')) {
856 7
                $this->_hostInfo = $http . '://' . $this->getHeaderLine('Host');
857 13
            } elseif (isset($_SERVER['SERVER_NAME'])) {
858
                $this->_hostInfo = $http . '://' . $_SERVER['SERVER_NAME'];
859
                $port = $secure ? $this->getSecurePort() : $this->getPort();
860
                if (($port !== 80 && !$secure) || ($port !== 443 && $secure)) {
861
                    $this->_hostInfo .= ':' . $port;
862
                }
863
            }
864
        }
865
866 24
        return $this->_hostInfo;
867
    }
868
869
    /**
870
     * Sets the schema and host part of the application URL.
871
     * This setter is provided in case the schema and hostname cannot be determined
872
     * on certain Web servers.
873
     * @param string|null $value the schema and host part of the application URL. The trailing slashes will be removed.
874
     * @see getHostInfo() for security related notes on this property.
875
     */
876 57
    public function setHostInfo($value)
877
    {
878 57
        $this->_hostName = null;
879 57
        $this->_hostInfo = $value === null ? null : rtrim($value, '/');
880 57
    }
881
882
    /**
883
     * Returns the host part of the current request URL.
884
     * Value is calculated from current [[getHostInfo()|hostInfo]] property.
885
     *
886
     * > Warning: The content of this value may not be reliable, dependent on the server
887
     * > configuration. Please refer to [[getHostInfo()]] for more information.
888
     *
889
     * @return string|null hostname part of the request URL (e.g. `www.yiiframework.com`)
890
     * @see getHostInfo()
891
     * @since 2.0.10
892
     */
893 11
    public function getHostName()
894
    {
895 11
        if ($this->_hostName === null) {
896 11
            $this->_hostName = parse_url($this->getHostInfo(), PHP_URL_HOST);
897
        }
898
899 11
        return $this->_hostName;
900
    }
901
902
    private $_baseUrl;
903
904
    /**
905
     * Returns the relative URL for the application.
906
     * This is similar to [[scriptUrl]] except that it does not include the script file name,
907
     * and the ending slashes are removed.
908
     * @return string the relative URL for the application
909
     * @see setScriptUrl()
910
     */
911 257
    public function getBaseUrl()
912
    {
913 257
        if ($this->_baseUrl === null) {
914 256
            $this->_baseUrl = rtrim(dirname($this->getScriptUrl()), '\\/');
915
        }
916
917 257
        return $this->_baseUrl;
918
    }
919
920
    /**
921
     * Sets the relative URL for the application.
922
     * By default the URL is determined based on the entry script URL.
923
     * This setter is provided in case you want to change this behavior.
924
     * @param string $value the relative URL for the application
925
     */
926 1
    public function setBaseUrl($value)
927
    {
928 1
        $this->_baseUrl = $value;
929 1
    }
930
931
    private $_scriptUrl;
932
933
    /**
934
     * Returns the relative URL of the entry script.
935
     * The implementation of this method referenced Zend_Controller_Request_Http in Zend Framework.
936
     * @return string the relative URL of the entry script.
937
     * @throws InvalidConfigException if unable to determine the entry script URL
938
     */
939 258
    public function getScriptUrl()
940
    {
941 258
        if ($this->_scriptUrl === null) {
942 2
            $scriptFile = $this->getScriptFile();
943 1
            $scriptName = basename($scriptFile);
944 1
            if (isset($_SERVER['SCRIPT_NAME']) && basename($_SERVER['SCRIPT_NAME']) === $scriptName) {
945 1
                $this->_scriptUrl = $_SERVER['SCRIPT_NAME'];
946
            } elseif (isset($_SERVER['PHP_SELF']) && basename($_SERVER['PHP_SELF']) === $scriptName) {
947
                $this->_scriptUrl = $_SERVER['PHP_SELF'];
948
            } elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $scriptName) {
949
                $this->_scriptUrl = $_SERVER['ORIG_SCRIPT_NAME'];
950
            } elseif (isset($_SERVER['PHP_SELF']) && ($pos = strpos($_SERVER['PHP_SELF'], '/' . $scriptName)) !== false) {
951
                $this->_scriptUrl = substr($_SERVER['SCRIPT_NAME'], 0, $pos) . '/' . $scriptName;
952
            } elseif (!empty($_SERVER['DOCUMENT_ROOT']) && strpos($scriptFile, $_SERVER['DOCUMENT_ROOT']) === 0) {
953
                $this->_scriptUrl = str_replace('\\', '/', str_replace($_SERVER['DOCUMENT_ROOT'], '', $scriptFile));
954
            } else {
955
                throw new InvalidConfigException('Unable to determine the entry script URL.');
956
            }
957
        }
958
959 257
        return $this->_scriptUrl;
960
    }
961
962
    /**
963
     * Sets the relative URL for the application entry script.
964
     * This setter is provided in case the entry script URL cannot be determined
965
     * on certain Web servers.
966
     * @param string $value the relative URL for the application entry script.
967
     */
968 268
    public function setScriptUrl($value)
969
    {
970 268
        $this->_scriptUrl = $value === null ? null : '/' . trim($value, '/');
971 268
    }
972
973
    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...
974
975
    /**
976
     * Returns the entry script file path.
977
     * The default implementation will simply return `$_SERVER['SCRIPT_FILENAME']`.
978
     * @return string the entry script file path
979
     * @throws InvalidConfigException
980
     */
981 259
    public function getScriptFile()
982
    {
983 259
        if (isset($this->_scriptFile)) {
984 237
            return $this->_scriptFile;
985
        }
986
987 23
        if (isset($_SERVER['SCRIPT_FILENAME'])) {
988 21
            return $_SERVER['SCRIPT_FILENAME'];
989
        }
990
991 2
        throw new InvalidConfigException('Unable to determine the entry script file path.');
992
    }
993
994
    /**
995
     * Sets the entry script file path.
996
     * The entry script file path normally can be obtained from `$_SERVER['SCRIPT_FILENAME']`.
997
     * If your server configuration does not return the correct value, you may configure
998
     * this property to make it right.
999
     * @param string $value the entry script file path.
1000
     */
1001 237
    public function setScriptFile($value)
1002
    {
1003 237
        $this->_scriptFile = $value;
1004 237
    }
1005
1006
    private $_pathInfo;
1007
1008
    /**
1009
     * Returns the path info of the currently requested URL.
1010
     * A path info refers to the part that is after the entry script and before the question mark (query string).
1011
     * The starting and ending slashes are both removed.
1012
     * @return string part of the request URL that is after the entry script and before the question mark.
1013
     * Note, the returned path info is already URL-decoded.
1014
     * @throws InvalidConfigException if the path info cannot be determined due to unexpected server configuration
1015
     */
1016 18
    public function getPathInfo()
1017
    {
1018 18
        if ($this->_pathInfo === null) {
1019
            $this->_pathInfo = $this->resolvePathInfo();
1020
        }
1021
1022 18
        return $this->_pathInfo;
1023
    }
1024
1025
    /**
1026
     * Sets the path info of the current request.
1027
     * This method is mainly provided for testing purpose.
1028
     * @param string $value the path info of the current request
1029
     */
1030 19
    public function setPathInfo($value)
1031
    {
1032 19
        $this->_pathInfo = $value === null ? null : ltrim($value, '/');
1033 19
    }
1034
1035
    /**
1036
     * Resolves the path info part of the currently requested URL.
1037
     * A path info refers to the part that is after the entry script and before the question mark (query string).
1038
     * The starting slashes are both removed (ending slashes will be kept).
1039
     * @return string part of the request URL that is after the entry script and before the question mark.
1040
     * Note, the returned path info is decoded.
1041
     * @throws InvalidConfigException if the path info cannot be determined due to unexpected server configuration
1042
     */
1043
    protected function resolvePathInfo()
1044
    {
1045
        $pathInfo = $this->getUrl();
1046
1047
        if (($pos = strpos($pathInfo, '?')) !== false) {
1048
            $pathInfo = substr($pathInfo, 0, $pos);
1049
        }
1050
1051
        $pathInfo = urldecode($pathInfo);
1052
1053
        // try to encode in UTF8 if not so
1054
        // http://w3.org/International/questions/qa-forms-utf-8.html
1055
        if (!preg_match('%^(?:
1056
            [\x09\x0A\x0D\x20-\x7E]              # ASCII
1057
            | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
1058
            | \xE0[\xA0-\xBF][\x80-\xBF]         # excluding overlongs
1059
            | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
1060
            | \xED[\x80-\x9F][\x80-\xBF]         # excluding surrogates
1061
            | \xF0[\x90-\xBF][\x80-\xBF]{2}      # planes 1-3
1062
            | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
1063
            | \xF4[\x80-\x8F][\x80-\xBF]{2}      # plane 16
1064
            )*$%xs', $pathInfo)
1065
        ) {
1066
            $pathInfo = utf8_encode($pathInfo);
1067
        }
1068
1069
        $scriptUrl = $this->getScriptUrl();
1070
        $baseUrl = $this->getBaseUrl();
1071
        if (strpos($pathInfo, $scriptUrl) === 0) {
1072
            $pathInfo = substr($pathInfo, strlen($scriptUrl));
1073
        } elseif ($baseUrl === '' || strpos($pathInfo, $baseUrl) === 0) {
1074
            $pathInfo = substr($pathInfo, strlen($baseUrl));
1075
        } elseif (isset($_SERVER['PHP_SELF']) && strpos($_SERVER['PHP_SELF'], $scriptUrl) === 0) {
1076
            $pathInfo = substr($_SERVER['PHP_SELF'], strlen($scriptUrl));
1077
        } else {
1078
            throw new InvalidConfigException('Unable to determine the path info of the current request.');
1079
        }
1080
1081
        if (substr($pathInfo, 0, 1) === '/') {
1082
            $pathInfo = substr($pathInfo, 1);
1083
        }
1084
1085
        return (string) $pathInfo;
1086
    }
1087
1088
    /**
1089
     * Returns the currently requested absolute URL.
1090
     * This is a shortcut to the concatenation of [[hostInfo]] and [[url]].
1091
     * @return string the currently requested absolute URL.
1092
     */
1093
    public function getAbsoluteUrl()
1094
    {
1095
        return $this->getHostInfo() . $this->getUrl();
1096
    }
1097
1098
    private $_url;
1099
1100
    /**
1101
     * Returns the currently requested relative URL.
1102
     * This refers to the portion of the URL that is after the [[hostInfo]] part.
1103
     * It includes the [[queryString]] part if any.
1104
     * @return string the currently requested relative URL. Note that the URI returned may be URL-encoded depending on the client.
1105
     * @throws InvalidConfigException if the URL cannot be determined due to unusual server configuration
1106
     */
1107 11
    public function getUrl()
1108
    {
1109 11
        if ($this->_url === null) {
1110 3
            $this->_url = $this->resolveRequestUri();
1111
        }
1112
1113 11
        return $this->_url;
1114
    }
1115
1116
    /**
1117
     * Sets the currently requested relative URL.
1118
     * The URI must refer to the portion that is after [[hostInfo]].
1119
     * Note that the URI should be URL-encoded.
1120
     * @param string $value the request URI to be set
1121
     */
1122 24
    public function setUrl($value)
1123
    {
1124 24
        $this->_url = $value;
1125 24
    }
1126
1127
    /**
1128
     * Resolves the request URI portion for the currently requested URL.
1129
     * This refers to the portion that is after the [[hostInfo]] part. It includes the [[queryString]] part if any.
1130
     * The implementation of this method referenced Zend_Controller_Request_Http in Zend Framework.
1131
     * @return string|bool the request URI portion for the currently requested URL.
1132
     * Note that the URI returned may be URL-encoded depending on the client.
1133
     * @throws InvalidConfigException if the request URI cannot be determined due to unusual server configuration
1134
     */
1135 3
    protected function resolveRequestUri()
1136
    {
1137 3
        if ($this->hasHeader('x-rewrite-url')) { // IIS
1138
            $requestUri = $this->getHeaderLine('x-rewrite-url');
1139 3
        } elseif (isset($_SERVER['REQUEST_URI'])) {
1140 3
            $requestUri = $_SERVER['REQUEST_URI'];
1141 3
            if ($requestUri !== '' && $requestUri[0] !== '/') {
1142 3
                $requestUri = preg_replace('/^(http|https):\/\/[^\/]+/i', '', $requestUri);
1143
            }
1144
        } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { // IIS 5.0 CGI
1145
            $requestUri = $_SERVER['ORIG_PATH_INFO'];
1146
            if (!empty($_SERVER['QUERY_STRING'])) {
1147
                $requestUri .= '?' . $_SERVER['QUERY_STRING'];
1148
            }
1149
        } else {
1150
            throw new InvalidConfigException('Unable to determine the request URI.');
1151
        }
1152
1153 3
        return $requestUri;
1154
    }
1155
1156
    /**
1157
     * Returns part of the request URL that is after the question mark.
1158
     * @return string part of the request URL that is after the question mark
1159
     */
1160
    public function getQueryString()
1161
    {
1162
        return isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '';
1163
    }
1164
1165
    /**
1166
     * Return if the request is sent via secure channel (https).
1167
     * @return bool if the request is sent via secure channel (https)
1168
     */
1169 37
    public function getIsSecureConnection()
1170
    {
1171 37
        if (isset($_SERVER['HTTPS']) && (strcasecmp($_SERVER['HTTPS'], 'on') === 0 || $_SERVER['HTTPS'] == 1)) {
1172 2
            return true;
1173
        }
1174 35
        foreach ($this->secureProtocolHeaders as $header => $values) {
1175 35
            if ($this->hasHeader($header)) {
1176 2
                foreach ($values as $value) {
1177 2
                    if (strcasecmp($this->getHeaderLine($header), $value) === 0) {
1178 35
                        return true;
1179
                    }
1180
                }
1181
            }
1182
        }
1183
1184 33
        return false;
1185
    }
1186
1187
    /**
1188
     * Returns the server name.
1189
     * @return string server name, null if not available
1190
     */
1191 1
    public function getServerName()
1192
    {
1193 1
        return isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : null;
1194
    }
1195
1196
    /**
1197
     * Returns the server port number.
1198
     * @return int|null server port number, null if not available
1199
     */
1200 1
    public function getServerPort()
1201
    {
1202 1
        return isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : null;
1203
    }
1204
1205
    /**
1206
     * Returns the URL referrer.
1207
     * @return string|null URL referrer, null if not available
1208
     */
1209
    public function getReferrer()
1210
    {
1211
        if (!$this->hasHeader('Referer')) {
1212
            return null;
1213
        }
1214
        return $this->getHeaderLine('Referer');
1215
    }
1216
1217
    /**
1218
     * Returns the URL origin of a CORS request.
1219
     *
1220
     * The return value is taken from the `Origin` [[getHeaders()|header]] sent by the browser.
1221
     *
1222
     * Note that the origin request header indicates where a fetch originates from.
1223
     * It doesn't include any path information, but only the server name.
1224
     * It is sent with a CORS requests, as well as with POST requests.
1225
     * It is similar to the referer header, but, unlike this header, it doesn't disclose the whole path.
1226
     * Please refer to <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin> for more information.
1227
     *
1228
     * @return string|null URL origin of a CORS request, `null` if not available.
1229
     * @see getHeaders()
1230
     * @since 2.0.13
1231
     */
1232 1
    public function getOrigin()
1233
    {
1234 1
        return $this->getHeaderLine('origin');
1235
    }
1236
1237
    /**
1238
     * Returns the user agent.
1239
     * @return string|null user agent, null if not available
1240
     */
1241
    public function getUserAgent()
1242
    {
1243
        if (!$this->hasHeader('User-Agent')) {
1244
            return null;
1245
        }
1246
        return $this->getHeaderLine('User-Agent');
1247
    }
1248
1249
    /**
1250
     * Returns the user IP address.
1251
     * The IP is determined using headers and / or `$_SERVER` variables.
1252
     * @return string|null user IP address, null if not available
1253
     */
1254 32
    public function getUserIP()
1255
    {
1256 32
        foreach ($this->ipHeaders as $ipHeader) {
1257 32
            if ($this->hasHeader($ipHeader)) {
1258 32
                return trim(explode(',', $this->getHeaderLine($ipHeader))[0]);
1259
            }
1260
        }
1261
1262 31
        return $this->getRemoteIP();
1263
    }
1264
1265
    /**
1266
     * Returns the user host name.
1267
     * The HOST is determined using headers and / or `$_SERVER` variables.
1268
     * @return string|null user host name, null if not available
1269
     */
1270
    public function getUserHost()
1271
    {
1272
        foreach ($this->ipHeaders as $ipHeader) {
1273
            if ($this->hasHeader($ipHeader)) {
1274
                return gethostbyaddr(trim(explode(',', $this->getHeaderLine($ipHeader))[0]));
1275
            }
1276
        }
1277
1278
        return $this->getRemoteHost();
1279
    }
1280
1281
    /**
1282
     * Returns the IP on the other end of this connection.
1283
     * This is always the next hop, any headers are ignored.
1284
     * @return string|null remote IP address, `null` if not available.
1285
     * @since 2.0.13
1286
     */
1287 47
    public function getRemoteIP()
1288
    {
1289 47
        return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null;
1290
    }
1291
1292
    /**
1293
     * Returns the host name of the other end of this connection.
1294
     * This is always the next hop, any headers are ignored.
1295
     * @return string|null remote host name, `null` if not available
1296
     * @see getUserHost()
1297
     * @see getRemoteIP()
1298
     * @since 2.0.13
1299
     */
1300
    public function getRemoteHost()
1301
    {
1302
        return isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : null;
1303
    }
1304
1305
    /**
1306
     * @return string|null the username sent via HTTP authentication, null if the username is not given
1307
     */
1308 10
    public function getAuthUser()
1309
    {
1310 10
        return isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null;
1311
    }
1312
1313
    /**
1314
     * @return string|null the password sent via HTTP authentication, null if the password is not given
1315
     */
1316 10
    public function getAuthPassword()
1317
    {
1318 10
        return isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : null;
1319
    }
1320
1321
    private $_port;
1322
1323
    /**
1324
     * Returns the port to use for insecure requests.
1325
     * Defaults to 80, or the port specified by the server if the current
1326
     * request is insecure.
1327
     * @return int port number for insecure requests.
1328
     * @see setPort()
1329
     */
1330
    public function getPort()
1331
    {
1332
        if ($this->_port === null) {
1333
            $this->_port = !$this->getIsSecureConnection() && isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : 80;
1334
        }
1335
1336
        return $this->_port;
1337
    }
1338
1339
    /**
1340
     * Sets the port to use for insecure requests.
1341
     * This setter is provided in case a custom port is necessary for certain
1342
     * server configurations.
1343
     * @param int $value port number.
1344
     */
1345
    public function setPort($value)
1346
    {
1347
        if ($value != $this->_port) {
1348
            $this->_port = (int) $value;
1349
            $this->_hostInfo = null;
1350
        }
1351
    }
1352
1353
    private $_securePort;
1354
1355
    /**
1356
     * Returns the port to use for secure requests.
1357
     * Defaults to 443, or the port specified by the server if the current
1358
     * request is secure.
1359
     * @return int port number for secure requests.
1360
     * @see setSecurePort()
1361
     */
1362
    public function getSecurePort()
1363
    {
1364
        if ($this->_securePort === null) {
1365
            $this->_securePort = $this->getIsSecureConnection() && isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : 443;
1366
        }
1367
1368
        return $this->_securePort;
1369
    }
1370
1371
    /**
1372
     * Sets the port to use for secure requests.
1373
     * This setter is provided in case a custom port is necessary for certain
1374
     * server configurations.
1375
     * @param int $value port number.
1376
     */
1377
    public function setSecurePort($value)
1378
    {
1379
        if ($value != $this->_securePort) {
1380
            $this->_securePort = (int) $value;
1381
            $this->_hostInfo = null;
1382
        }
1383
    }
1384
1385
    private $_contentTypes;
1386
1387
    /**
1388
     * Returns the content types acceptable by the end user.
1389
     *
1390
     * This is determined by the `Accept` HTTP header. For example,
1391
     *
1392
     * ```php
1393
     * $_SERVER['HTTP_ACCEPT'] = 'text/plain; q=0.5, application/json; version=1.0, application/xml; version=2.0;';
1394
     * $types = $request->getAcceptableContentTypes();
1395
     * print_r($types);
1396
     * // displays:
1397
     * // [
1398
     * //     'application/json' => ['q' => 1, 'version' => '1.0'],
1399
     * //      'application/xml' => ['q' => 1, 'version' => '2.0'],
1400
     * //           'text/plain' => ['q' => 0.5],
1401
     * // ]
1402
     * ```
1403
     *
1404
     * @return array the content types ordered by the quality score. Types with the highest scores
1405
     * will be returned first. The array keys are the content types, while the array values
1406
     * are the corresponding quality score and other parameters as given in the header.
1407
     */
1408 3
    public function getAcceptableContentTypes()
1409
    {
1410 3
        if ($this->_contentTypes === null) {
1411 2
            if ($this->hasHeader('Accept')) {
1412 2
                $this->_contentTypes = $this->parseAcceptHeader($this->getHeaderLine('Accept'));
1413
            } else {
1414 1
                $this->_contentTypes = [];
1415
            }
1416
        }
1417
1418 3
        return $this->_contentTypes;
1419
    }
1420
1421
    /**
1422
     * Sets the acceptable content types.
1423
     * Please refer to [[getAcceptableContentTypes()]] on the format of the parameter.
1424
     * @param array $value the content types that are acceptable by the end user. They should
1425
     * be ordered by the preference level.
1426
     * @see getAcceptableContentTypes()
1427
     * @see parseAcceptHeader()
1428
     */
1429 1
    public function setAcceptableContentTypes($value)
1430
    {
1431 1
        $this->_contentTypes = $value;
1432 1
    }
1433
1434
    /**
1435
     * Returns request content-type
1436
     * The Content-Type header field indicates the MIME type of the data
1437
     * contained in [[getBody()]] or, in the case of the HEAD method, the
1438
     * media type that would have been sent had the request been a GET.
1439
     * For the MIME-types the user expects in response, see [[acceptableContentTypes]].
1440
     * @return string request content-type. Empty string is returned if this information is not available.
1441
     * @link https://tools.ietf.org/html/rfc2616#section-14.17
1442
     * HTTP 1.1 header field definitions
1443
     */
1444 12
    public function getContentType()
1445
    {
1446 12
        return $this->getHeaderLine('Content-Type');
1447
    }
1448
1449
    private $_languages;
1450
1451
    /**
1452
     * Returns the languages acceptable by the end user.
1453
     * This is determined by the `Accept-Language` HTTP header.
1454
     * @return array the languages ordered by the preference level. The first element
1455
     * represents the most preferred language.
1456
     */
1457 1
    public function getAcceptableLanguages()
1458
    {
1459 1
        if ($this->_languages === null) {
1460
            if ($this->hasHeader('Accept-Language')) {
1461
                $this->_languages = array_keys($this->parseAcceptHeader($this->getHeaderLine('Accept-Language')));
1462
            } else {
1463
                $this->_languages = [];
1464
            }
1465
        }
1466
1467 1
        return $this->_languages;
1468
    }
1469
1470
    /**
1471
     * @param array $value the languages that are acceptable by the end user. They should
1472
     * be ordered by the preference level.
1473
     */
1474 1
    public function setAcceptableLanguages($value)
1475
    {
1476 1
        $this->_languages = $value;
1477 1
    }
1478
1479
    /**
1480
     * Parses the given `Accept` (or `Accept-Language`) header.
1481
     *
1482
     * This method will return the acceptable values with their quality scores and the corresponding parameters
1483
     * as specified in the given `Accept` header. The array keys of the return value are the acceptable values,
1484
     * while the array values consisting of the corresponding quality scores and parameters. The acceptable
1485
     * values with the highest quality scores will be returned first. For example,
1486
     *
1487
     * ```php
1488
     * $header = 'text/plain; q=0.5, application/json; version=1.0, application/xml; version=2.0;';
1489
     * $accepts = $request->parseAcceptHeader($header);
1490
     * print_r($accepts);
1491
     * // displays:
1492
     * // [
1493
     * //     'application/json' => ['q' => 1, 'version' => '1.0'],
1494
     * //      'application/xml' => ['q' => 1, 'version' => '2.0'],
1495
     * //           'text/plain' => ['q' => 0.5],
1496
     * // ]
1497
     * ```
1498
     *
1499
     * @param string $header the header to be parsed
1500
     * @return array the acceptable values ordered by their quality score. The values with the highest scores
1501
     * will be returned first.
1502
     */
1503 3
    public function parseAcceptHeader($header)
1504
    {
1505 3
        $accepts = [];
1506 3
        foreach (explode(',', $header) as $i => $part) {
1507 3
            $params = preg_split('/\s*;\s*/', trim($part), -1, PREG_SPLIT_NO_EMPTY);
1508 3
            if (empty($params)) {
1509 1
                continue;
1510
            }
1511
            $values = [
1512 3
                'q' => [$i, array_shift($params), 1],
1513
            ];
1514 3
            foreach ($params as $param) {
1515 2
                if (strpos($param, '=') !== false) {
1516 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...
1517 2
                    if ($key === 'q') {
1518 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...
1519
                    } else {
1520 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...
1521
                    }
1522
                } else {
1523 2
                    $values[] = $param;
1524
                }
1525
            }
1526 3
            $accepts[] = $values;
1527
        }
1528
1529 3
        usort($accepts, function ($a, $b) {
1530 3
            $a = $a['q']; // index, name, q
1531 3
            $b = $b['q'];
1532 3
            if ($a[2] > $b[2]) {
1533 2
                return -1;
1534
            }
1535
1536 2
            if ($a[2] < $b[2]) {
1537 1
                return 1;
1538
            }
1539
1540 2
            if ($a[1] === $b[1]) {
1541
                return $a[0] > $b[0] ? 1 : -1;
1542
            }
1543
1544 2
            if ($a[1] === '*/*') {
1545
                return 1;
1546
            }
1547
1548 2
            if ($b[1] === '*/*') {
1549
                return -1;
1550
            }
1551
1552 2
            $wa = $a[1][strlen($a[1]) - 1] === '*';
1553 2
            $wb = $b[1][strlen($b[1]) - 1] === '*';
1554 2
            if ($wa xor $wb) {
1555
                return $wa ? 1 : -1;
1556
            }
1557
1558 2
            return $a[0] > $b[0] ? 1 : -1;
1559 3
        });
1560
1561 3
        $result = [];
1562 3
        foreach ($accepts as $accept) {
1563 3
            $name = $accept['q'][1];
1564 3
            $accept['q'] = $accept['q'][2];
1565 3
            $result[$name] = $accept;
1566
        }
1567
1568 3
        return $result;
1569
    }
1570
1571
    /**
1572
     * Returns the user-preferred language that should be used by this application.
1573
     * The language resolution is based on the user preferred languages and the languages
1574
     * supported by the application. The method will try to find the best match.
1575
     * @param array $languages a list of the languages supported by the application. If this is empty, the current
1576
     * application language will be returned without further processing.
1577
     * @return string the language that the application should use.
1578
     */
1579 1
    public function getPreferredLanguage(array $languages = [])
1580
    {
1581 1
        if (empty($languages)) {
1582 1
            return Yii::$app->language;
1583
        }
1584 1
        foreach ($this->getAcceptableLanguages() as $acceptableLanguage) {
1585 1
            $acceptableLanguage = str_replace('_', '-', strtolower($acceptableLanguage));
1586 1
            foreach ($languages as $language) {
1587 1
                $normalizedLanguage = str_replace('_', '-', strtolower($language));
1588
1589
                if (
1590 1
                    $normalizedLanguage === $acceptableLanguage // en-us==en-us
1591 1
                    || strpos($acceptableLanguage, $normalizedLanguage . '-') === 0 // en==en-us
1592 1
                    || strpos($normalizedLanguage, $acceptableLanguage . '-') === 0 // en-us==en
1593
                ) {
1594 1
                    return $language;
1595
                }
1596
            }
1597
        }
1598
1599 1
        return reset($languages);
1600
    }
1601
1602
    /**
1603
     * Gets the Etags.
1604
     *
1605
     * @return array The entity tags
1606
     */
1607
    public function getETags()
1608
    {
1609
        if ($this->hasHeader('if-none-match')) {
1610
            return preg_split('/[\s,]+/', str_replace('-gzip', '', $this->getHeaderLine('if-none-match')), -1, PREG_SPLIT_NO_EMPTY);
1611
        }
1612
1613
        return [];
1614
    }
1615
1616
    /**
1617
     * Returns the cookie collection.
1618
     *
1619
     * Through the returned cookie collection, you may access a cookie using the following syntax:
1620
     *
1621
     * ```php
1622
     * $cookie = $request->cookies['name']
1623
     * if ($cookie !== null) {
1624
     *     $value = $cookie->value;
1625
     * }
1626
     *
1627
     * // alternatively
1628
     * $value = $request->cookies->getValue('name');
1629
     * ```
1630
     *
1631
     * @return CookieCollection the cookie collection.
1632
     */
1633 33
    public function getCookies()
1634
    {
1635 33
        if ($this->_cookies === null) {
1636 33
            $this->_cookies = new CookieCollection($this->loadCookies(), [
1637 33
                'readOnly' => true,
1638
            ]);
1639
        }
1640
1641 33
        return $this->_cookies;
1642
    }
1643
1644
    /**
1645
     * Converts `$_COOKIE` into an array of [[Cookie]].
1646
     * @return array the cookies obtained from request
1647
     * @throws InvalidConfigException if [[cookieValidationKey]] is not set when [[enableCookieValidation]] is true
1648
     */
1649 33
    protected function loadCookies()
1650
    {
1651 33
        $cookies = [];
1652 33
        if ($this->enableCookieValidation) {
1653 32
            if ($this->cookieValidationKey == '') {
1654
                throw new InvalidConfigException(get_class($this) . '::cookieValidationKey must be configured with a secret key.');
1655
            }
1656 32
            foreach ($_COOKIE as $name => $value) {
1657
                if (!is_string($value)) {
1658
                    continue;
1659
                }
1660
                $data = Yii::$app->getSecurity()->validateData($value, $this->cookieValidationKey);
1661
                if ($data === false) {
1662
                    continue;
1663
                }
1664
                $data = @unserialize($data);
1665
                if (is_array($data) && isset($data[0], $data[1]) && $data[0] === $name) {
1666
                    $cookies[$name] = new Cookie([
1667 32
                        'name' => $name,
1668
                        'value' => $data[1],
1669
                        'expire' => null,
1670
                    ]);
1671
                }
1672
            }
1673
        } else {
1674 1
            foreach ($_COOKIE as $name => $value) {
1675
                $cookies[$name] = new Cookie([
1676
                    'name' => $name,
1677
                    'value' => $value,
1678
                    'expire' => null,
1679
                ]);
1680
            }
1681
        }
1682
1683 33
        return $cookies;
1684
    }
1685
1686
    /**
1687
     * Returns uploaded files for this request.
1688
     * Uploaded files are returned in format according to [PSR-7 Uploaded Files specs](http://www.php-fig.org/psr/psr-7/#16-uploaded-files).
1689
     * @return array uploaded files.
1690
     * @since 2.1.0
1691
     */
1692 12
    public function getUploadedFiles()
1693
    {
1694 12
        if ($this->_uploadedFiles === null) {
1695 6
            $this->getBodyParams(); // uploaded files are the part of the body and may be set while its parsing
1696 6
            if ($this->_uploadedFiles === null) {
1697 6
                $this->_uploadedFiles = $this->defaultUploadedFiles();
1698
            }
1699
        }
1700 12
        return $this->_uploadedFiles;
1701
    }
1702
1703
    /**
1704
     * Sets uploaded files for this request.
1705
     * Data structure for the uploaded files should follow [PSR-7 Uploaded Files specs](http://www.php-fig.org/psr/psr-7/#16-uploaded-files).
1706
     * @param array|null $uploadedFiles uploaded files.
1707
     * @since 2.1.0
1708
     */
1709 6
    public function setUploadedFiles($uploadedFiles)
1710
    {
1711 6
        $this->_uploadedFiles = $uploadedFiles;
0 ignored issues
show
Documentation Bug introduced by
It seems like $uploadedFiles can be null. However, the property $_uploadedFiles is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
1712 6
    }
1713
1714
    /**
1715
     * Initializes default uploaded files data structure parsing super-global $_FILES.
1716
     * @see http://www.php-fig.org/psr/psr-7/#16-uploaded-files
1717
     * @return array uploaded files.
1718
     * @since 2.1.0
1719
     */
1720 6
    protected function defaultUploadedFiles()
1721
    {
1722 6
        $files = [];
1723 6
        foreach ($_FILES as $class => $info) {
1724 3
            $files[$class] = [];
1725 3
            $this->populateUploadedFileRecursive($files[$class], $info['name'], $info['tmp_name'], $info['type'], $info['size'], $info['error']);
1726
        }
1727
1728 6
        return $files;
1729
    }
1730
1731
    /**
1732
     * Populates uploaded files array from $_FILE data structure recursively.
1733
     * @param array $files uploaded files array to be populated.
1734
     * @param mixed $names file names provided by PHP
1735
     * @param mixed $tempNames temporary file names provided by PHP
1736
     * @param mixed $types file types provided by PHP
1737
     * @param mixed $sizes file sizes provided by PHP
1738
     * @param mixed $errors uploading issues provided by PHP
1739
     * @since 2.1.0
1740
     */
1741 3
    private function populateUploadedFileRecursive(&$files, $names, $tempNames, $types, $sizes, $errors)
1742
    {
1743 3
        if (is_array($names)) {
1744 2
            foreach ($names as $i => $name) {
1745 2
                $files[$i] = [];
1746 2
                $this->populateUploadedFileRecursive($files[$i], $name, $tempNames[$i], $types[$i], $sizes[$i], $errors[$i]);
1747
            }
1748
        } else {
1749 3
            $files = Yii::createObject([
1750 3
                'class' => $this->uploadedFileClass,
1751 3
                'clientFilename' => $names,
1752 3
                'tempFilename' => $tempNames,
1753 3
                'clientMediaType' => $types,
1754 3
                'size' => $sizes,
1755 3
                'error' => $errors,
1756
            ]);
1757
        }
1758 3
    }
1759
1760
    /**
1761
     * Returns an uploaded file according to the given name.
1762
     * Name can be either a string HTML form input name, e.g. 'Item[file]' or array path, e.g. `['Item', 'file']`.
1763
     * Note: this method returns `null` in case given name matches multiple files.
1764
     * @param string|array $name HTML form input name or array path.
1765
     * @return UploadedFileInterface|null uploaded file instance, `null` - if not found.
1766
     * @since 2.1.0
1767
     */
1768 1
    public function getUploadedFileByName($name)
1769
    {
1770 1
        $uploadedFile = $this->findUploadedFiles($name);
1771 1
        if ($uploadedFile instanceof UploadedFileInterface) {
1772 1
            return $uploadedFile;
1773
        }
1774 1
        return null;
1775
    }
1776
1777
    /**
1778
     * Returns the list of uploaded file instances according to the given name.
1779
     * Name can be either a string HTML form input name, e.g. 'Item[file]' or array path, e.g. `['Item', 'file']`.
1780
     * Note: this method does NOT preserve uploaded files structure - it returns instances in single-level array (list),
1781
     * even if they are set by nested keys.
1782
     * @param string|array $name HTML form input name or array path.
1783
     * @return UploadedFileInterface[] list of uploaded file instances.
1784
     * @since 2.1.0
1785
     */
1786 1
    public function getUploadedFilesByName($name)
1787
    {
1788 1
        $uploadedFiles = $this->findUploadedFiles($name);
1789 1
        if ($uploadedFiles === null) {
1790
            return [];
1791
        }
1792 1
        if ($uploadedFiles instanceof UploadedFileInterface) {
1793 1
            return [$uploadedFiles];
1794
        }
1795 1
        return $this->reduceUploadedFiles($uploadedFiles);
1796
    }
1797
1798
    /**
1799
     * Finds the uploaded file or set of uploaded files inside [[$uploadedFiles]] according to given name.
1800
     * Name can be either a string HTML form input name, e.g. 'Item[file]' or array path, e.g. `['Item', 'file']`.
1801
     * @param string|array $name HTML form input name or array path.
1802
     * @return UploadedFileInterface|array|null
1803
     * @since 2.1.0
1804
     */
1805 2
    private function findUploadedFiles($name)
1806
    {
1807 2
        if (!is_array($name)) {
1808 2
            $name = preg_split('/\\]\\[|\\[|\\]/s', $name, -1, PREG_SPLIT_NO_EMPTY);
1809
        }
1810 2
        return ArrayHelper::getValue($this->getUploadedFiles(), $name);
1811
    }
1812
1813
    /**
1814
     * Reduces complex uploaded files structure to the single-level array (list).
1815
     * @param array $uploadedFiles raw set of the uploaded files.
1816
     * @return UploadedFileInterface[] list of uploaded files.
1817
     * @since 2.1.0
1818
     */
1819
    private function reduceUploadedFiles($uploadedFiles)
1820
    {
1821 1
        return array_reduce($uploadedFiles, function ($carry, $item) {
1822 1
            if ($item instanceof UploadedFileInterface) {
1823 1
                $carry[] = $item;
1824
            } else {
1825 1
                $carry = array_merge($carry, $this->reduceUploadedFiles($item));
1826
            }
1827 1
            return $carry;
1828 1
        }, []);
1829
    }
1830
1831
    private $_csrfToken;
1832
1833
    /**
1834
     * Returns the token used to perform CSRF validation.
1835
     *
1836
     * This token is generated in a way to prevent [BREACH attacks](http://breachattack.com/). It may be passed
1837
     * along via a hidden field of an HTML form or an HTTP header value to support CSRF validation.
1838
     * @param bool $regenerate whether to regenerate CSRF token. When this parameter is true, each time
1839
     * this method is called, a new CSRF token will be generated and persisted (in session or cookie).
1840
     * @return string the token used to perform CSRF validation.
1841
     */
1842 37
    public function getCsrfToken($regenerate = false)
1843
    {
1844 37
        if ($this->_csrfToken === null || $regenerate) {
1845 37
            if ($regenerate || ($token = $this->loadCsrfToken()) === null) {
1846 35
                $token = $this->generateCsrfToken();
1847
            }
1848 37
            $this->_csrfToken = Yii::$app->security->maskToken($token);
1849
        }
1850
1851 37
        return $this->_csrfToken;
1852
    }
1853
1854
    /**
1855
     * Loads the CSRF token from cookie or session.
1856
     * @return string the CSRF token loaded from cookie or session. Null is returned if the cookie or session
1857
     * does not have CSRF token.
1858
     */
1859 37
    protected function loadCsrfToken()
1860
    {
1861 37
        if ($this->enableCsrfCookie) {
1862 33
            return $this->getCookies()->getValue($this->csrfParam);
1863
        }
1864
1865 4
        return Yii::$app->getSession()->get($this->csrfParam);
0 ignored issues
show
Bug introduced by
The method getSession does only exist in yii\web\Application, but not in yii\console\Application.

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

Let’s take a look at an example:

class A
{
    public function foo() { }
}

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

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

Available Fixes

  1. Add an additional type-check:

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

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1866
    }
1867
1868
    /**
1869
     * Generates an unmasked random token used to perform CSRF validation.
1870
     * @return string the random token for CSRF validation.
1871
     */
1872 35
    protected function generateCsrfToken()
1873
    {
1874 35
        $token = Yii::$app->getSecurity()->generateRandomString();
1875 35
        if ($this->enableCsrfCookie) {
1876 33
            $cookie = $this->createCsrfCookie($token);
1877 33
            Yii::$app->getResponse()->getCookies()->add($cookie);
1878
        } else {
1879 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...
1880
        }
1881
1882 35
        return $token;
1883
    }
1884
1885
    /**
1886
     * @return string the CSRF token sent via [[CSRF_HEADER]] by browser. Null is returned if no such header is sent.
1887
     */
1888 3
    public function getCsrfTokenFromHeader()
1889
    {
1890 3
        return $this->getHeaderLine(static::CSRF_HEADER);
1891
    }
1892
1893
    /**
1894
     * Creates a cookie with a randomly generated CSRF token.
1895
     * Initial values specified in [[csrfCookie]] will be applied to the generated cookie.
1896
     * @param string $token the CSRF token
1897
     * @return Cookie the generated cookie
1898
     * @see enableCsrfValidation
1899
     */
1900 33
    protected function createCsrfCookie($token)
1901
    {
1902 33
        $options = $this->csrfCookie;
1903 33
        $options['name'] = $this->csrfParam;
1904 33
        $options['value'] = $token;
1905 33
        return new Cookie($options);
1906
    }
1907
1908
    /**
1909
     * Performs the CSRF validation.
1910
     *
1911
     * This method will validate the user-provided CSRF token by comparing it with the one stored in cookie or session.
1912
     * This method is mainly called in [[Controller::beforeAction()]].
1913
     *
1914
     * Note that the method will NOT perform CSRF validation if [[enableCsrfValidation]] is false or the HTTP method
1915
     * is among GET, HEAD or OPTIONS.
1916
     *
1917
     * @param string $clientSuppliedToken the user-provided CSRF token to be validated. If null, the token will be retrieved from
1918
     * the [[csrfParam]] POST field or HTTP header.
1919
     * This parameter is available since version 2.0.4.
1920
     * @return bool whether CSRF token is valid. If [[enableCsrfValidation]] is false, this method will return true.
1921
     */
1922 5
    public function validateCsrfToken($clientSuppliedToken = null)
1923
    {
1924 5
        $method = $this->getMethod();
1925
        // only validate CSRF token on non-"safe" methods https://tools.ietf.org/html/rfc2616#section-9.1.1
1926 5
        if (!$this->enableCsrfValidation || in_array($method, ['GET', 'HEAD', 'OPTIONS'], true)) {
1927 5
            return true;
1928
        }
1929
1930 3
        $trueToken = $this->getCsrfToken();
1931
1932 3
        if ($clientSuppliedToken !== null) {
1933 1
            return $this->validateCsrfTokenInternal($clientSuppliedToken, $trueToken);
1934
        }
1935
1936 3
        return $this->validateCsrfTokenInternal($this->getBodyParam($this->csrfParam), $trueToken)
1937 3
            || $this->validateCsrfTokenInternal($this->getCsrfTokenFromHeader(), $trueToken);
1938
    }
1939
1940
    /**
1941
     * Validates CSRF token.
1942
     *
1943
     * @param string $clientSuppliedToken The masked client-supplied token.
1944
     * @param string $trueToken The masked true token.
1945
     * @return bool
1946
     */
1947 3
    private function validateCsrfTokenInternal($clientSuppliedToken, $trueToken)
1948
    {
1949 3
        if (!is_string($clientSuppliedToken)) {
1950 3
            return false;
1951
        }
1952
1953 3
        $security = Yii::$app->security;
1954
1955 3
        return $security->unmaskToken($clientSuppliedToken) === $security->unmaskToken($trueToken);
1956
    }
1957
1958
    /**
1959
     * {@inheritdoc}
1960
     */
1961 2
    public function __clone()
1962
    {
1963 2
        parent::__clone();
1964
1965 2
        $this->cloneHttpMessageInternals();
1966
1967 2
        if (is_object($this->_cookies)) {
1968
            $this->_cookies = clone $this->_cookies;
1969
        }
1970 2
    }
1971
}
1972