comparison vendor/symfony/http-foundation/Request.php @ 17:129ea1e6d783

Update, including to Drupal core 8.6.10
author Chris Cannam
date Thu, 28 Feb 2019 13:21:36 +0000
parents c2387f117808
children af1871eacc83
comparison
equal deleted inserted replaced
16:c2387f117808 17:129ea1e6d783
59 const METHOD_CONNECT = 'CONNECT'; 59 const METHOD_CONNECT = 'CONNECT';
60 60
61 /** 61 /**
62 * @var string[] 62 * @var string[]
63 */ 63 */
64 protected static $trustedProxies = array(); 64 protected static $trustedProxies = [];
65 65
66 /** 66 /**
67 * @var string[] 67 * @var string[]
68 */ 68 */
69 protected static $trustedHostPatterns = array(); 69 protected static $trustedHostPatterns = [];
70 70
71 /** 71 /**
72 * @var string[] 72 * @var string[]
73 */ 73 */
74 protected static $trustedHosts = array(); 74 protected static $trustedHosts = [];
75 75
76 /** 76 /**
77 * Names for headers that can be trusted when 77 * Names for headers that can be trusted when
78 * using trusted proxies. 78 * using trusted proxies.
79 * 79 *
82 * The other headers are non-standard, but widely used 82 * The other headers are non-standard, but widely used
83 * by popular reverse proxies (like Apache mod_proxy or Amazon EC2). 83 * by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
84 * 84 *
85 * @deprecated since version 3.3, to be removed in 4.0 85 * @deprecated since version 3.3, to be removed in 4.0
86 */ 86 */
87 protected static $trustedHeaders = array( 87 protected static $trustedHeaders = [
88 self::HEADER_FORWARDED => 'FORWARDED', 88 self::HEADER_FORWARDED => 'FORWARDED',
89 self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR', 89 self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
90 self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST', 90 self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
91 self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO', 91 self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
92 self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT', 92 self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
93 ); 93 ];
94 94
95 protected static $httpMethodParameterOverride = false; 95 protected static $httpMethodParameterOverride = false;
96 96
97 /** 97 /**
98 * Custom parameters. 98 * Custom parameters.
224 private $isForwardedValid = true; 224 private $isForwardedValid = true;
225 225
226 private static $trustedHeaderSet = -1; 226 private static $trustedHeaderSet = -1;
227 227
228 /** @deprecated since version 3.3, to be removed in 4.0 */ 228 /** @deprecated since version 3.3, to be removed in 4.0 */
229 private static $trustedHeaderNames = array( 229 private static $trustedHeaderNames = [
230 self::HEADER_FORWARDED => 'FORWARDED', 230 self::HEADER_FORWARDED => 'FORWARDED',
231 self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR', 231 self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
232 self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST', 232 self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
233 self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO', 233 self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
234 self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT', 234 self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
235 ); 235 ];
236 236
237 private static $forwardedParams = array( 237 private static $forwardedParams = [
238 self::HEADER_X_FORWARDED_FOR => 'for', 238 self::HEADER_X_FORWARDED_FOR => 'for',
239 self::HEADER_X_FORWARDED_HOST => 'host', 239 self::HEADER_X_FORWARDED_HOST => 'host',
240 self::HEADER_X_FORWARDED_PROTO => 'proto', 240 self::HEADER_X_FORWARDED_PROTO => 'proto',
241 self::HEADER_X_FORWARDED_PORT => 'host', 241 self::HEADER_X_FORWARDED_PORT => 'host',
242 ); 242 ];
243 243
244 /** 244 /**
245 * @param array $query The GET parameters 245 * @param array $query The GET parameters
246 * @param array $request The POST parameters 246 * @param array $request The POST parameters
247 * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) 247 * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
248 * @param array $cookies The COOKIE parameters 248 * @param array $cookies The COOKIE parameters
249 * @param array $files The FILES parameters 249 * @param array $files The FILES parameters
250 * @param array $server The SERVER parameters 250 * @param array $server The SERVER parameters
251 * @param string|resource|null $content The raw body data 251 * @param string|resource|null $content The raw body data
252 */ 252 */
253 public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) 253 public function __construct(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
254 { 254 {
255 $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content); 255 $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content);
256 } 256 }
257 257
258 /** 258 /**
266 * @param array $cookies The COOKIE parameters 266 * @param array $cookies The COOKIE parameters
267 * @param array $files The FILES parameters 267 * @param array $files The FILES parameters
268 * @param array $server The SERVER parameters 268 * @param array $server The SERVER parameters
269 * @param string|resource|null $content The raw body data 269 * @param string|resource|null $content The raw body data
270 */ 270 */
271 public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) 271 public function initialize(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
272 { 272 {
273 $this->request = new ParameterBag($request); 273 $this->request = new ParameterBag($request);
274 $this->query = new ParameterBag($query); 274 $this->query = new ParameterBag($query);
275 $this->attributes = new ParameterBag($attributes); 275 $this->attributes = new ParameterBag($attributes);
276 $this->cookies = new ParameterBag($cookies); 276 $this->cookies = new ParameterBag($cookies);
300 { 300 {
301 // With the php's bug #66606, the php's built-in web server 301 // With the php's bug #66606, the php's built-in web server
302 // stores the Content-Type and Content-Length header values in 302 // stores the Content-Type and Content-Length header values in
303 // HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH fields. 303 // HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH fields.
304 $server = $_SERVER; 304 $server = $_SERVER;
305 if ('cli-server' === PHP_SAPI) { 305 if ('cli-server' === \PHP_SAPI) {
306 if (array_key_exists('HTTP_CONTENT_LENGTH', $_SERVER)) { 306 if (array_key_exists('HTTP_CONTENT_LENGTH', $_SERVER)) {
307 $server['CONTENT_LENGTH'] = $_SERVER['HTTP_CONTENT_LENGTH']; 307 $server['CONTENT_LENGTH'] = $_SERVER['HTTP_CONTENT_LENGTH'];
308 } 308 }
309 if (array_key_exists('HTTP_CONTENT_TYPE', $_SERVER)) { 309 if (array_key_exists('HTTP_CONTENT_TYPE', $_SERVER)) {
310 $server['CONTENT_TYPE'] = $_SERVER['HTTP_CONTENT_TYPE']; 310 $server['CONTENT_TYPE'] = $_SERVER['HTTP_CONTENT_TYPE'];
311 } 311 }
312 } 312 }
313 313
314 $request = self::createRequestFromFactory($_GET, $_POST, array(), $_COOKIE, $_FILES, $server); 314 $request = self::createRequestFromFactory($_GET, $_POST, [], $_COOKIE, $_FILES, $server);
315 315
316 if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded') 316 if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
317 && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH')) 317 && \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), ['PUT', 'DELETE', 'PATCH'])
318 ) { 318 ) {
319 parse_str($request->getContent(), $data); 319 parse_str($request->getContent(), $data);
320 $request->request = new ParameterBag($data); 320 $request->request = new ParameterBag($data);
321 } 321 }
322 322
337 * @param array $server The server parameters ($_SERVER) 337 * @param array $server The server parameters ($_SERVER)
338 * @param string|resource|null $content The raw body data 338 * @param string|resource|null $content The raw body data
339 * 339 *
340 * @return static 340 * @return static
341 */ 341 */
342 public static function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null) 342 public static function create($uri, $method = 'GET', $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
343 { 343 {
344 $server = array_replace(array( 344 $server = array_replace([
345 'SERVER_NAME' => 'localhost', 345 'SERVER_NAME' => 'localhost',
346 'SERVER_PORT' => 80, 346 'SERVER_PORT' => 80,
347 'HTTP_HOST' => 'localhost', 347 'HTTP_HOST' => 'localhost',
348 'HTTP_USER_AGENT' => 'Symfony/3.X', 348 'HTTP_USER_AGENT' => 'Symfony/3.X',
349 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 349 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
352 'REMOTE_ADDR' => '127.0.0.1', 352 'REMOTE_ADDR' => '127.0.0.1',
353 'SCRIPT_NAME' => '', 353 'SCRIPT_NAME' => '',
354 'SCRIPT_FILENAME' => '', 354 'SCRIPT_FILENAME' => '',
355 'SERVER_PROTOCOL' => 'HTTP/1.1', 355 'SERVER_PROTOCOL' => 'HTTP/1.1',
356 'REQUEST_TIME' => time(), 356 'REQUEST_TIME' => time(),
357 ), $server); 357 ], $server);
358 358
359 $server['PATH_INFO'] = ''; 359 $server['PATH_INFO'] = '';
360 $server['REQUEST_METHOD'] = strtoupper($method); 360 $server['REQUEST_METHOD'] = strtoupper($method);
361 361
362 $components = parse_url($uri); 362 $components = parse_url($uri);
375 } 375 }
376 } 376 }
377 377
378 if (isset($components['port'])) { 378 if (isset($components['port'])) {
379 $server['SERVER_PORT'] = $components['port']; 379 $server['SERVER_PORT'] = $components['port'];
380 $server['HTTP_HOST'] = $server['HTTP_HOST'].':'.$components['port']; 380 $server['HTTP_HOST'] .= ':'.$components['port'];
381 } 381 }
382 382
383 if (isset($components['user'])) { 383 if (isset($components['user'])) {
384 $server['PHP_AUTH_USER'] = $components['user']; 384 $server['PHP_AUTH_USER'] = $components['user'];
385 } 385 }
400 $server['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; 400 $server['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
401 } 401 }
402 // no break 402 // no break
403 case 'PATCH': 403 case 'PATCH':
404 $request = $parameters; 404 $request = $parameters;
405 $query = array(); 405 $query = [];
406 break; 406 break;
407 default: 407 default:
408 $request = array(); 408 $request = [];
409 $query = $parameters; 409 $query = $parameters;
410 break; 410 break;
411 } 411 }
412 412
413 $queryString = ''; 413 $queryString = '';
426 } 426 }
427 427
428 $server['REQUEST_URI'] = $components['path'].('' !== $queryString ? '?'.$queryString : ''); 428 $server['REQUEST_URI'] = $components['path'].('' !== $queryString ? '?'.$queryString : '');
429 $server['QUERY_STRING'] = $queryString; 429 $server['QUERY_STRING'] = $queryString;
430 430
431 return self::createRequestFromFactory($query, $request, array(), $cookies, $files, $server, $content); 431 return self::createRequestFromFactory($query, $request, [], $cookies, $files, $server, $content);
432 } 432 }
433 433
434 /** 434 /**
435 * Sets a callable able to create a Request instance. 435 * Sets a callable able to create a Request instance.
436 * 436 *
530 } catch (\LogicException $e) { 530 } catch (\LogicException $e) {
531 return trigger_error($e, E_USER_ERROR); 531 return trigger_error($e, E_USER_ERROR);
532 } 532 }
533 533
534 $cookieHeader = ''; 534 $cookieHeader = '';
535 $cookies = array(); 535 $cookies = [];
536 536
537 foreach ($this->cookies as $k => $v) { 537 foreach ($this->cookies as $k => $v) {
538 $cookies[] = $k.'='.$v; 538 $cookies[] = $k.'='.$v;
539 } 539 }
540 540
564 $_SERVER = $this->server->all(); 564 $_SERVER = $this->server->all();
565 $_COOKIE = $this->cookies->all(); 565 $_COOKIE = $this->cookies->all();
566 566
567 foreach ($this->headers->all() as $key => $value) { 567 foreach ($this->headers->all() as $key => $value) {
568 $key = strtoupper(str_replace('-', '_', $key)); 568 $key = strtoupper(str_replace('-', '_', $key));
569 if (in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH'))) { 569 if (\in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH'])) {
570 $_SERVER[$key] = implode(', ', $value); 570 $_SERVER[$key] = implode(', ', $value);
571 } else { 571 } else {
572 $_SERVER['HTTP_'.$key] = implode(', ', $value); 572 $_SERVER['HTTP_'.$key] = implode(', ', $value);
573 } 573 }
574 } 574 }
575 575
576 $request = array('g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE); 576 $request = ['g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE];
577 577
578 $requestOrder = ini_get('request_order') ?: ini_get('variables_order'); 578 $requestOrder = ini_get('request_order') ?: ini_get('variables_order');
579 $requestOrder = preg_replace('#[^cgp]#', '', strtolower($requestOrder)) ?: 'gp'; 579 $requestOrder = preg_replace('#[^cgp]#', '', strtolower($requestOrder)) ?: 'gp';
580 580
581 $_REQUEST = array(); 581 $_REQUEST = [];
582 foreach (str_split($requestOrder) as $order) { 582 foreach (str_split($requestOrder) as $order) {
583 $_REQUEST = array_merge($_REQUEST, $request[$order]); 583 $_REQUEST = array_merge($_REQUEST, $request[$order]);
584 } 584 }
585 } 585 }
586 586
596 */ 596 */
597 public static function setTrustedProxies(array $proxies/*, int $trustedHeaderSet*/) 597 public static function setTrustedProxies(array $proxies/*, int $trustedHeaderSet*/)
598 { 598 {
599 self::$trustedProxies = $proxies; 599 self::$trustedProxies = $proxies;
600 600
601 if (2 > func_num_args()) { 601 if (2 > \func_num_args()) {
602 @trigger_error(sprintf('The %s() method expects a bit field of Request::HEADER_* as second argument since Symfony 3.3. Defining it will be required in 4.0. ', __METHOD__), E_USER_DEPRECATED); 602 @trigger_error(sprintf('The %s() method expects a bit field of Request::HEADER_* as second argument since Symfony 3.3. Defining it will be required in 4.0. ', __METHOD__), E_USER_DEPRECATED);
603 603
604 return; 604 return;
605 } 605 }
606 $trustedHeaderSet = (int) func_get_arg(1); 606 $trustedHeaderSet = (int) func_get_arg(1);
642 { 642 {
643 self::$trustedHostPatterns = array_map(function ($hostPattern) { 643 self::$trustedHostPatterns = array_map(function ($hostPattern) {
644 return sprintf('{%s}i', $hostPattern); 644 return sprintf('{%s}i', $hostPattern);
645 }, $hostPatterns); 645 }, $hostPatterns);
646 // we need to reset trusted hosts on trusted host patterns change 646 // we need to reset trusted hosts on trusted host patterns change
647 self::$trustedHosts = array(); 647 self::$trustedHosts = [];
648 } 648 }
649 649
650 /** 650 /**
651 * Gets the list of trusted host patterns. 651 * Gets the list of trusted host patterns.
652 * 652 *
716 * 716 *
717 * @deprecated since version 3.3, to be removed in 4.0. Use the Request::getTrustedHeaderSet() method instead. 717 * @deprecated since version 3.3, to be removed in 4.0. Use the Request::getTrustedHeaderSet() method instead.
718 */ 718 */
719 public static function getTrustedHeaderName($key) 719 public static function getTrustedHeaderName($key)
720 { 720 {
721 if (2 > func_num_args() || func_get_arg(1)) { 721 if (2 > \func_num_args() || func_get_arg(1)) {
722 @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the Request::getTrustedHeaderSet() method instead.', __METHOD__), E_USER_DEPRECATED); 722 @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the Request::getTrustedHeaderSet() method instead.', __METHOD__), E_USER_DEPRECATED);
723 } 723 }
724 724
725 if (!array_key_exists($key, self::$trustedHeaders)) { 725 if (!array_key_exists($key, self::$trustedHeaders)) {
726 throw new \InvalidArgumentException(sprintf('Unable to get the trusted header name for key "%s".', $key)); 726 throw new \InvalidArgumentException(sprintf('Unable to get the trusted header name for key "%s".', $key));
743 { 743 {
744 if ('' == $qs) { 744 if ('' == $qs) {
745 return ''; 745 return '';
746 } 746 }
747 747
748 $parts = array(); 748 $parts = [];
749 $order = array(); 749 $order = [];
750 750
751 foreach (explode('&', $qs) as $param) { 751 foreach (explode('&', $qs) as $param) {
752 if ('' === $param || '=' === $param[0]) { 752 if ('' === $param || '=' === $param[0]) {
753 // Ignore useless delimiters, e.g. "x=y&". 753 // Ignore useless delimiters, e.g. "x=y&".
754 // Also ignore pairs with empty key, even if there was a value, e.g. "=value", as such nameless values cannot be retrieved anyway. 754 // Also ignore pairs with empty key, even if there was a value, e.g. "=value", as such nameless values cannot be retrieved anyway.
891 public function getClientIps() 891 public function getClientIps()
892 { 892 {
893 $ip = $this->server->get('REMOTE_ADDR'); 893 $ip = $this->server->get('REMOTE_ADDR');
894 894
895 if (!$this->isFromTrustedProxy()) { 895 if (!$this->isFromTrustedProxy()) {
896 return array($ip); 896 return [$ip];
897 } 897 }
898 898
899 return $this->getTrustedValues(self::HEADER_CLIENT_IP, $ip) ?: array($ip); 899 return $this->getTrustedValues(self::HEADER_CLIENT_IP, $ip) ?: [$ip];
900 } 900 }
901 901
902 /** 902 /**
903 * Returns the client IP address. 903 * Returns the client IP address.
904 * 904 *
1184 if ($path === $basePath = $this->getPathInfo()) { 1184 if ($path === $basePath = $this->getPathInfo()) {
1185 return ''; 1185 return '';
1186 } 1186 }
1187 1187
1188 $sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath); 1188 $sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath);
1189 $targetDirs = explode('/', isset($path[0]) && '/' === $path[0] ? substr($path, 1) : $path); 1189 $targetDirs = explode('/', substr($path, 1));
1190 array_pop($sourceDirs); 1190 array_pop($sourceDirs);
1191 $targetFile = array_pop($targetDirs); 1191 $targetFile = array_pop($targetDirs);
1192 1192
1193 foreach ($sourceDirs as $i => $dir) { 1193 foreach ($sourceDirs as $i => $dir) {
1194 if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) { 1194 if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) {
1197 break; 1197 break;
1198 } 1198 }
1199 } 1199 }
1200 1200
1201 $targetDirs[] = $targetFile; 1201 $targetDirs[] = $targetFile;
1202 $path = str_repeat('../', count($sourceDirs)).implode('/', $targetDirs); 1202 $path = str_repeat('../', \count($sourceDirs)).implode('/', $targetDirs);
1203 1203
1204 // A reference to the same base directory or an empty subdirectory must be prefixed with "./". 1204 // A reference to the same base directory or an empty subdirectory must be prefixed with "./".
1205 // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used 1205 // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
1206 // as the first segment of a relative-path reference, as it would be mistaken for a scheme name 1206 // as the first segment of a relative-path reference, as it would be mistaken for a scheme name
1207 // (see http://tools.ietf.org/html/rfc3986#section-4.2). 1207 // (see http://tools.ietf.org/html/rfc3986#section-4.2).
1240 * @return bool 1240 * @return bool
1241 */ 1241 */
1242 public function isSecure() 1242 public function isSecure()
1243 { 1243 {
1244 if ($this->isFromTrustedProxy() && $proto = $this->getTrustedValues(self::HEADER_CLIENT_PROTO)) { 1244 if ($this->isFromTrustedProxy() && $proto = $this->getTrustedValues(self::HEADER_CLIENT_PROTO)) {
1245 return in_array(strtolower($proto[0]), array('https', 'on', 'ssl', '1'), true); 1245 return \in_array(strtolower($proto[0]), ['https', 'on', 'ssl', '1'], true);
1246 } 1246 }
1247 1247
1248 $https = $this->server->get('HTTPS'); 1248 $https = $this->server->get('HTTPS');
1249 1249
1250 return !empty($https) && 'off' !== strtolower($https); 1250 return !empty($https) && 'off' !== strtolower($https);
1290 $this->isHostValid = false; 1290 $this->isHostValid = false;
1291 1291
1292 throw new SuspiciousOperationException(sprintf('Invalid Host "%s".', $host)); 1292 throw new SuspiciousOperationException(sprintf('Invalid Host "%s".', $host));
1293 } 1293 }
1294 1294
1295 if (count(self::$trustedHostPatterns) > 0) { 1295 if (\count(self::$trustedHostPatterns) > 0) {
1296 // to avoid host header injection attacks, you should provide a list of trusted host patterns 1296 // to avoid host header injection attacks, you should provide a list of trusted host patterns
1297 1297
1298 if (in_array($host, self::$trustedHosts)) { 1298 if (\in_array($host, self::$trustedHosts)) {
1299 return $host; 1299 return $host;
1300 } 1300 }
1301 1301
1302 foreach (self::$trustedHostPatterns as $pattern) { 1302 foreach (self::$trustedHostPatterns as $pattern) {
1303 if (preg_match($pattern, $host)) { 1303 if (preg_match($pattern, $host)) {
1351 1351
1352 if ('POST' === $this->method) { 1352 if ('POST' === $this->method) {
1353 if ($method = $this->headers->get('X-HTTP-METHOD-OVERRIDE')) { 1353 if ($method = $this->headers->get('X-HTTP-METHOD-OVERRIDE')) {
1354 $this->method = strtoupper($method); 1354 $this->method = strtoupper($method);
1355 } elseif (self::$httpMethodParameterOverride) { 1355 } elseif (self::$httpMethodParameterOverride) {
1356 $this->method = strtoupper($this->request->get('_method', $this->query->get('_method', 'POST'))); 1356 $method = $this->request->get('_method', $this->query->get('_method', 'POST'));
1357 if (\is_string($method)) {
1358 $this->method = strtoupper($method);
1359 }
1357 } 1360 }
1358 } 1361 }
1359 } 1362 }
1360 1363
1361 return $this->method; 1364 return $this->method;
1400 { 1403 {
1401 if (null === static::$formats) { 1404 if (null === static::$formats) {
1402 static::initializeFormats(); 1405 static::initializeFormats();
1403 } 1406 }
1404 1407
1405 return isset(static::$formats[$format]) ? static::$formats[$format] : array(); 1408 return isset(static::$formats[$format]) ? static::$formats[$format] : [];
1406 } 1409 }
1407 1410
1408 /** 1411 /**
1409 * Gets the format associated with the mime type. 1412 * Gets the format associated with the mime type.
1410 * 1413 *
1414 */ 1417 */
1415 public function getFormat($mimeType) 1418 public function getFormat($mimeType)
1416 { 1419 {
1417 $canonicalMimeType = null; 1420 $canonicalMimeType = null;
1418 if (false !== $pos = strpos($mimeType, ';')) { 1421 if (false !== $pos = strpos($mimeType, ';')) {
1419 $canonicalMimeType = substr($mimeType, 0, $pos); 1422 $canonicalMimeType = trim(substr($mimeType, 0, $pos));
1420 } 1423 }
1421 1424
1422 if (null === static::$formats) { 1425 if (null === static::$formats) {
1423 static::initializeFormats(); 1426 static::initializeFormats();
1424 } 1427 }
1425 1428
1426 foreach (static::$formats as $format => $mimeTypes) { 1429 foreach (static::$formats as $format => $mimeTypes) {
1427 if (in_array($mimeType, (array) $mimeTypes)) { 1430 if (\in_array($mimeType, (array) $mimeTypes)) {
1428 return $format; 1431 return $format;
1429 } 1432 }
1430 if (null !== $canonicalMimeType && in_array($canonicalMimeType, (array) $mimeTypes)) { 1433 if (null !== $canonicalMimeType && \in_array($canonicalMimeType, (array) $mimeTypes)) {
1431 return $format; 1434 return $format;
1432 } 1435 }
1433 } 1436 }
1434 } 1437 }
1435 1438
1443 { 1446 {
1444 if (null === static::$formats) { 1447 if (null === static::$formats) {
1445 static::initializeFormats(); 1448 static::initializeFormats();
1446 } 1449 }
1447 1450
1448 static::$formats[$format] = is_array($mimeTypes) ? $mimeTypes : array($mimeTypes); 1451 static::$formats[$format] = \is_array($mimeTypes) ? $mimeTypes : [$mimeTypes];
1449 } 1452 }
1450 1453
1451 /** 1454 /**
1452 * Gets the request format. 1455 * Gets the request format.
1453 * 1456 *
1455 * 1458 *
1456 * * format defined by the user (with setRequestFormat()) 1459 * * format defined by the user (with setRequestFormat())
1457 * * _format request attribute 1460 * * _format request attribute
1458 * * $default 1461 * * $default
1459 * 1462 *
1460 * @param string $default The default format 1463 * @param string|null $default The default format
1461 * 1464 *
1462 * @return string The request format 1465 * @return string The request format
1463 */ 1466 */
1464 public function getRequestFormat($default = 'html') 1467 public function getRequestFormat($default = 'html')
1465 { 1468 {
1555 * 1558 *
1556 * @return bool 1559 * @return bool
1557 */ 1560 */
1558 public function isMethodSafe(/* $andCacheable = true */) 1561 public function isMethodSafe(/* $andCacheable = true */)
1559 { 1562 {
1560 if (!func_num_args() || func_get_arg(0)) { 1563 if (!\func_num_args() || func_get_arg(0)) {
1561 // This deprecation should be turned into a BadMethodCallException in 4.0 (without adding the argument in the signature) 1564 // This deprecation should be turned into a BadMethodCallException in 4.0 (without adding the argument in the signature)
1562 // then setting $andCacheable to false should be deprecated in 4.1 1565 // then setting $andCacheable to false should be deprecated in 4.1
1563 @trigger_error('Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is deprecated since Symfony 3.2 and will throw an exception in 4.0. Disable checking only for cacheable methods by calling the method with `false` as first argument or use the Request::isMethodCacheable() instead.', E_USER_DEPRECATED); 1566 @trigger_error('Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is deprecated since Symfony 3.2 and will throw an exception in 4.0. Disable checking only for cacheable methods by calling the method with `false` as first argument or use the Request::isMethodCacheable() instead.', E_USER_DEPRECATED);
1564 1567
1565 return in_array($this->getMethod(), array('GET', 'HEAD')); 1568 return \in_array($this->getMethod(), ['GET', 'HEAD']);
1566 } 1569 }
1567 1570
1568 return in_array($this->getMethod(), array('GET', 'HEAD', 'OPTIONS', 'TRACE')); 1571 return \in_array($this->getMethod(), ['GET', 'HEAD', 'OPTIONS', 'TRACE']);
1569 } 1572 }
1570 1573
1571 /** 1574 /**
1572 * Checks whether or not the method is idempotent. 1575 * Checks whether or not the method is idempotent.
1573 * 1576 *
1574 * @return bool 1577 * @return bool
1575 */ 1578 */
1576 public function isMethodIdempotent() 1579 public function isMethodIdempotent()
1577 { 1580 {
1578 return in_array($this->getMethod(), array('HEAD', 'GET', 'PUT', 'DELETE', 'TRACE', 'OPTIONS', 'PURGE')); 1581 return \in_array($this->getMethod(), ['HEAD', 'GET', 'PUT', 'DELETE', 'TRACE', 'OPTIONS', 'PURGE']);
1579 } 1582 }
1580 1583
1581 /** 1584 /**
1582 * Checks whether the method is cacheable or not. 1585 * Checks whether the method is cacheable or not.
1583 * 1586 *
1584 * @see https://tools.ietf.org/html/rfc7231#section-4.2.3 1587 * @see https://tools.ietf.org/html/rfc7231#section-4.2.3
1585 * 1588 *
1586 * @return bool 1589 * @return bool True for GET and HEAD, false otherwise
1587 */ 1590 */
1588 public function isMethodCacheable() 1591 public function isMethodCacheable()
1589 { 1592 {
1590 return in_array($this->getMethod(), array('GET', 'HEAD')); 1593 return \in_array($this->getMethod(), ['GET', 'HEAD']);
1591 } 1594 }
1592 1595
1593 /** 1596 /**
1594 * Returns the protocol version. 1597 * Returns the protocol version.
1595 * 1598 *
1623 * 1626 *
1624 * @throws \LogicException 1627 * @throws \LogicException
1625 */ 1628 */
1626 public function getContent($asResource = false) 1629 public function getContent($asResource = false)
1627 { 1630 {
1628 $currentContentIsResource = is_resource($this->content); 1631 $currentContentIsResource = \is_resource($this->content);
1629 if (\PHP_VERSION_ID < 50600 && false === $this->content) { 1632 if (\PHP_VERSION_ID < 50600 && false === $this->content) {
1630 throw new \LogicException('getContent() can only be called once when using the resource return type and PHP below 5.6.'); 1633 throw new \LogicException('getContent() can only be called once when using the resource return type and PHP below 5.6.');
1631 } 1634 }
1632 1635
1633 if (true === $asResource) { 1636 if (true === $asResource) {
1636 1639
1637 return $this->content; 1640 return $this->content;
1638 } 1641 }
1639 1642
1640 // Content passed in parameter (test) 1643 // Content passed in parameter (test)
1641 if (is_string($this->content)) { 1644 if (\is_string($this->content)) {
1642 $resource = fopen('php://temp', 'r+'); 1645 $resource = fopen('php://temp', 'r+');
1643 fwrite($resource, $this->content); 1646 fwrite($resource, $this->content);
1644 rewind($resource); 1647 rewind($resource);
1645 1648
1646 return $resource; 1649 return $resource;
1699 1702
1700 if (!$preferredLanguages) { 1703 if (!$preferredLanguages) {
1701 return $locales[0]; 1704 return $locales[0];
1702 } 1705 }
1703 1706
1704 $extendedPreferredLanguages = array(); 1707 $extendedPreferredLanguages = [];
1705 foreach ($preferredLanguages as $language) { 1708 foreach ($preferredLanguages as $language) {
1706 $extendedPreferredLanguages[] = $language; 1709 $extendedPreferredLanguages[] = $language;
1707 if (false !== $position = strpos($language, '_')) { 1710 if (false !== $position = strpos($language, '_')) {
1708 $superLanguage = substr($language, 0, $position); 1711 $superLanguage = substr($language, 0, $position);
1709 if (!in_array($superLanguage, $preferredLanguages)) { 1712 if (!\in_array($superLanguage, $preferredLanguages)) {
1710 $extendedPreferredLanguages[] = $superLanguage; 1713 $extendedPreferredLanguages[] = $superLanguage;
1711 } 1714 }
1712 } 1715 }
1713 } 1716 }
1714 1717
1727 if (null !== $this->languages) { 1730 if (null !== $this->languages) {
1728 return $this->languages; 1731 return $this->languages;
1729 } 1732 }
1730 1733
1731 $languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all(); 1734 $languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all();
1732 $this->languages = array(); 1735 $this->languages = [];
1733 foreach ($languages as $lang => $acceptHeaderItem) { 1736 foreach ($languages as $lang => $acceptHeaderItem) {
1734 if (false !== strpos($lang, '-')) { 1737 if (false !== strpos($lang, '-')) {
1735 $codes = explode('-', $lang); 1738 $codes = explode('-', $lang);
1736 if ('i' === $codes[0]) { 1739 if ('i' === $codes[0]) {
1737 // Language not listed in ISO 639 that are not variants 1740 // Language not listed in ISO 639 that are not variants
1738 // of any listed language, which can be registered with the 1741 // of any listed language, which can be registered with the
1739 // i-prefix, such as i-cherokee 1742 // i-prefix, such as i-cherokee
1740 if (count($codes) > 1) { 1743 if (\count($codes) > 1) {
1741 $lang = $codes[1]; 1744 $lang = $codes[1];
1742 } 1745 }
1743 } else { 1746 } else {
1744 for ($i = 0, $max = count($codes); $i < $max; ++$i) { 1747 for ($i = 0, $max = \count($codes); $i < $max; ++$i) {
1745 if (0 === $i) { 1748 if (0 === $i) {
1746 $lang = strtolower($codes[0]); 1749 $lang = strtolower($codes[0]);
1747 } else { 1750 } else {
1748 $lang .= '_'.strtoupper($codes[$i]); 1751 $lang .= '_'.strtoupper($codes[$i]);
1749 } 1752 }
1824 1827
1825 protected function prepareRequestUri() 1828 protected function prepareRequestUri()
1826 { 1829 {
1827 $requestUri = ''; 1830 $requestUri = '';
1828 1831
1829 if ($this->headers->has('X_ORIGINAL_URL')) { 1832 if ('1' == $this->server->get('IIS_WasUrlRewritten') && '' != $this->server->get('UNENCODED_URL')) {
1830 // IIS with Microsoft Rewrite Module
1831 $requestUri = $this->headers->get('X_ORIGINAL_URL');
1832 $this->headers->remove('X_ORIGINAL_URL');
1833 $this->server->remove('HTTP_X_ORIGINAL_URL');
1834 $this->server->remove('UNENCODED_URL');
1835 $this->server->remove('IIS_WasUrlRewritten');
1836 } elseif ($this->headers->has('X_REWRITE_URL')) {
1837 // IIS with ISAPI_Rewrite
1838 $requestUri = $this->headers->get('X_REWRITE_URL');
1839 $this->headers->remove('X_REWRITE_URL');
1840 } elseif ('1' == $this->server->get('IIS_WasUrlRewritten') && '' != $this->server->get('UNENCODED_URL')) {
1841 // IIS7 with URL Rewrite: make sure we get the unencoded URL (double slash problem) 1833 // IIS7 with URL Rewrite: make sure we get the unencoded URL (double slash problem)
1842 $requestUri = $this->server->get('UNENCODED_URL'); 1834 $requestUri = $this->server->get('UNENCODED_URL');
1843 $this->server->remove('UNENCODED_URL'); 1835 $this->server->remove('UNENCODED_URL');
1844 $this->server->remove('IIS_WasUrlRewritten'); 1836 $this->server->remove('IIS_WasUrlRewritten');
1845 } elseif ($this->server->has('REQUEST_URI')) { 1837 } elseif ($this->server->has('REQUEST_URI')) {
1846 $requestUri = $this->server->get('REQUEST_URI'); 1838 $requestUri = $this->server->get('REQUEST_URI');
1847 // HTTP proxy reqs setup request URI with scheme and host [and port] + the URL path, only use URL path 1839
1848 $schemeAndHttpHost = $this->getSchemeAndHttpHost(); 1840 if ('' !== $requestUri && '/' === $requestUri[0]) {
1849 if (0 === strpos($requestUri, $schemeAndHttpHost)) { 1841 // To only use path and query remove the fragment.
1850 $requestUri = substr($requestUri, strlen($schemeAndHttpHost)); 1842 if (false !== $pos = strpos($requestUri, '#')) {
1843 $requestUri = substr($requestUri, 0, $pos);
1844 }
1845 } else {
1846 // HTTP proxy reqs setup request URI with scheme and host [and port] + the URL path,
1847 // only use URL path.
1848 $uriComponents = parse_url($requestUri);
1849
1850 if (isset($uriComponents['path'])) {
1851 $requestUri = $uriComponents['path'];
1852 }
1853
1854 if (isset($uriComponents['query'])) {
1855 $requestUri .= '?'.$uriComponents['query'];
1856 }
1851 } 1857 }
1852 } elseif ($this->server->has('ORIG_PATH_INFO')) { 1858 } elseif ($this->server->has('ORIG_PATH_INFO')) {
1853 // IIS 5.0, PHP as CGI 1859 // IIS 5.0, PHP as CGI
1854 $requestUri = $this->server->get('ORIG_PATH_INFO'); 1860 $requestUri = $this->server->get('ORIG_PATH_INFO');
1855 if ('' != $this->server->get('QUERY_STRING')) { 1861 if ('' != $this->server->get('QUERY_STRING')) {
1885 $path = $this->server->get('PHP_SELF', ''); 1891 $path = $this->server->get('PHP_SELF', '');
1886 $file = $this->server->get('SCRIPT_FILENAME', ''); 1892 $file = $this->server->get('SCRIPT_FILENAME', '');
1887 $segs = explode('/', trim($file, '/')); 1893 $segs = explode('/', trim($file, '/'));
1888 $segs = array_reverse($segs); 1894 $segs = array_reverse($segs);
1889 $index = 0; 1895 $index = 0;
1890 $last = count($segs); 1896 $last = \count($segs);
1891 $baseUrl = ''; 1897 $baseUrl = '';
1892 do { 1898 do {
1893 $seg = $segs[$index]; 1899 $seg = $segs[$index];
1894 $baseUrl = '/'.$seg.$baseUrl; 1900 $baseUrl = '/'.$seg.$baseUrl;
1895 ++$index; 1901 ++$index;
1905 if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) { 1911 if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) {
1906 // full $baseUrl matches 1912 // full $baseUrl matches
1907 return $prefix; 1913 return $prefix;
1908 } 1914 }
1909 1915
1910 if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, rtrim(dirname($baseUrl), '/'.DIRECTORY_SEPARATOR).'/')) { 1916 if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, rtrim(\dirname($baseUrl), '/'.\DIRECTORY_SEPARATOR).'/')) {
1911 // directory portion of $baseUrl matches 1917 // directory portion of $baseUrl matches
1912 return rtrim($prefix, '/'.DIRECTORY_SEPARATOR); 1918 return rtrim($prefix, '/'.\DIRECTORY_SEPARATOR);
1913 } 1919 }
1914 1920
1915 $truncatedRequestUri = $requestUri; 1921 $truncatedRequestUri = $requestUri;
1916 if (false !== $pos = strpos($requestUri, '?')) { 1922 if (false !== $pos = strpos($requestUri, '?')) {
1917 $truncatedRequestUri = substr($requestUri, 0, $pos); 1923 $truncatedRequestUri = substr($requestUri, 0, $pos);
1924 } 1930 }
1925 1931
1926 // If using mod_rewrite or ISAPI_Rewrite strip the script filename 1932 // If using mod_rewrite or ISAPI_Rewrite strip the script filename
1927 // out of baseUrl. $pos !== 0 makes sure it is not matching a value 1933 // out of baseUrl. $pos !== 0 makes sure it is not matching a value
1928 // from PATH_INFO or QUERY_STRING 1934 // from PATH_INFO or QUERY_STRING
1929 if (strlen($requestUri) >= strlen($baseUrl) && (false !== $pos = strpos($requestUri, $baseUrl)) && 0 !== $pos) { 1935 if (\strlen($requestUri) >= \strlen($baseUrl) && (false !== $pos = strpos($requestUri, $baseUrl)) && 0 !== $pos) {
1930 $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl)); 1936 $baseUrl = substr($requestUri, 0, $pos + \strlen($baseUrl));
1931 } 1937 }
1932 1938
1933 return rtrim($baseUrl, '/'.DIRECTORY_SEPARATOR); 1939 return rtrim($baseUrl, '/'.\DIRECTORY_SEPARATOR);
1934 } 1940 }
1935 1941
1936 /** 1942 /**
1937 * Prepares the base path. 1943 * Prepares the base path.
1938 * 1944 *
1945 return ''; 1951 return '';
1946 } 1952 }
1947 1953
1948 $filename = basename($this->server->get('SCRIPT_FILENAME')); 1954 $filename = basename($this->server->get('SCRIPT_FILENAME'));
1949 if (basename($baseUrl) === $filename) { 1955 if (basename($baseUrl) === $filename) {
1950 $basePath = dirname($baseUrl); 1956 $basePath = \dirname($baseUrl);
1951 } else { 1957 } else {
1952 $basePath = $baseUrl; 1958 $basePath = $baseUrl;
1953 } 1959 }
1954 1960
1955 if ('\\' === DIRECTORY_SEPARATOR) { 1961 if ('\\' === \DIRECTORY_SEPARATOR) {
1956 $basePath = str_replace('\\', '/', $basePath); 1962 $basePath = str_replace('\\', '/', $basePath);
1957 } 1963 }
1958 1964
1959 return rtrim($basePath, '/'); 1965 return rtrim($basePath, '/');
1960 } 1966 }
1980 1986
1981 if (null === ($baseUrl = $this->getBaseUrl())) { 1987 if (null === ($baseUrl = $this->getBaseUrl())) {
1982 return $requestUri; 1988 return $requestUri;
1983 } 1989 }
1984 1990
1985 $pathInfo = substr($requestUri, strlen($baseUrl)); 1991 $pathInfo = substr($requestUri, \strlen($baseUrl));
1986 if (false === $pathInfo || '' === $pathInfo) { 1992 if (false === $pathInfo || '' === $pathInfo) {
1987 // If substr() returns false then PATH_INFO is set to an empty string 1993 // If substr() returns false then PATH_INFO is set to an empty string
1988 return '/'; 1994 return '/';
1989 } 1995 }
1990 1996
1994 /** 2000 /**
1995 * Initializes HTTP request formats. 2001 * Initializes HTTP request formats.
1996 */ 2002 */
1997 protected static function initializeFormats() 2003 protected static function initializeFormats()
1998 { 2004 {
1999 static::$formats = array( 2005 static::$formats = [
2000 'html' => array('text/html', 'application/xhtml+xml'), 2006 'html' => ['text/html', 'application/xhtml+xml'],
2001 'txt' => array('text/plain'), 2007 'txt' => ['text/plain'],
2002 'js' => array('application/javascript', 'application/x-javascript', 'text/javascript'), 2008 'js' => ['application/javascript', 'application/x-javascript', 'text/javascript'],
2003 'css' => array('text/css'), 2009 'css' => ['text/css'],
2004 'json' => array('application/json', 'application/x-json'), 2010 'json' => ['application/json', 'application/x-json'],
2005 'jsonld' => array('application/ld+json'), 2011 'jsonld' => ['application/ld+json'],
2006 'xml' => array('text/xml', 'application/xml', 'application/x-xml'), 2012 'xml' => ['text/xml', 'application/xml', 'application/x-xml'],
2007 'rdf' => array('application/rdf+xml'), 2013 'rdf' => ['application/rdf+xml'],
2008 'atom' => array('application/atom+xml'), 2014 'atom' => ['application/atom+xml'],
2009 'rss' => array('application/rss+xml'), 2015 'rss' => ['application/rss+xml'],
2010 'form' => array('application/x-www-form-urlencoded'), 2016 'form' => ['application/x-www-form-urlencoded'],
2011 ); 2017 ];
2012 } 2018 }
2013 2019
2014 /** 2020 /**
2015 * Sets the default PHP locale. 2021 * Sets the default PHP locale.
2016 * 2022 *
2042 { 2048 {
2043 if (0 !== strpos(rawurldecode($string), $prefix)) { 2049 if (0 !== strpos(rawurldecode($string), $prefix)) {
2044 return false; 2050 return false;
2045 } 2051 }
2046 2052
2047 $len = strlen($prefix); 2053 $len = \strlen($prefix);
2048 2054
2049 if (preg_match(sprintf('#^(%%[[:xdigit:]]{2}|.){%d}#', $len), $string, $match)) { 2055 if (preg_match(sprintf('#^(%%[[:xdigit:]]{2}|.){%d}#', $len), $string, $match)) {
2050 return $match[0]; 2056 return $match[0];
2051 } 2057 }
2052 2058
2053 return false; 2059 return false;
2054 } 2060 }
2055 2061
2056 private static function createRequestFromFactory(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) 2062 private static function createRequestFromFactory(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
2057 { 2063 {
2058 if (self::$requestFactory) { 2064 if (self::$requestFactory) {
2059 $request = call_user_func(self::$requestFactory, $query, $request, $attributes, $cookies, $files, $server, $content); 2065 $request = \call_user_func(self::$requestFactory, $query, $request, $attributes, $cookies, $files, $server, $content);
2060 2066
2061 if (!$request instanceof self) { 2067 if (!$request instanceof self) {
2062 throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.'); 2068 throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.');
2063 } 2069 }
2064 2070
2081 return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR'), self::$trustedProxies); 2087 return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR'), self::$trustedProxies);
2082 } 2088 }
2083 2089
2084 private function getTrustedValues($type, $ip = null) 2090 private function getTrustedValues($type, $ip = null)
2085 { 2091 {
2086 $clientValues = array(); 2092 $clientValues = [];
2087 $forwardedValues = array(); 2093 $forwardedValues = [];
2088 2094
2089 if (self::$trustedHeaders[$type] && $this->headers->has(self::$trustedHeaders[$type])) { 2095 if (self::$trustedHeaders[$type] && $this->headers->has(self::$trustedHeaders[$type])) {
2090 foreach (explode(',', $this->headers->get(self::$trustedHeaders[$type])) as $v) { 2096 foreach (explode(',', $this->headers->get(self::$trustedHeaders[$type])) as $v) {
2091 $clientValues[] = (self::HEADER_CLIENT_PORT === $type ? '0.0.0.0:' : '').trim($v); 2097 $clientValues[] = (self::HEADER_CLIENT_PORT === $type ? '0.0.0.0:' : '').trim($v);
2092 } 2098 }
2093 } 2099 }
2094 2100
2095 if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) { 2101 if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) {
2096 $forwardedValues = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]); 2102 $forwardedValues = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
2097 $forwardedValues = preg_match_all(sprintf('{(?:%s)=(?:"?\[?)([a-zA-Z0-9\.:_\-/]*+)}', self::$forwardedParams[$type]), $forwardedValues, $matches) ? $matches[1] : array(); 2103 $forwardedValues = preg_match_all(sprintf('{(?:%s)="?([a-zA-Z0-9\.:_\-/\[\]]*+)}', self::$forwardedParams[$type]), $forwardedValues, $matches) ? $matches[1] : [];
2104 if (self::HEADER_CLIENT_PORT === $type) {
2105 foreach ($forwardedValues as $k => $v) {
2106 if (']' === substr($v, -1) || false === $v = strrchr($v, ':')) {
2107 $v = $this->isSecure() ? ':443' : ':80';
2108 }
2109 $forwardedValues[$k] = '0.0.0.0'.$v;
2110 }
2111 }
2098 } 2112 }
2099 2113
2100 if (null !== $ip) { 2114 if (null !== $ip) {
2101 $clientValues = $this->normalizeAndFilterClientIps($clientValues, $ip); 2115 $clientValues = $this->normalizeAndFilterClientIps($clientValues, $ip);
2102 $forwardedValues = $this->normalizeAndFilterClientIps($forwardedValues, $ip); 2116 $forwardedValues = $this->normalizeAndFilterClientIps($forwardedValues, $ip);
2109 if (!$forwardedValues) { 2123 if (!$forwardedValues) {
2110 return $clientValues; 2124 return $clientValues;
2111 } 2125 }
2112 2126
2113 if (!$this->isForwardedValid) { 2127 if (!$this->isForwardedValid) {
2114 return null !== $ip ? array('0.0.0.0', $ip) : array(); 2128 return null !== $ip ? ['0.0.0.0', $ip] : [];
2115 } 2129 }
2116 $this->isForwardedValid = false; 2130 $this->isForwardedValid = false;
2117 2131
2118 throw new ConflictingHeadersException(sprintf('The request has both a trusted "%s" header and a trusted "%s" header, conflicting with each other. You should either configure your proxy to remove one of them, or configure your project to distrust the offending one.', self::$trustedHeaders[self::HEADER_FORWARDED], self::$trustedHeaders[$type])); 2132 throw new ConflictingHeadersException(sprintf('The request has both a trusted "%s" header and a trusted "%s" header, conflicting with each other. You should either configure your proxy to remove one of them, or configure your project to distrust the offending one.', self::$trustedHeaders[self::HEADER_FORWARDED], self::$trustedHeaders[$type]));
2119 } 2133 }
2120 2134
2121 private function normalizeAndFilterClientIps(array $clientIps, $ip) 2135 private function normalizeAndFilterClientIps(array $clientIps, $ip)
2122 { 2136 {
2123 if (!$clientIps) { 2137 if (!$clientIps) {
2124 return array(); 2138 return [];
2125 } 2139 }
2126 $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from 2140 $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
2127 $firstTrustedIp = null; 2141 $firstTrustedIp = null;
2128 2142
2129 foreach ($clientIps as $key => $clientIp) { 2143 foreach ($clientIps as $key => $clientIp) {
2130 // Remove port (unfortunately, it does happen) 2144 if (strpos($clientIp, '.')) {
2131 if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) { 2145 // Strip :port from IPv4 addresses. This is allowed in Forwarded
2132 $clientIps[$key] = $clientIp = $match[1]; 2146 // and may occur in X-Forwarded-For.
2147 $i = strpos($clientIp, ':');
2148 if ($i) {
2149 $clientIps[$key] = $clientIp = substr($clientIp, 0, $i);
2150 }
2151 } elseif (0 === strpos($clientIp, '[')) {
2152 // Strip brackets and :port from IPv6 addresses.
2153 $i = strpos($clientIp, ']', 1);
2154 $clientIps[$key] = $clientIp = substr($clientIp, 1, $i - 1);
2133 } 2155 }
2134 2156
2135 if (!filter_var($clientIp, FILTER_VALIDATE_IP)) { 2157 if (!filter_var($clientIp, FILTER_VALIDATE_IP)) {
2136 unset($clientIps[$key]); 2158 unset($clientIps[$key]);
2137 2159
2147 } 2169 }
2148 } 2170 }
2149 } 2171 }
2150 2172
2151 // Now the IP chain contains only untrusted proxies and the client IP 2173 // Now the IP chain contains only untrusted proxies and the client IP
2152 return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp); 2174 return $clientIps ? array_reverse($clientIps) : [$firstTrustedIp];
2153 } 2175 }
2154 } 2176 }