|
@@ -30,10 +30,10 @@ use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
|
|
*/
|
|
|
class Request
|
|
|
{
|
|
|
- const HEADER_CLIENT_IP = 'client_ip';
|
|
|
- const HEADER_CLIENT_HOST = 'client_host';
|
|
|
+ const HEADER_CLIENT_IP = 'client_ip';
|
|
|
+ const HEADER_CLIENT_HOST = 'client_host';
|
|
|
const HEADER_CLIENT_PROTO = 'client_proto';
|
|
|
- const HEADER_CLIENT_PORT = 'client_port';
|
|
|
+ const HEADER_CLIENT_PORT = 'client_port';
|
|
|
|
|
|
protected static $trustProxy = false;
|
|
|
|
|
@@ -53,6 +53,8 @@ class Request
|
|
|
self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
|
|
|
);
|
|
|
|
|
|
+ protected static $httpMethodParameterOverride = false;
|
|
|
+
|
|
|
/**
|
|
|
* @var \Symfony\Component\HttpFoundation\ParameterBag
|
|
|
*
|
|
@@ -251,6 +253,9 @@ class Request
|
|
|
/**
|
|
|
* Creates a Request based on a given URI and configuration.
|
|
|
*
|
|
|
+ * The information contained in the URI always take precedence
|
|
|
+ * over the other information (server and parameters).
|
|
|
+ *
|
|
|
* @param string $uri The URI
|
|
|
* @param string $method The HTTP method
|
|
|
* @param array $parameters The query (GET) or request (POST) parameters
|
|
@@ -265,7 +270,7 @@ class Request
|
|
|
*/
|
|
|
public static function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null)
|
|
|
{
|
|
|
- $defaults = array(
|
|
|
+ $server = array_replace(array(
|
|
|
'SERVER_NAME' => 'localhost',
|
|
|
'SERVER_PORT' => 80,
|
|
|
'HTTP_HOST' => 'localhost',
|
|
@@ -278,32 +283,38 @@ class Request
|
|
|
'SCRIPT_FILENAME' => '',
|
|
|
'SERVER_PROTOCOL' => 'HTTP/1.1',
|
|
|
'REQUEST_TIME' => time(),
|
|
|
- );
|
|
|
+ ), $server);
|
|
|
+
|
|
|
+ $server['PATH_INFO'] = '';
|
|
|
+ $server['REQUEST_METHOD'] = strtoupper($method);
|
|
|
|
|
|
$components = parse_url($uri);
|
|
|
if (isset($components['host'])) {
|
|
|
- $defaults['SERVER_NAME'] = $components['host'];
|
|
|
- $defaults['HTTP_HOST'] = $components['host'];
|
|
|
+ $server['SERVER_NAME'] = $components['host'];
|
|
|
+ $server['HTTP_HOST'] = $components['host'];
|
|
|
}
|
|
|
|
|
|
if (isset($components['scheme'])) {
|
|
|
if ('https' === $components['scheme']) {
|
|
|
- $defaults['HTTPS'] = 'on';
|
|
|
- $defaults['SERVER_PORT'] = 443;
|
|
|
+ $server['HTTPS'] = 'on';
|
|
|
+ $server['SERVER_PORT'] = 443;
|
|
|
+ } else {
|
|
|
+ unset($server['HTTPS']);
|
|
|
+ $server['SERVER_PORT'] = 80;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (isset($components['port'])) {
|
|
|
- $defaults['SERVER_PORT'] = $components['port'];
|
|
|
- $defaults['HTTP_HOST'] = $defaults['HTTP_HOST'].':'.$components['port'];
|
|
|
+ $server['SERVER_PORT'] = $components['port'];
|
|
|
+ $server['HTTP_HOST'] = $server['HTTP_HOST'].':'.$components['port'];
|
|
|
}
|
|
|
|
|
|
if (isset($components['user'])) {
|
|
|
- $defaults['PHP_AUTH_USER'] = $components['user'];
|
|
|
+ $server['PHP_AUTH_USER'] = $components['user'];
|
|
|
}
|
|
|
|
|
|
if (isset($components['pass'])) {
|
|
|
- $defaults['PHP_AUTH_PW'] = $components['pass'];
|
|
|
+ $server['PHP_AUTH_PW'] = $components['pass'];
|
|
|
}
|
|
|
|
|
|
if (!isset($components['path'])) {
|
|
@@ -314,7 +325,9 @@ class Request
|
|
|
case 'POST':
|
|
|
case 'PUT':
|
|
|
case 'DELETE':
|
|
|
- $defaults['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
|
|
|
+ if (!isset($server['CONTENT_TYPE'])) {
|
|
|
+ $server['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
|
|
|
+ }
|
|
|
case 'PATCH':
|
|
|
$request = $parameters;
|
|
|
$query = array();
|
|
@@ -331,14 +344,8 @@ class Request
|
|
|
}
|
|
|
$queryString = http_build_query($query, '', '&');
|
|
|
|
|
|
- $uri = $components['path'].('' !== $queryString ? '?'.$queryString : '');
|
|
|
-
|
|
|
- $server = array_replace($defaults, $server, array(
|
|
|
- 'REQUEST_METHOD' => strtoupper($method),
|
|
|
- 'PATH_INFO' => '',
|
|
|
- 'REQUEST_URI' => $uri,
|
|
|
- 'QUERY_STRING' => $queryString,
|
|
|
- ));
|
|
|
+ $server['REQUEST_URI'] = $components['path'].('' !== $queryString ? '?'.$queryString : '');
|
|
|
+ $server['QUERY_STRING'] = $queryString;
|
|
|
|
|
|
return new static($query, $request, array(), $cookies, $files, $server, $content);
|
|
|
}
|
|
@@ -464,6 +471,8 @@ class Request
|
|
|
*/
|
|
|
public static function trustProxyData()
|
|
|
{
|
|
|
+ trigger_error('trustProxyData() is deprecated since version 2.0 and will be removed in 2.3. Use setTrustedProxies() instead.', E_USER_DEPRECATED);
|
|
|
+
|
|
|
self::$trustProxy = true;
|
|
|
}
|
|
|
|
|
@@ -482,6 +491,16 @@ class Request
|
|
|
self::$trustProxy = $proxies ? true : false;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Gets the list of trusted proxies.
|
|
|
+ *
|
|
|
+ * @return array An array of trusted proxies.
|
|
|
+ */
|
|
|
+ public static function getTrustedProxies()
|
|
|
+ {
|
|
|
+ return self::$trustedProxies;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Sets the name for trusted headers.
|
|
|
*
|
|
@@ -496,6 +515,8 @@ class Request
|
|
|
*
|
|
|
* @param string $key The header key
|
|
|
* @param string $value The header name
|
|
|
+ *
|
|
|
+ * @throws \InvalidArgumentException
|
|
|
*/
|
|
|
public static function setTrustedHeaderName($key, $value)
|
|
|
{
|
|
@@ -511,6 +532,8 @@ class Request
|
|
|
* false otherwise.
|
|
|
*
|
|
|
* @return boolean
|
|
|
+ *
|
|
|
+ * @deprecated Deprecated since version 2.2, to be removed in 2.3. Use getTrustedProxies instead.
|
|
|
*/
|
|
|
public static function isProxyTrusted()
|
|
|
{
|
|
@@ -560,6 +583,29 @@ class Request
|
|
|
return implode('&', $parts);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Enables support for the _method request parameter to determine the intended HTTP method.
|
|
|
+ *
|
|
|
+ * Be warned that enabling this feature might lead to CSRF issues in your code.
|
|
|
+ * Check that you are using CSRF tokens when required.
|
|
|
+ *
|
|
|
+ * The HTTP method can only be overridden when the real HTTP method is POST.
|
|
|
+ */
|
|
|
+ public static function enableHttpMethodParameterOverride()
|
|
|
+ {
|
|
|
+ self::$httpMethodParameterOverride = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Checks whether support for the _method request parameter is enabled.
|
|
|
+ *
|
|
|
+ * @return Boolean True when the _method request parameter is enabled, false otherwise
|
|
|
+ */
|
|
|
+ public static function getHttpMethodParameterOverride()
|
|
|
+ {
|
|
|
+ return self::$httpMethodParameterOverride;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Gets a "parameter" value.
|
|
|
*
|
|
@@ -657,8 +703,6 @@ class Request
|
|
|
*
|
|
|
* @see http://en.wikipedia.org/wiki/X-Forwarded-For
|
|
|
*
|
|
|
- * @deprecated The proxy argument is deprecated since version 2.0 and will be removed in 2.3. Use setTrustedProxies instead.
|
|
|
- *
|
|
|
* @api
|
|
|
*/
|
|
|
public function getClientIp()
|
|
@@ -703,7 +747,7 @@ class Request
|
|
|
*
|
|
|
* * http://localhost/mysite returns an empty string
|
|
|
* * http://localhost/mysite/about returns '/about'
|
|
|
- * * htpp://localhost/mysite/enco%20ded returns '/enco%20ded'
|
|
|
+ * * http://localhost/mysite/enco%20ded returns '/enco%20ded'
|
|
|
* * http://localhost/mysite/about?var=1 returns '/about'
|
|
|
*
|
|
|
* @return string The raw path (i.e. not urldecoded)
|
|
@@ -897,8 +941,7 @@ class Request
|
|
|
*/
|
|
|
public function getUri()
|
|
|
{
|
|
|
- $qs = $this->getQueryString();
|
|
|
- if (null !== $qs) {
|
|
|
+ if (null !== $qs = $this->getQueryString()) {
|
|
|
$qs = '?'.$qs;
|
|
|
}
|
|
|
|
|
@@ -1017,26 +1060,51 @@ class Request
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Gets the request method.
|
|
|
+ * Gets the request "intended" method.
|
|
|
+ *
|
|
|
+ * If the X-HTTP-Method-Override header is set, and if the method is a POST,
|
|
|
+ * then it is used to determine the "real" intended HTTP method.
|
|
|
+ *
|
|
|
+ * The _method request parameter can also be used to determine the HTTP method,
|
|
|
+ * but only if enableHttpMethodParameterOverride() has been called.
|
|
|
*
|
|
|
* The method is always an uppercased string.
|
|
|
*
|
|
|
* @return string The request method
|
|
|
*
|
|
|
* @api
|
|
|
+ *
|
|
|
+ * @see getRealMethod
|
|
|
*/
|
|
|
public function getMethod()
|
|
|
{
|
|
|
if (null === $this->method) {
|
|
|
$this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
|
|
|
+
|
|
|
if ('POST' === $this->method) {
|
|
|
- $this->method = strtoupper($this->headers->get('X-HTTP-METHOD-OVERRIDE', $this->request->get('_method', $this->query->get('_method', 'POST'))));
|
|
|
+ if ($method = $this->headers->get('X-HTTP-METHOD-OVERRIDE')) {
|
|
|
+ $this->method = strtoupper($method);
|
|
|
+ } elseif (self::$httpMethodParameterOverride) {
|
|
|
+ $this->method = strtoupper($this->request->get('_method', $this->query->get('_method', 'POST')));
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return $this->method;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Gets the "real" request method.
|
|
|
+ *
|
|
|
+ * @return string The request method
|
|
|
+ *
|
|
|
+ * @see getMethod
|
|
|
+ */
|
|
|
+ public function getRealMethod()
|
|
|
+ {
|
|
|
+ return strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Gets the mime type associated with the format.
|
|
|
*
|
|
@@ -1216,6 +1284,8 @@ class Request
|
|
|
* @param Boolean $asResource If true, a resource will be returned
|
|
|
*
|
|
|
* @return string|resource The request body content or a resource to read the body stream.
|
|
|
+ *
|
|
|
+ * @throws \LogicException
|
|
|
*/
|
|
|
public function getContent($asResource = false)
|
|
|
{
|
|
@@ -1275,7 +1345,18 @@ class Request
|
|
|
return $locales[0];
|
|
|
}
|
|
|
|
|
|
- $preferredLanguages = array_values(array_intersect($preferredLanguages, $locales));
|
|
|
+ $extendedPreferredLanguages = array();
|
|
|
+ foreach ($preferredLanguages as $language) {
|
|
|
+ $extendedPreferredLanguages[] = $language;
|
|
|
+ if (false !== $position = strpos($language, '_')) {
|
|
|
+ $superLanguage = substr($language, 0, $position);
|
|
|
+ if (!in_array($superLanguage, $preferredLanguages)) {
|
|
|
+ $extendedPreferredLanguages[] = $superLanguage;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $preferredLanguages = array_values(array_intersect($extendedPreferredLanguages, $locales));
|
|
|
|
|
|
return isset($preferredLanguages[0]) ? $preferredLanguages[0] : $locales[0];
|
|
|
}
|
|
@@ -1293,9 +1374,9 @@ class Request
|
|
|
return $this->languages;
|
|
|
}
|
|
|
|
|
|
- $languages = $this->splitHttpAcceptHeader($this->headers->get('Accept-Language'));
|
|
|
+ $languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all();
|
|
|
$this->languages = array();
|
|
|
- foreach ($languages as $lang => $q) {
|
|
|
+ foreach (array_keys($languages) as $lang) {
|
|
|
if (strstr($lang, '-')) {
|
|
|
$codes = explode('-', $lang);
|
|
|
if ($codes[0] == 'i') {
|
|
@@ -1335,7 +1416,7 @@ class Request
|
|
|
return $this->charsets;
|
|
|
}
|
|
|
|
|
|
- return $this->charsets = array_keys($this->splitHttpAcceptHeader($this->headers->get('Accept-Charset')));
|
|
|
+ return $this->charsets = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all());
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1351,14 +1432,15 @@ class Request
|
|
|
return $this->acceptableContentTypes;
|
|
|
}
|
|
|
|
|
|
- return $this->acceptableContentTypes = array_keys($this->splitHttpAcceptHeader($this->headers->get('Accept')));
|
|
|
+ return $this->acceptableContentTypes = array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns true if the request is a XMLHttpRequest.
|
|
|
*
|
|
|
* It works if your JavaScript library set an X-Requested-With HTTP header.
|
|
|
- * It is known to work with Prototype, Mootools, jQuery.
|
|
|
+ * It is known to work with common JavaScript frameworks:
|
|
|
+ * @link http://en.wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript
|
|
|
*
|
|
|
* @return Boolean true if the request is an XMLHttpRequest, false otherwise
|
|
|
*
|
|
@@ -1375,40 +1457,23 @@ class Request
|
|
|
* @param string $header Header to split
|
|
|
*
|
|
|
* @return array Array indexed by the values of the Accept-* header in preferred order
|
|
|
+ *
|
|
|
+ * @deprecated Deprecated since version 2.2, to be removed in 2.3.
|
|
|
*/
|
|
|
public function splitHttpAcceptHeader($header)
|
|
|
{
|
|
|
- if (!$header) {
|
|
|
- return array();
|
|
|
- }
|
|
|
-
|
|
|
- $values = array();
|
|
|
- $groups = array();
|
|
|
- foreach (array_filter(explode(',', $header)) as $value) {
|
|
|
- // Cut off any q-value that might come after a semi-colon
|
|
|
- if (preg_match('/;\s*(q=.*$)/', $value, $match)) {
|
|
|
- $q = substr(trim($match[1]), 2);
|
|
|
- $value = trim(substr($value, 0, -strlen($match[0])));
|
|
|
- } else {
|
|
|
- $q = 1;
|
|
|
- }
|
|
|
-
|
|
|
- $groups[$q][] = $value;
|
|
|
- }
|
|
|
-
|
|
|
- krsort($groups);
|
|
|
+ trigger_error('splitHttpAcceptHeader() is deprecated since version 2.2 and will be removed in 2.3.', E_USER_DEPRECATED);
|
|
|
|
|
|
- foreach ($groups as $q => $items) {
|
|
|
- $q = (float) $q;
|
|
|
-
|
|
|
- if (0 < $q) {
|
|
|
- foreach ($items as $value) {
|
|
|
- $values[trim($value)] = $q;
|
|
|
- }
|
|
|
+ $headers = array();
|
|
|
+ foreach (AcceptHeader::fromString($header)->all() as $item) {
|
|
|
+ $key = $item->getValue();
|
|
|
+ foreach ($item->getAttributes() as $name => $value) {
|
|
|
+ $key .= sprintf(';%s=%s', $name, $value);
|
|
|
}
|
|
|
+ $headers[$key] = $item->getQuality();
|
|
|
}
|
|
|
|
|
|
- return $values;
|
|
|
+ return $headers;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1426,12 +1491,16 @@ class Request
|
|
|
if ($this->headers->has('X_ORIGINAL_URL') && false !== stripos(PHP_OS, 'WIN')) {
|
|
|
// IIS with Microsoft Rewrite Module
|
|
|
$requestUri = $this->headers->get('X_ORIGINAL_URL');
|
|
|
+ $this->headers->remove('X_ORIGINAL_URL');
|
|
|
} elseif ($this->headers->has('X_REWRITE_URL') && false !== stripos(PHP_OS, 'WIN')) {
|
|
|
// IIS with ISAPI_Rewrite
|
|
|
$requestUri = $this->headers->get('X_REWRITE_URL');
|
|
|
+ $this->headers->remove('X_REWRITE_URL');
|
|
|
} elseif ($this->server->get('IIS_WasUrlRewritten') == '1' && $this->server->get('UNENCODED_URL') != '') {
|
|
|
// IIS7 with URL Rewrite: make sure we get the unencoded url (double slash problem)
|
|
|
$requestUri = $this->server->get('UNENCODED_URL');
|
|
|
+ $this->server->remove('UNENCODED_URL');
|
|
|
+ $this->server->remove('IIS_WasUrlRewritten');
|
|
|
} elseif ($this->server->has('REQUEST_URI')) {
|
|
|
$requestUri = $this->server->get('REQUEST_URI');
|
|
|
// HTTP proxy reqs setup request uri with scheme and host [and port] + the url path, only use url path
|
|
@@ -1445,8 +1514,12 @@ class Request
|
|
|
if ('' != $this->server->get('QUERY_STRING')) {
|
|
|
$requestUri .= '?'.$this->server->get('QUERY_STRING');
|
|
|
}
|
|
|
+ $this->server->remove('ORIG_PATH_INFO');
|
|
|
}
|
|
|
|
|
|
+ // normalize the request URI to ease creating sub-requests from this request
|
|
|
+ $this->server->set('REQUEST_URI', $requestUri);
|
|
|
+
|
|
|
return $requestUri;
|
|
|
}
|
|
|
|