comparison vendor/symfony/http-foundation/Request.php @ 14:1fec387a4317

Update Drupal core to 8.5.2 via Composer
author Chris Cannam
date Mon, 23 Apr 2018 09:46:53 +0100
parents 7a779792577d
children c2387f117808
comparison
equal deleted inserted replaced
13:5fb285c0d0e3 14:1fec387a4317
10 */ 10 */
11 11
12 namespace Symfony\Component\HttpFoundation; 12 namespace Symfony\Component\HttpFoundation;
13 13
14 use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException; 14 use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException;
15 use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
15 use Symfony\Component\HttpFoundation\Session\SessionInterface; 16 use Symfony\Component\HttpFoundation\Session\SessionInterface;
16 17
17 /** 18 /**
18 * Request represents an HTTP request. 19 * Request represents an HTTP request.
19 * 20 *
27 * 28 *
28 * @author Fabien Potencier <fabien@symfony.com> 29 * @author Fabien Potencier <fabien@symfony.com>
29 */ 30 */
30 class Request 31 class Request
31 { 32 {
32 const HEADER_FORWARDED = 'forwarded'; 33 const HEADER_FORWARDED = 0b00001; // When using RFC 7239
33 const HEADER_CLIENT_IP = 'client_ip'; 34 const HEADER_X_FORWARDED_FOR = 0b00010;
34 const HEADER_CLIENT_HOST = 'client_host'; 35 const HEADER_X_FORWARDED_HOST = 0b00100;
35 const HEADER_CLIENT_PROTO = 'client_proto'; 36 const HEADER_X_FORWARDED_PROTO = 0b01000;
36 const HEADER_CLIENT_PORT = 'client_port'; 37 const HEADER_X_FORWARDED_PORT = 0b10000;
38 const HEADER_X_FORWARDED_ALL = 0b11110; // All "X-Forwarded-*" headers
39 const HEADER_X_FORWARDED_AWS_ELB = 0b11010; // AWS ELB doesn't send X-Forwarded-Host
40
41 /** @deprecated since version 3.3, to be removed in 4.0 */
42 const HEADER_CLIENT_IP = self::HEADER_X_FORWARDED_FOR;
43 /** @deprecated since version 3.3, to be removed in 4.0 */
44 const HEADER_CLIENT_HOST = self::HEADER_X_FORWARDED_HOST;
45 /** @deprecated since version 3.3, to be removed in 4.0 */
46 const HEADER_CLIENT_PROTO = self::HEADER_X_FORWARDED_PROTO;
47 /** @deprecated since version 3.3, to be removed in 4.0 */
48 const HEADER_CLIENT_PORT = self::HEADER_X_FORWARDED_PORT;
37 49
38 const METHOD_HEAD = 'HEAD'; 50 const METHOD_HEAD = 'HEAD';
39 const METHOD_GET = 'GET'; 51 const METHOD_GET = 'GET';
40 const METHOD_POST = 'POST'; 52 const METHOD_POST = 'POST';
41 const METHOD_PUT = 'PUT'; 53 const METHOD_PUT = 'PUT';
67 * 79 *
68 * The FORWARDED header is the standard as of rfc7239. 80 * The FORWARDED header is the standard as of rfc7239.
69 * 81 *
70 * The other headers are non-standard, but widely used 82 * The other headers are non-standard, but widely used
71 * by popular reverse proxies (like Apache mod_proxy or Amazon EC2). 83 * by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
84 *
85 * @deprecated since version 3.3, to be removed in 4.0
72 */ 86 */
73 protected static $trustedHeaders = array( 87 protected static $trustedHeaders = array(
74 self::HEADER_FORWARDED => 'FORWARDED', 88 self::HEADER_FORWARDED => 'FORWARDED',
75 self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR', 89 self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
76 self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST', 90 self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
128 * @var \Symfony\Component\HttpFoundation\HeaderBag 142 * @var \Symfony\Component\HttpFoundation\HeaderBag
129 */ 143 */
130 public $headers; 144 public $headers;
131 145
132 /** 146 /**
147 * @var string|resource|false|null
148 */
149 protected $content;
150
151 /**
152 * @var array
153 */
154 protected $languages;
155
156 /**
157 * @var array
158 */
159 protected $charsets;
160
161 /**
162 * @var array
163 */
164 protected $encodings;
165
166 /**
167 * @var array
168 */
169 protected $acceptableContentTypes;
170
171 /**
133 * @var string 172 * @var string
134 */ 173 */
135 protected $content; 174 protected $pathInfo;
175
176 /**
177 * @var string
178 */
179 protected $requestUri;
180
181 /**
182 * @var string
183 */
184 protected $baseUrl;
185
186 /**
187 * @var string
188 */
189 protected $basePath;
190
191 /**
192 * @var string
193 */
194 protected $method;
195
196 /**
197 * @var string
198 */
199 protected $format;
200
201 /**
202 * @var \Symfony\Component\HttpFoundation\Session\SessionInterface
203 */
204 protected $session;
205
206 /**
207 * @var string
208 */
209 protected $locale;
210
211 /**
212 * @var string
213 */
214 protected $defaultLocale = 'en';
136 215
137 /** 216 /**
138 * @var array 217 * @var array
139 */ 218 */
140 protected $languages;
141
142 /**
143 * @var array
144 */
145 protected $charsets;
146
147 /**
148 * @var array
149 */
150 protected $encodings;
151
152 /**
153 * @var array
154 */
155 protected $acceptableContentTypes;
156
157 /**
158 * @var string
159 */
160 protected $pathInfo;
161
162 /**
163 * @var string
164 */
165 protected $requestUri;
166
167 /**
168 * @var string
169 */
170 protected $baseUrl;
171
172 /**
173 * @var string
174 */
175 protected $basePath;
176
177 /**
178 * @var string
179 */
180 protected $method;
181
182 /**
183 * @var string
184 */
185 protected $format;
186
187 /**
188 * @var \Symfony\Component\HttpFoundation\Session\SessionInterface
189 */
190 protected $session;
191
192 /**
193 * @var string
194 */
195 protected $locale;
196
197 /**
198 * @var string
199 */
200 protected $defaultLocale = 'en';
201
202 /**
203 * @var array
204 */
205 protected static $formats; 219 protected static $formats;
206 220
207 protected static $requestFactory; 221 protected static $requestFactory;
208 222
223 private $isHostValid = true;
209 private $isForwardedValid = true; 224 private $isForwardedValid = true;
210 225
226 private static $trustedHeaderSet = -1;
227
228 /** @deprecated since version 3.3, to be removed in 4.0 */
229 private static $trustedHeaderNames = array(
230 self::HEADER_FORWARDED => 'FORWARDED',
231 self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
232 self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
233 self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
234 self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
235 );
236
211 private static $forwardedParams = array( 237 private static $forwardedParams = array(
212 self::HEADER_CLIENT_IP => 'for', 238 self::HEADER_X_FORWARDED_FOR => 'for',
213 self::HEADER_CLIENT_HOST => 'host', 239 self::HEADER_X_FORWARDED_HOST => 'host',
214 self::HEADER_CLIENT_PROTO => 'proto', 240 self::HEADER_X_FORWARDED_PROTO => 'proto',
215 self::HEADER_CLIENT_PORT => 'host', 241 self::HEADER_X_FORWARDED_PORT => 'host',
216 ); 242 );
217 243
218 /** 244 /**
219 * Constructor. 245 * @param array $query The GET parameters
220 * 246 * @param array $request The POST parameters
221 * @param array $query The GET parameters 247 * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
222 * @param array $request The POST parameters 248 * @param array $cookies The COOKIE parameters
223 * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) 249 * @param array $files The FILES parameters
224 * @param array $cookies The COOKIE parameters 250 * @param array $server The SERVER parameters
225 * @param array $files The FILES parameters 251 * @param string|resource|null $content The raw body data
226 * @param array $server The SERVER parameters
227 * @param string|resource $content The raw body data
228 */ 252 */
229 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(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
230 { 254 {
231 $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content); 255 $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content);
232 } 256 }
234 /** 258 /**
235 * Sets the parameters for this request. 259 * Sets the parameters for this request.
236 * 260 *
237 * This method also re-initializes all properties. 261 * This method also re-initializes all properties.
238 * 262 *
239 * @param array $query The GET parameters 263 * @param array $query The GET parameters
240 * @param array $request The POST parameters 264 * @param array $request The POST parameters
241 * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) 265 * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
242 * @param array $cookies The COOKIE parameters 266 * @param array $cookies The COOKIE parameters
243 * @param array $files The FILES parameters 267 * @param array $files The FILES parameters
244 * @param array $server The SERVER parameters 268 * @param array $server The SERVER parameters
245 * @param string|resource $content The raw body data 269 * @param string|resource|null $content The raw body data
246 */ 270 */
247 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(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
248 { 272 {
249 $this->request = new ParameterBag($request); 273 $this->request = new ParameterBag($request);
250 $this->query = new ParameterBag($query); 274 $this->query = new ParameterBag($query);
303 * Creates a Request based on a given URI and configuration. 327 * Creates a Request based on a given URI and configuration.
304 * 328 *
305 * The information contained in the URI always take precedence 329 * The information contained in the URI always take precedence
306 * over the other information (server and parameters). 330 * over the other information (server and parameters).
307 * 331 *
308 * @param string $uri The URI 332 * @param string $uri The URI
309 * @param string $method The HTTP method 333 * @param string $method The HTTP method
310 * @param array $parameters The query (GET) or request (POST) parameters 334 * @param array $parameters The query (GET) or request (POST) parameters
311 * @param array $cookies The request cookies ($_COOKIE) 335 * @param array $cookies The request cookies ($_COOKIE)
312 * @param array $files The request files ($_FILES) 336 * @param array $files The request files ($_FILES)
313 * @param array $server The server parameters ($_SERVER) 337 * @param array $server The server parameters ($_SERVER)
314 * @param string $content The raw body data 338 * @param string|resource|null $content The raw body data
315 * 339 *
316 * @return static 340 * @return static
317 */ 341 */
318 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 = array(), $cookies = array(), $files = array(), $server = array(), $content = null)
319 { 343 {
434 * @return static 458 * @return static
435 */ 459 */
436 public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null) 460 public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null)
437 { 461 {
438 $dup = clone $this; 462 $dup = clone $this;
439 if ($query !== null) { 463 if (null !== $query) {
440 $dup->query = new ParameterBag($query); 464 $dup->query = new ParameterBag($query);
441 } 465 }
442 if ($request !== null) { 466 if (null !== $request) {
443 $dup->request = new ParameterBag($request); 467 $dup->request = new ParameterBag($request);
444 } 468 }
445 if ($attributes !== null) { 469 if (null !== $attributes) {
446 $dup->attributes = new ParameterBag($attributes); 470 $dup->attributes = new ParameterBag($attributes);
447 } 471 }
448 if ($cookies !== null) { 472 if (null !== $cookies) {
449 $dup->cookies = new ParameterBag($cookies); 473 $dup->cookies = new ParameterBag($cookies);
450 } 474 }
451 if ($files !== null) { 475 if (null !== $files) {
452 $dup->files = new FileBag($files); 476 $dup->files = new FileBag($files);
453 } 477 }
454 if ($server !== null) { 478 if (null !== $server) {
455 $dup->server = new ServerBag($server); 479 $dup->server = new ServerBag($server);
456 $dup->headers = new HeaderBag($dup->server->getHeaders()); 480 $dup->headers = new HeaderBag($dup->server->getHeaders());
457 } 481 }
458 $dup->languages = null; 482 $dup->languages = null;
459 $dup->charsets = null; 483 $dup->charsets = null;
505 $content = $this->getContent(); 529 $content = $this->getContent();
506 } catch (\LogicException $e) { 530 } catch (\LogicException $e) {
507 return trigger_error($e, E_USER_ERROR); 531 return trigger_error($e, E_USER_ERROR);
508 } 532 }
509 533
534 $cookieHeader = '';
535 $cookies = array();
536
537 foreach ($this->cookies as $k => $v) {
538 $cookies[] = $k.'='.$v;
539 }
540
541 if (!empty($cookies)) {
542 $cookieHeader = 'Cookie: '.implode('; ', $cookies)."\r\n";
543 }
544
510 return 545 return
511 sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n". 546 sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n".
512 $this->headers."\r\n". 547 $this->headers.
548 $cookieHeader."\r\n".
513 $content; 549 $content;
514 } 550 }
515 551
516 /** 552 /**
517 * Overrides the PHP global variables according to this request instance. 553 * Overrides the PHP global variables according to this request instance.
519 * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE. 555 * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE.
520 * $_FILES is never overridden, see rfc1867 556 * $_FILES is never overridden, see rfc1867
521 */ 557 */
522 public function overrideGlobals() 558 public function overrideGlobals()
523 { 559 {
524 $this->server->set('QUERY_STRING', static::normalizeQueryString(http_build_query($this->query->all(), null, '&'))); 560 $this->server->set('QUERY_STRING', static::normalizeQueryString(http_build_query($this->query->all(), '', '&')));
525 561
526 $_GET = $this->query->all(); 562 $_GET = $this->query->all();
527 $_POST = $this->request->all(); 563 $_POST = $this->request->all();
528 $_SERVER = $this->server->all(); 564 $_SERVER = $this->server->all();
529 $_COOKIE = $this->cookies->all(); 565 $_COOKIE = $this->cookies->all();
551 /** 587 /**
552 * Sets a list of trusted proxies. 588 * Sets a list of trusted proxies.
553 * 589 *
554 * You should only list the reverse proxies that you manage directly. 590 * You should only list the reverse proxies that you manage directly.
555 * 591 *
556 * @param array $proxies A list of trusted proxies 592 * @param array $proxies A list of trusted proxies
557 */ 593 * @param int $trustedHeaderSet A bit field of Request::HEADER_*, to set which headers to trust from your proxies
558 public static function setTrustedProxies(array $proxies) 594 *
595 * @throws \InvalidArgumentException When $trustedHeaderSet is invalid
596 */
597 public static function setTrustedProxies(array $proxies/*, int $trustedHeaderSet*/)
559 { 598 {
560 self::$trustedProxies = $proxies; 599 self::$trustedProxies = $proxies;
600
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);
603
604 return;
605 }
606 $trustedHeaderSet = (int) func_get_arg(1);
607
608 foreach (self::$trustedHeaderNames as $header => $name) {
609 self::$trustedHeaders[$header] = $header & $trustedHeaderSet ? $name : null;
610 }
611 self::$trustedHeaderSet = $trustedHeaderSet;
561 } 612 }
562 613
563 /** 614 /**
564 * Gets the list of trusted proxies. 615 * Gets the list of trusted proxies.
565 * 616 *
566 * @return array An array of trusted proxies 617 * @return array An array of trusted proxies
567 */ 618 */
568 public static function getTrustedProxies() 619 public static function getTrustedProxies()
569 { 620 {
570 return self::$trustedProxies; 621 return self::$trustedProxies;
622 }
623
624 /**
625 * Gets the set of trusted headers from trusted proxies.
626 *
627 * @return int A bit field of Request::HEADER_* that defines which headers are trusted from your proxies
628 */
629 public static function getTrustedHeaderSet()
630 {
631 return self::$trustedHeaderSet;
571 } 632 }
572 633
573 /** 634 /**
574 * Sets a list of trusted host patterns. 635 * Sets a list of trusted host patterns.
575 * 636 *
611 * 672 *
612 * @param string $key The header key 673 * @param string $key The header key
613 * @param string $value The header name 674 * @param string $value The header name
614 * 675 *
615 * @throws \InvalidArgumentException 676 * @throws \InvalidArgumentException
677 *
678 * @deprecated since version 3.3, to be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead.
616 */ 679 */
617 public static function setTrustedHeaderName($key, $value) 680 public static function setTrustedHeaderName($key, $value)
618 { 681 {
619 if (!array_key_exists($key, self::$trustedHeaders)) { 682 @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead.', __METHOD__), E_USER_DEPRECATED);
683
684 if ('forwarded' === $key) {
685 $key = self::HEADER_FORWARDED;
686 } elseif ('client_ip' === $key) {
687 $key = self::HEADER_CLIENT_IP;
688 } elseif ('client_host' === $key) {
689 $key = self::HEADER_CLIENT_HOST;
690 } elseif ('client_proto' === $key) {
691 $key = self::HEADER_CLIENT_PROTO;
692 } elseif ('client_port' === $key) {
693 $key = self::HEADER_CLIENT_PORT;
694 } elseif (!array_key_exists($key, self::$trustedHeaders)) {
620 throw new \InvalidArgumentException(sprintf('Unable to set the trusted header name for key "%s".', $key)); 695 throw new \InvalidArgumentException(sprintf('Unable to set the trusted header name for key "%s".', $key));
621 } 696 }
622 697
623 self::$trustedHeaders[$key] = $value; 698 self::$trustedHeaders[$key] = $value;
699
700 if (null !== $value) {
701 self::$trustedHeaderNames[$key] = $value;
702 self::$trustedHeaderSet |= $key;
703 } else {
704 self::$trustedHeaderSet &= ~$key;
705 }
624 } 706 }
625 707
626 /** 708 /**
627 * Gets the trusted proxy header name. 709 * Gets the trusted proxy header name.
628 * 710 *
629 * @param string $key The header key 711 * @param string $key The header key
630 * 712 *
631 * @return string The header name 713 * @return string The header name
632 * 714 *
633 * @throws \InvalidArgumentException 715 * @throws \InvalidArgumentException
716 *
717 * @deprecated since version 3.3, to be removed in 4.0. Use the Request::getTrustedHeaderSet() method instead.
634 */ 718 */
635 public static function getTrustedHeaderName($key) 719 public static function getTrustedHeaderName($key)
636 { 720 {
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);
723 }
724
637 if (!array_key_exists($key, self::$trustedHeaders)) { 725 if (!array_key_exists($key, self::$trustedHeaders)) {
638 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));
639 } 727 }
640 728
641 return self::$trustedHeaders[$key]; 729 return self::$trustedHeaders[$key];
717 * flexibility in controllers, it is better to explicitly get request parameters from the appropriate 805 * flexibility in controllers, it is better to explicitly get request parameters from the appropriate
718 * public property instead (attributes, query, request). 806 * public property instead (attributes, query, request).
719 * 807 *
720 * Order of precedence: PATH (routing placeholders or custom attributes), GET, BODY 808 * Order of precedence: PATH (routing placeholders or custom attributes), GET, BODY
721 * 809 *
722 * @param string $key the key 810 * @param string $key The key
723 * @param mixed $default the default value if the parameter key does not exist 811 * @param mixed $default The default value if the parameter key does not exist
724 * 812 *
725 * @return mixed 813 * @return mixed
726 */ 814 */
727 public function get($key, $default = null) 815 public function get($key, $default = null)
728 { 816 {
819 * header value is a comma+space separated list of IP addresses, the left-most 907 * header value is a comma+space separated list of IP addresses, the left-most
820 * being the original client, and each successive proxy that passed the request 908 * being the original client, and each successive proxy that passed the request
821 * adding the IP address where it received the request from. 909 * adding the IP address where it received the request from.
822 * 910 *
823 * If your reverse proxy uses a different header name than "X-Forwarded-For", 911 * If your reverse proxy uses a different header name than "X-Forwarded-For",
824 * ("Client-Ip" for instance), configure it via "setTrustedHeaderName()" with 912 * ("Client-Ip" for instance), configure it via the $trustedHeaderSet
825 * the "client-ip" key. 913 * argument of the Request::setTrustedProxies() method instead.
826 * 914 *
827 * @return string|null The client IP address 915 * @return string|null The client IP address
828 * 916 *
829 * @see getClientIps() 917 * @see getClientIps()
830 * @see http://en.wikipedia.org/wiki/X-Forwarded-For 918 * @see http://en.wikipedia.org/wiki/X-Forwarded-For
926 * when trusted proxies were set via "setTrustedProxies()". 1014 * when trusted proxies were set via "setTrustedProxies()".
927 * 1015 *
928 * The "X-Forwarded-Port" header must contain the client port. 1016 * The "X-Forwarded-Port" header must contain the client port.
929 * 1017 *
930 * If your reverse proxy uses a different header name than "X-Forwarded-Port", 1018 * If your reverse proxy uses a different header name than "X-Forwarded-Port",
931 * configure it via "setTrustedHeaderName()" with the "client-port" key. 1019 * configure it via via the $trustedHeaderSet argument of the
1020 * Request::setTrustedProxies() method instead.
932 * 1021 *
933 * @return int|string can be a string if fetched from the server bag 1022 * @return int|string can be a string if fetched from the server bag
934 */ 1023 */
935 public function getPort() 1024 public function getPort()
936 { 1025 {
940 $host = $host[0]; 1029 $host = $host[0];
941 } elseif (!$host = $this->headers->get('HOST')) { 1030 } elseif (!$host = $this->headers->get('HOST')) {
942 return $this->server->get('SERVER_PORT'); 1031 return $this->server->get('SERVER_PORT');
943 } 1032 }
944 1033
945 if ($host[0] === '[') { 1034 if ('[' === $host[0]) {
946 $pos = strpos($host, ':', strrpos($host, ']')); 1035 $pos = strpos($host, ':', strrpos($host, ']'));
947 } else { 1036 } else {
948 $pos = strrpos($host, ':'); 1037 $pos = strrpos($host, ':');
949 } 1038 }
950 1039
1002 public function getHttpHost() 1091 public function getHttpHost()
1003 { 1092 {
1004 $scheme = $this->getScheme(); 1093 $scheme = $this->getScheme();
1005 $port = $this->getPort(); 1094 $port = $this->getPort();
1006 1095
1007 if (('http' == $scheme && $port == 80) || ('https' == $scheme && $port == 443)) { 1096 if (('http' == $scheme && 80 == $port) || ('https' == $scheme && 443 == $port)) {
1008 return $this->getHost(); 1097 return $this->getHost();
1009 } 1098 }
1010 1099
1011 return $this->getHost().':'.$port; 1100 return $this->getHost().':'.$port;
1012 } 1101 }
1143 * when trusted proxies were set via "setTrustedProxies()". 1232 * when trusted proxies were set via "setTrustedProxies()".
1144 * 1233 *
1145 * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http". 1234 * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http".
1146 * 1235 *
1147 * If your reverse proxy uses a different header name than "X-Forwarded-Proto" 1236 * If your reverse proxy uses a different header name than "X-Forwarded-Proto"
1148 * ("SSL_HTTPS" for instance), configure it via "setTrustedHeaderName()" with 1237 * ("SSL_HTTPS" for instance), configure it via the $trustedHeaderSet
1149 * the "client-proto" key. 1238 * argument of the Request::setTrustedProxies() method instead.
1150 * 1239 *
1151 * @return bool 1240 * @return bool
1152 */ 1241 */
1153 public function isSecure() 1242 public function isSecure()
1154 { 1243 {
1168 * when trusted proxies were set via "setTrustedProxies()". 1257 * when trusted proxies were set via "setTrustedProxies()".
1169 * 1258 *
1170 * The "X-Forwarded-Host" header must contain the client host name. 1259 * The "X-Forwarded-Host" header must contain the client host name.
1171 * 1260 *
1172 * If your reverse proxy uses a different header name than "X-Forwarded-Host", 1261 * If your reverse proxy uses a different header name than "X-Forwarded-Host",
1173 * configure it via "setTrustedHeaderName()" with the "client-host" key. 1262 * configure it via the $trustedHeaderSet argument of the
1263 * Request::setTrustedProxies() method instead.
1174 * 1264 *
1175 * @return string 1265 * @return string
1176 * 1266 *
1177 * @throws \UnexpectedValueException when the host name is invalid 1267 * @throws SuspiciousOperationException when the host name is invalid or not trusted
1178 */ 1268 */
1179 public function getHost() 1269 public function getHost()
1180 { 1270 {
1181 if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_HOST)) { 1271 if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_HOST)) {
1182 $host = $host[0]; 1272 $host = $host[0];
1192 1282
1193 // as the host can come from the user (HTTP_HOST and depending on the configuration, SERVER_NAME too can come from the user) 1283 // as the host can come from the user (HTTP_HOST and depending on the configuration, SERVER_NAME too can come from the user)
1194 // check that it does not contain forbidden characters (see RFC 952 and RFC 2181) 1284 // check that it does not contain forbidden characters (see RFC 952 and RFC 2181)
1195 // use preg_replace() instead of preg_match() to prevent DoS attacks with long host names 1285 // use preg_replace() instead of preg_match() to prevent DoS attacks with long host names
1196 if ($host && '' !== preg_replace('/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/', '', $host)) { 1286 if ($host && '' !== preg_replace('/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/', '', $host)) {
1197 throw new \UnexpectedValueException(sprintf('Invalid Host "%s"', $host)); 1287 if (!$this->isHostValid) {
1288 return '';
1289 }
1290 $this->isHostValid = false;
1291
1292 throw new SuspiciousOperationException(sprintf('Invalid Host "%s".', $host));
1198 } 1293 }
1199 1294
1200 if (count(self::$trustedHostPatterns) > 0) { 1295 if (count(self::$trustedHostPatterns) > 0) {
1201 // 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
1202 1297
1210 1305
1211 return $host; 1306 return $host;
1212 } 1307 }
1213 } 1308 }
1214 1309
1215 throw new \UnexpectedValueException(sprintf('Untrusted Host "%s"', $host)); 1310 if (!$this->isHostValid) {
1311 return '';
1312 }
1313 $this->isHostValid = false;
1314
1315 throw new SuspiciousOperationException(sprintf('Untrusted Host "%s".', $host));
1216 } 1316 }
1217 1317
1218 return $host; 1318 return $host;
1219 } 1319 }
1220 1320
1458 public function isMethodSafe(/* $andCacheable = true */) 1558 public function isMethodSafe(/* $andCacheable = true */)
1459 { 1559 {
1460 if (!func_num_args() || func_get_arg(0)) { 1560 if (!func_num_args() || func_get_arg(0)) {
1461 // This deprecation should be turned into a BadMethodCallException in 4.0 (without adding the argument in the signature) 1561 // This deprecation should be turned into a BadMethodCallException in 4.0 (without adding the argument in the signature)
1462 // then setting $andCacheable to false should be deprecated in 4.1 1562 // then setting $andCacheable to false should be deprecated in 4.1
1463 @trigger_error('Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is deprecated since version 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); 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);
1464 1564
1465 return in_array($this->getMethod(), array('GET', 'HEAD')); 1565 return in_array($this->getMethod(), array('GET', 'HEAD'));
1466 } 1566 }
1467 1567
1468 return in_array($this->getMethod(), array('GET', 'HEAD', 'OPTIONS', 'TRACE')); 1568 return in_array($this->getMethod(), array('GET', 'HEAD', 'OPTIONS', 'TRACE'));
1486 * @return bool 1586 * @return bool
1487 */ 1587 */
1488 public function isMethodCacheable() 1588 public function isMethodCacheable()
1489 { 1589 {
1490 return in_array($this->getMethod(), array('GET', 'HEAD')); 1590 return in_array($this->getMethod(), array('GET', 'HEAD'));
1591 }
1592
1593 /**
1594 * Returns the protocol version.
1595 *
1596 * If the application is behind a proxy, the protocol version used in the
1597 * requests between the client and the proxy and between the proxy and the
1598 * server might be different. This returns the former (from the "Via" header)
1599 * if the proxy is trusted (see "setTrustedProxies()"), otherwise it returns
1600 * the latter (from the "SERVER_PROTOCOL" server parameter).
1601 *
1602 * @return string
1603 */
1604 public function getProtocolVersion()
1605 {
1606 if ($this->isFromTrustedProxy()) {
1607 preg_match('~^(HTTP/)?([1-9]\.[0-9]) ~', $this->headers->get('Via'), $matches);
1608
1609 if ($matches) {
1610 return 'HTTP/'.$matches[2];
1611 }
1612 }
1613
1614 return $this->server->get('SERVER_PROTOCOL');
1491 } 1615 }
1492 1616
1493 /** 1617 /**
1494 * Returns the request body content. 1618 * Returns the request body content.
1495 * 1619 *
1616 if (count($codes) > 1) { 1740 if (count($codes) > 1) {
1617 $lang = $codes[1]; 1741 $lang = $codes[1];
1618 } 1742 }
1619 } else { 1743 } else {
1620 for ($i = 0, $max = count($codes); $i < $max; ++$i) { 1744 for ($i = 0, $max = count($codes); $i < $max; ++$i) {
1621 if ($i === 0) { 1745 if (0 === $i) {
1622 $lang = strtolower($codes[0]); 1746 $lang = strtolower($codes[0]);
1623 } else { 1747 } else {
1624 $lang .= '_'.strtoupper($codes[$i]); 1748 $lang .= '_'.strtoupper($codes[$i]);
1625 } 1749 }
1626 } 1750 }
1711 $this->server->remove('IIS_WasUrlRewritten'); 1835 $this->server->remove('IIS_WasUrlRewritten');
1712 } elseif ($this->headers->has('X_REWRITE_URL')) { 1836 } elseif ($this->headers->has('X_REWRITE_URL')) {
1713 // IIS with ISAPI_Rewrite 1837 // IIS with ISAPI_Rewrite
1714 $requestUri = $this->headers->get('X_REWRITE_URL'); 1838 $requestUri = $this->headers->get('X_REWRITE_URL');
1715 $this->headers->remove('X_REWRITE_URL'); 1839 $this->headers->remove('X_REWRITE_URL');
1716 } elseif ($this->server->get('IIS_WasUrlRewritten') == '1' && $this->server->get('UNENCODED_URL') != '') { 1840 } elseif ('1' == $this->server->get('IIS_WasUrlRewritten') && '' != $this->server->get('UNENCODED_URL')) {
1717 // IIS7 with URL Rewrite: make sure we get the unencoded URL (double slash problem) 1841 // IIS7 with URL Rewrite: make sure we get the unencoded URL (double slash problem)
1718 $requestUri = $this->server->get('UNENCODED_URL'); 1842 $requestUri = $this->server->get('UNENCODED_URL');
1719 $this->server->remove('UNENCODED_URL'); 1843 $this->server->remove('UNENCODED_URL');
1720 $this->server->remove('IIS_WasUrlRewritten'); 1844 $this->server->remove('IIS_WasUrlRewritten');
1721 } elseif ($this->server->has('REQUEST_URI')) { 1845 } elseif ($this->server->has('REQUEST_URI')) {
1722 $requestUri = $this->server->get('REQUEST_URI'); 1846 $requestUri = $this->server->get('REQUEST_URI');
1723 // HTTP proxy reqs setup request URI with scheme and host [and port] + the URL path, only use URL path 1847 // HTTP proxy reqs setup request URI with scheme and host [and port] + the URL path, only use URL path
1724 $schemeAndHttpHost = $this->getSchemeAndHttpHost(); 1848 $schemeAndHttpHost = $this->getSchemeAndHttpHost();
1725 if (strpos($requestUri, $schemeAndHttpHost) === 0) { 1849 if (0 === strpos($requestUri, $schemeAndHttpHost)) {
1726 $requestUri = substr($requestUri, strlen($schemeAndHttpHost)); 1850 $requestUri = substr($requestUri, strlen($schemeAndHttpHost));
1727 } 1851 }
1728 } elseif ($this->server->has('ORIG_PATH_INFO')) { 1852 } elseif ($this->server->has('ORIG_PATH_INFO')) {
1729 // IIS 5.0, PHP as CGI 1853 // IIS 5.0, PHP as CGI
1730 $requestUri = $this->server->get('ORIG_PATH_INFO'); 1854 $requestUri = $this->server->get('ORIG_PATH_INFO');
1772 } while ($last > $index && (false !== $pos = strpos($path, $baseUrl)) && 0 != $pos); 1896 } while ($last > $index && (false !== $pos = strpos($path, $baseUrl)) && 0 != $pos);
1773 } 1897 }
1774 1898
1775 // Does the baseUrl have anything in common with the request_uri? 1899 // Does the baseUrl have anything in common with the request_uri?
1776 $requestUri = $this->getRequestUri(); 1900 $requestUri = $this->getRequestUri();
1777 if ($requestUri !== '' && $requestUri[0] !== '/') { 1901 if ('' !== $requestUri && '/' !== $requestUri[0]) {
1778 $requestUri = '/'.$requestUri; 1902 $requestUri = '/'.$requestUri;
1779 } 1903 }
1780 1904
1781 if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) { 1905 if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) {
1782 // full $baseUrl matches 1906 // full $baseUrl matches
1800 } 1924 }
1801 1925
1802 // If using mod_rewrite or ISAPI_Rewrite strip the script filename 1926 // If using mod_rewrite or ISAPI_Rewrite strip the script filename
1803 // out of baseUrl. $pos !== 0 makes sure it is not matching a value 1927 // out of baseUrl. $pos !== 0 makes sure it is not matching a value
1804 // from PATH_INFO or QUERY_STRING 1928 // from PATH_INFO or QUERY_STRING
1805 if (strlen($requestUri) >= strlen($baseUrl) && (false !== $pos = strpos($requestUri, $baseUrl)) && $pos !== 0) { 1929 if (strlen($requestUri) >= strlen($baseUrl) && (false !== $pos = strpos($requestUri, $baseUrl)) && 0 !== $pos) {
1806 $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl)); 1930 $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl));
1807 } 1931 }
1808 1932
1809 return rtrim($baseUrl, '/'.DIRECTORY_SEPARATOR); 1933 return rtrim($baseUrl, '/'.DIRECTORY_SEPARATOR);
1810 } 1934 }
1814 * 1938 *
1815 * @return string base path 1939 * @return string base path
1816 */ 1940 */
1817 protected function prepareBasePath() 1941 protected function prepareBasePath()
1818 { 1942 {
1819 $filename = basename($this->server->get('SCRIPT_FILENAME'));
1820 $baseUrl = $this->getBaseUrl(); 1943 $baseUrl = $this->getBaseUrl();
1821 if (empty($baseUrl)) { 1944 if (empty($baseUrl)) {
1822 return ''; 1945 return '';
1823 } 1946 }
1824 1947
1948 $filename = basename($this->server->get('SCRIPT_FILENAME'));
1825 if (basename($baseUrl) === $filename) { 1949 if (basename($baseUrl) === $filename) {
1826 $basePath = dirname($baseUrl); 1950 $basePath = dirname($baseUrl);
1827 } else { 1951 } else {
1828 $basePath = $baseUrl; 1952 $basePath = $baseUrl;
1829 } 1953 }
1840 * 1964 *
1841 * @return string path info 1965 * @return string path info
1842 */ 1966 */
1843 protected function preparePathInfo() 1967 protected function preparePathInfo()
1844 { 1968 {
1845 $baseUrl = $this->getBaseUrl();
1846
1847 if (null === ($requestUri = $this->getRequestUri())) { 1969 if (null === ($requestUri = $this->getRequestUri())) {
1848 return '/'; 1970 return '/';
1849 } 1971 }
1850 1972
1851 // Remove the query string from REQUEST_URI 1973 // Remove the query string from REQUEST_URI
1852 if (false !== $pos = strpos($requestUri, '?')) { 1974 if (false !== $pos = strpos($requestUri, '?')) {
1853 $requestUri = substr($requestUri, 0, $pos); 1975 $requestUri = substr($requestUri, 0, $pos);
1854 } 1976 }
1855 if ($requestUri !== '' && $requestUri[0] !== '/') { 1977 if ('' !== $requestUri && '/' !== $requestUri[0]) {
1856 $requestUri = '/'.$requestUri; 1978 $requestUri = '/'.$requestUri;
1857 } 1979 }
1858 1980
1981 if (null === ($baseUrl = $this->getBaseUrl())) {
1982 return $requestUri;
1983 }
1984
1859 $pathInfo = substr($requestUri, strlen($baseUrl)); 1985 $pathInfo = substr($requestUri, strlen($baseUrl));
1860 if (null !== $baseUrl && (false === $pathInfo || '' === $pathInfo)) { 1986 if (false === $pathInfo || '' === $pathInfo) {
1861 // If substr() returns false then PATH_INFO is set to an empty string 1987 // If substr() returns false then PATH_INFO is set to an empty string
1862 return '/'; 1988 return '/';
1863 } elseif (null === $baseUrl) {
1864 return $requestUri;
1865 } 1989 }
1866 1990
1867 return (string) $pathInfo; 1991 return (string) $pathInfo;
1868 } 1992 }
1869 1993
1876 'html' => array('text/html', 'application/xhtml+xml'), 2000 'html' => array('text/html', 'application/xhtml+xml'),
1877 'txt' => array('text/plain'), 2001 'txt' => array('text/plain'),
1878 'js' => array('application/javascript', 'application/x-javascript', 'text/javascript'), 2002 'js' => array('application/javascript', 'application/x-javascript', 'text/javascript'),
1879 'css' => array('text/css'), 2003 'css' => array('text/css'),
1880 'json' => array('application/json', 'application/x-json'), 2004 'json' => array('application/json', 'application/x-json'),
2005 'jsonld' => array('application/ld+json'),
1881 'xml' => array('text/xml', 'application/xml', 'application/x-xml'), 2006 'xml' => array('text/xml', 'application/xml', 'application/x-xml'),
1882 'rdf' => array('application/rdf+xml'), 2007 'rdf' => array('application/rdf+xml'),
1883 'atom' => array('application/atom+xml'), 2008 'atom' => array('application/atom+xml'),
1884 'rss' => array('application/rss+xml'), 2009 'rss' => array('application/rss+xml'),
1885 'form' => array('application/x-www-form-urlencoded'), 2010 'form' => array('application/x-www-form-urlencoded'),