2015-08-18 00:00:26 +00:00
< ? php
/*
* This file is part of the Symfony package .
*
* ( c ) Fabien Potencier < fabien @ symfony . com >
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
namespace Symfony\Component\HttpFoundation ;
2017-02-03 00:28:38 +00:00
use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException ;
2018-11-23 12:29:20 +00:00
use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException ;
2015-08-18 00:00:26 +00:00
use Symfony\Component\HttpFoundation\Session\SessionInterface ;
/**
* Request represents an HTTP request .
*
* The methods dealing with URL accept / return a raw path ( % encoded ) :
* * getBasePath
* * getBaseUrl
* * getPathInfo
* * getRequestUri
* * getUri
* * getUriForPath
*
* @ author Fabien Potencier < fabien @ symfony . com >
*/
class Request
{
2018-11-23 12:29:20 +00:00
const HEADER_FORWARDED = 0 b00001 ; // When using RFC 7239
const HEADER_X_FORWARDED_FOR = 0 b00010 ;
const HEADER_X_FORWARDED_HOST = 0 b00100 ;
const HEADER_X_FORWARDED_PROTO = 0 b01000 ;
const HEADER_X_FORWARDED_PORT = 0 b10000 ;
const HEADER_X_FORWARDED_ALL = 0 b11110 ; // All "X-Forwarded-*" headers
const HEADER_X_FORWARDED_AWS_ELB = 0 b11010 ; // AWS ELB doesn't send X-Forwarded-Host
/** @deprecated since version 3.3, to be removed in 4.0 */
const HEADER_CLIENT_IP = self :: HEADER_X_FORWARDED_FOR ;
/** @deprecated since version 3.3, to be removed in 4.0 */
const HEADER_CLIENT_HOST = self :: HEADER_X_FORWARDED_HOST ;
/** @deprecated since version 3.3, to be removed in 4.0 */
const HEADER_CLIENT_PROTO = self :: HEADER_X_FORWARDED_PROTO ;
/** @deprecated since version 3.3, to be removed in 4.0 */
const HEADER_CLIENT_PORT = self :: HEADER_X_FORWARDED_PORT ;
2015-08-18 00:00:26 +00:00
const METHOD_HEAD = 'HEAD' ;
const METHOD_GET = 'GET' ;
const METHOD_POST = 'POST' ;
const METHOD_PUT = 'PUT' ;
const METHOD_PATCH = 'PATCH' ;
const METHOD_DELETE = 'DELETE' ;
const METHOD_PURGE = 'PURGE' ;
const METHOD_OPTIONS = 'OPTIONS' ;
const METHOD_TRACE = 'TRACE' ;
const METHOD_CONNECT = 'CONNECT' ;
/**
* @ var string []
*/
protected static $trustedProxies = array ();
/**
* @ var string []
*/
protected static $trustedHostPatterns = array ();
/**
* @ var string []
*/
protected static $trustedHosts = array ();
/**
* Names for headers that can be trusted when
* using trusted proxies .
*
* The FORWARDED header is the standard as of rfc7239 .
*
* The other headers are non - standard , but widely used
* by popular reverse proxies ( like Apache mod_proxy or Amazon EC2 ) .
2018-11-23 12:29:20 +00:00
*
* @ deprecated since version 3.3 , to be removed in 4.0
2015-08-18 00:00:26 +00:00
*/
protected static $trustedHeaders = array (
self :: HEADER_FORWARDED => 'FORWARDED' ,
self :: HEADER_CLIENT_IP => 'X_FORWARDED_FOR' ,
self :: HEADER_CLIENT_HOST => 'X_FORWARDED_HOST' ,
self :: HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO' ,
self :: HEADER_CLIENT_PORT => 'X_FORWARDED_PORT' ,
);
protected static $httpMethodParameterOverride = false ;
/**
* Custom parameters .
*
* @ var \Symfony\Component\HttpFoundation\ParameterBag
*/
public $attributes ;
/**
* Request body parameters ( $_POST ) .
*
* @ var \Symfony\Component\HttpFoundation\ParameterBag
*/
public $request ;
/**
* Query string parameters ( $_GET ) .
*
* @ var \Symfony\Component\HttpFoundation\ParameterBag
*/
public $query ;
/**
* Server and execution environment parameters ( $_SERVER ) .
*
* @ var \Symfony\Component\HttpFoundation\ServerBag
*/
public $server ;
/**
* Uploaded files ( $_FILES ) .
*
* @ var \Symfony\Component\HttpFoundation\FileBag
*/
public $files ;
/**
* Cookies ( $_COOKIE ) .
*
* @ var \Symfony\Component\HttpFoundation\ParameterBag
*/
public $cookies ;
/**
* Headers ( taken from the $_SERVER ) .
*
* @ var \Symfony\Component\HttpFoundation\HeaderBag
*/
public $headers ;
/**
2018-11-23 12:29:20 +00:00
* @ var string | resource | false | null
2015-08-18 00:00:26 +00:00
*/
protected $content ;
/**
* @ var array
*/
protected $languages ;
/**
* @ var array
*/
protected $charsets ;
/**
* @ var array
*/
protected $encodings ;
/**
* @ var array
*/
protected $acceptableContentTypes ;
/**
* @ var string
*/
protected $pathInfo ;
/**
* @ var string
*/
protected $requestUri ;
/**
* @ var string
*/
protected $baseUrl ;
/**
* @ var string
*/
protected $basePath ;
/**
* @ var string
*/
protected $method ;
/**
* @ var string
*/
protected $format ;
/**
* @ var \Symfony\Component\HttpFoundation\Session\SessionInterface
*/
protected $session ;
/**
* @ var string
*/
protected $locale ;
/**
* @ var string
*/
protected $defaultLocale = 'en' ;
/**
* @ var array
*/
protected static $formats ;
protected static $requestFactory ;
2018-11-23 12:29:20 +00:00
private $isHostValid = true ;
2017-04-13 14:53:35 +00:00
private $isForwardedValid = true ;
2018-11-23 12:29:20 +00:00
private static $trustedHeaderSet = - 1 ;
/** @deprecated since version 3.3, to be removed in 4.0 */
private static $trustedHeaderNames = array (
self :: HEADER_FORWARDED => 'FORWARDED' ,
self :: HEADER_CLIENT_IP => 'X_FORWARDED_FOR' ,
self :: HEADER_CLIENT_HOST => 'X_FORWARDED_HOST' ,
self :: HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO' ,
self :: HEADER_CLIENT_PORT => 'X_FORWARDED_PORT' ,
);
2017-04-13 14:53:35 +00:00
private static $forwardedParams = array (
2018-11-23 12:29:20 +00:00
self :: HEADER_X_FORWARDED_FOR => 'for' ,
self :: HEADER_X_FORWARDED_HOST => 'host' ,
self :: HEADER_X_FORWARDED_PROTO => 'proto' ,
self :: HEADER_X_FORWARDED_PORT => 'host' ,
2017-04-13 14:53:35 +00:00
);
2015-08-18 00:00:26 +00:00
/**
2018-11-23 12:29:20 +00:00
* @ param array $query The GET parameters
* @ param array $request The POST parameters
* @ param array $attributes The request attributes ( parameters parsed from the PATH_INFO , ... )
* @ param array $cookies The COOKIE parameters
* @ param array $files The FILES parameters
* @ param array $server The SERVER parameters
* @ param string | resource | null $content The raw body data
2015-08-18 00:00:26 +00:00
*/
public function __construct ( array $query = array (), array $request = array (), array $attributes = array (), array $cookies = array (), array $files = array (), array $server = array (), $content = null )
{
$this -> initialize ( $query , $request , $attributes , $cookies , $files , $server , $content );
}
/**
* Sets the parameters for this request .
*
* This method also re - initializes all properties .
*
2018-11-23 12:29:20 +00:00
* @ param array $query The GET parameters
* @ param array $request The POST parameters
* @ param array $attributes The request attributes ( parameters parsed from the PATH_INFO , ... )
* @ param array $cookies The COOKIE parameters
* @ param array $files The FILES parameters
* @ param array $server The SERVER parameters
* @ param string | resource | null $content The raw body data
2015-08-18 00:00:26 +00:00
*/
public function initialize ( array $query = array (), array $request = array (), array $attributes = array (), array $cookies = array (), array $files = array (), array $server = array (), $content = null )
{
$this -> request = new ParameterBag ( $request );
$this -> query = new ParameterBag ( $query );
$this -> attributes = new ParameterBag ( $attributes );
$this -> cookies = new ParameterBag ( $cookies );
$this -> files = new FileBag ( $files );
$this -> server = new ServerBag ( $server );
$this -> headers = new HeaderBag ( $this -> server -> getHeaders ());
$this -> content = $content ;
$this -> languages = null ;
$this -> charsets = null ;
$this -> encodings = null ;
$this -> acceptableContentTypes = null ;
$this -> pathInfo = null ;
$this -> requestUri = null ;
$this -> baseUrl = null ;
$this -> basePath = null ;
$this -> method = null ;
$this -> format = null ;
}
/**
* Creates a new request with values from PHP ' s super globals .
*
2017-02-03 00:28:38 +00:00
* @ return static
2015-08-18 00:00:26 +00:00
*/
public static function createFromGlobals ()
{
// With the php's bug #66606, the php's built-in web server
// stores the Content-Type and Content-Length header values in
// HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH fields.
$server = $_SERVER ;
2018-11-23 12:29:20 +00:00
if ( 'cli-server' === \PHP_SAPI ) {
2015-08-18 00:00:26 +00:00
if ( array_key_exists ( 'HTTP_CONTENT_LENGTH' , $_SERVER )) {
$server [ 'CONTENT_LENGTH' ] = $_SERVER [ 'HTTP_CONTENT_LENGTH' ];
}
if ( array_key_exists ( 'HTTP_CONTENT_TYPE' , $_SERVER )) {
$server [ 'CONTENT_TYPE' ] = $_SERVER [ 'HTTP_CONTENT_TYPE' ];
}
}
$request = self :: createRequestFromFactory ( $_GET , $_POST , array (), $_COOKIE , $_FILES , $server );
if ( 0 === strpos ( $request -> headers -> get ( 'CONTENT_TYPE' ), 'application/x-www-form-urlencoded' )
2018-11-23 12:29:20 +00:00
&& \in_array ( strtoupper ( $request -> server -> get ( 'REQUEST_METHOD' , 'GET' )), array ( 'PUT' , 'DELETE' , 'PATCH' ))
2015-08-18 00:00:26 +00:00
) {
parse_str ( $request -> getContent (), $data );
$request -> request = new ParameterBag ( $data );
}
return $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 ) .
*
2018-11-23 12:29:20 +00:00
* @ param string $uri The URI
* @ param string $method The HTTP method
* @ param array $parameters The query ( GET ) or request ( POST ) parameters
* @ param array $cookies The request cookies ( $_COOKIE )
* @ param array $files The request files ( $_FILES )
* @ param array $server The server parameters ( $_SERVER )
* @ param string | resource | null $content The raw body data
2015-08-18 00:00:26 +00:00
*
2017-02-03 00:28:38 +00:00
* @ return static
2015-08-18 00:00:26 +00:00
*/
public static function create ( $uri , $method = 'GET' , $parameters = array (), $cookies = array (), $files = array (), $server = array (), $content = null )
{
$server = array_replace ( array (
'SERVER_NAME' => 'localhost' ,
'SERVER_PORT' => 80 ,
'HTTP_HOST' => 'localhost' ,
2018-11-23 12:29:20 +00:00
'HTTP_USER_AGENT' => 'Symfony/3.X' ,
2015-08-18 00:00:26 +00:00
'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' ,
'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5' ,
'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7' ,
'REMOTE_ADDR' => '127.0.0.1' ,
'SCRIPT_NAME' => '' ,
'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' ])) {
$server [ 'SERVER_NAME' ] = $components [ 'host' ];
$server [ 'HTTP_HOST' ] = $components [ 'host' ];
}
if ( isset ( $components [ 'scheme' ])) {
if ( 'https' === $components [ 'scheme' ]) {
$server [ 'HTTPS' ] = 'on' ;
$server [ 'SERVER_PORT' ] = 443 ;
} else {
unset ( $server [ 'HTTPS' ]);
$server [ 'SERVER_PORT' ] = 80 ;
}
}
if ( isset ( $components [ 'port' ])) {
$server [ 'SERVER_PORT' ] = $components [ 'port' ];
2018-11-23 12:29:20 +00:00
$server [ 'HTTP_HOST' ] .= ':' . $components [ 'port' ];
2015-08-18 00:00:26 +00:00
}
if ( isset ( $components [ 'user' ])) {
$server [ 'PHP_AUTH_USER' ] = $components [ 'user' ];
}
if ( isset ( $components [ 'pass' ])) {
$server [ 'PHP_AUTH_PW' ] = $components [ 'pass' ];
}
if ( ! isset ( $components [ 'path' ])) {
$components [ 'path' ] = '/' ;
}
switch ( strtoupper ( $method )) {
case 'POST' :
case 'PUT' :
case 'DELETE' :
if ( ! isset ( $server [ 'CONTENT_TYPE' ])) {
$server [ 'CONTENT_TYPE' ] = 'application/x-www-form-urlencoded' ;
}
// no break
case 'PATCH' :
$request = $parameters ;
$query = array ();
break ;
default :
$request = array ();
$query = $parameters ;
break ;
}
$queryString = '' ;
if ( isset ( $components [ 'query' ])) {
parse_str ( html_entity_decode ( $components [ 'query' ]), $qs );
if ( $query ) {
$query = array_replace ( $qs , $query );
$queryString = http_build_query ( $query , '' , '&' );
} else {
$query = $qs ;
$queryString = $components [ 'query' ];
}
} elseif ( $query ) {
$queryString = http_build_query ( $query , '' , '&' );
}
$server [ 'REQUEST_URI' ] = $components [ 'path' ] . ( '' !== $queryString ? '?' . $queryString : '' );
$server [ 'QUERY_STRING' ] = $queryString ;
return self :: createRequestFromFactory ( $query , $request , array (), $cookies , $files , $server , $content );
}
/**
* Sets a callable able to create a Request instance .
*
* This is mainly useful when you need to override the Request class
* to keep BC with an existing system . It should not be used for any
* other purpose .
*
* @ param callable | null $callable A PHP callable
*/
public static function setFactory ( $callable )
{
self :: $requestFactory = $callable ;
}
/**
* Clones a request and overrides some of its parameters .
*
* @ param array $query The GET parameters
* @ param array $request The POST parameters
* @ param array $attributes The request attributes ( parameters parsed from the PATH_INFO , ... )
* @ param array $cookies The COOKIE parameters
* @ param array $files The FILES parameters
* @ param array $server The SERVER parameters
*
2017-02-03 00:28:38 +00:00
* @ return static
2015-08-18 00:00:26 +00:00
*/
public function duplicate ( array $query = null , array $request = null , array $attributes = null , array $cookies = null , array $files = null , array $server = null )
{
$dup = clone $this ;
2018-11-23 12:29:20 +00:00
if ( null !== $query ) {
2015-08-18 00:00:26 +00:00
$dup -> query = new ParameterBag ( $query );
}
2018-11-23 12:29:20 +00:00
if ( null !== $request ) {
2015-08-18 00:00:26 +00:00
$dup -> request = new ParameterBag ( $request );
}
2018-11-23 12:29:20 +00:00
if ( null !== $attributes ) {
2015-08-18 00:00:26 +00:00
$dup -> attributes = new ParameterBag ( $attributes );
}
2018-11-23 12:29:20 +00:00
if ( null !== $cookies ) {
2015-08-18 00:00:26 +00:00
$dup -> cookies = new ParameterBag ( $cookies );
}
2018-11-23 12:29:20 +00:00
if ( null !== $files ) {
2015-08-18 00:00:26 +00:00
$dup -> files = new FileBag ( $files );
}
2018-11-23 12:29:20 +00:00
if ( null !== $server ) {
2015-08-18 00:00:26 +00:00
$dup -> server = new ServerBag ( $server );
$dup -> headers = new HeaderBag ( $dup -> server -> getHeaders ());
}
$dup -> languages = null ;
$dup -> charsets = null ;
$dup -> encodings = null ;
$dup -> acceptableContentTypes = null ;
$dup -> pathInfo = null ;
$dup -> requestUri = null ;
$dup -> baseUrl = null ;
$dup -> basePath = null ;
$dup -> method = null ;
$dup -> format = null ;
if ( ! $dup -> get ( '_format' ) && $this -> get ( '_format' )) {
$dup -> attributes -> set ( '_format' , $this -> get ( '_format' ));
}
if ( ! $dup -> getRequestFormat ( null )) {
$dup -> setRequestFormat ( $this -> getRequestFormat ( null ));
}
return $dup ;
}
/**
* Clones the current request .
*
* Note that the session is not cloned as duplicated requests
* are most of the time sub - requests of the main one .
*/
public function __clone ()
{
$this -> query = clone $this -> query ;
$this -> request = clone $this -> request ;
$this -> attributes = clone $this -> attributes ;
$this -> cookies = clone $this -> cookies ;
$this -> files = clone $this -> files ;
$this -> server = clone $this -> server ;
$this -> headers = clone $this -> headers ;
}
/**
* Returns the request as a string .
*
* @ return string The request
*/
public function __toString ()
{
2015-08-27 19:03:05 +00:00
try {
$content = $this -> getContent ();
} catch ( \LogicException $e ) {
return trigger_error ( $e , E_USER_ERROR );
}
2018-11-23 12:29:20 +00:00
$cookieHeader = '' ;
$cookies = array ();
foreach ( $this -> cookies as $k => $v ) {
$cookies [] = $k . '=' . $v ;
}
if ( ! empty ( $cookies )) {
$cookieHeader = 'Cookie: ' . implode ( '; ' , $cookies ) . " \r \n " ;
}
2015-08-18 00:00:26 +00:00
return
sprintf ( '%s %s %s' , $this -> getMethod (), $this -> getRequestUri (), $this -> server -> get ( 'SERVER_PROTOCOL' )) . " \r \n " .
2018-11-23 12:29:20 +00:00
$this -> headers .
$cookieHeader . " \r \n " .
2015-08-27 19:03:05 +00:00
$content ;
2015-08-18 00:00:26 +00:00
}
/**
* Overrides the PHP global variables according to this request instance .
*
* It overrides $_GET , $_POST , $_REQUEST , $_SERVER , $_COOKIE .
* $_FILES is never overridden , see rfc1867
*/
public function overrideGlobals ()
{
2018-11-23 12:29:20 +00:00
$this -> server -> set ( 'QUERY_STRING' , static :: normalizeQueryString ( http_build_query ( $this -> query -> all (), '' , '&' )));
2015-08-18 00:00:26 +00:00
$_GET = $this -> query -> all ();
$_POST = $this -> request -> all ();
$_SERVER = $this -> server -> all ();
$_COOKIE = $this -> cookies -> all ();
foreach ( $this -> headers -> all () as $key => $value ) {
$key = strtoupper ( str_replace ( '-' , '_' , $key ));
2018-11-23 12:29:20 +00:00
if ( \in_array ( $key , array ( 'CONTENT_TYPE' , 'CONTENT_LENGTH' ))) {
2015-08-18 00:00:26 +00:00
$_SERVER [ $key ] = implode ( ', ' , $value );
} else {
$_SERVER [ 'HTTP_' . $key ] = implode ( ', ' , $value );
}
}
$request = array ( 'g' => $_GET , 'p' => $_POST , 'c' => $_COOKIE );
$requestOrder = ini_get ( 'request_order' ) ? : ini_get ( 'variables_order' );
$requestOrder = preg_replace ( '#[^cgp]#' , '' , strtolower ( $requestOrder )) ? : 'gp' ;
$_REQUEST = array ();
foreach ( str_split ( $requestOrder ) as $order ) {
$_REQUEST = array_merge ( $_REQUEST , $request [ $order ]);
}
}
/**
* Sets a list of trusted proxies .
*
* You should only list the reverse proxies that you manage directly .
*
2018-11-23 12:29:20 +00:00
* @ param array $proxies A list of trusted proxies
* @ param int $trustedHeaderSet A bit field of Request :: HEADER_ * , to set which headers to trust from your proxies
*
* @ throws \InvalidArgumentException When $trustedHeaderSet is invalid
2015-08-18 00:00:26 +00:00
*/
2018-11-23 12:29:20 +00:00
public static function setTrustedProxies ( array $proxies /*, int $trustedHeaderSet*/ )
2015-08-18 00:00:26 +00:00
{
self :: $trustedProxies = $proxies ;
2018-11-23 12:29:20 +00:00
if ( 2 > \func_num_args ()) {
@ 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 );
return ;
}
$trustedHeaderSet = ( int ) func_get_arg ( 1 );
foreach ( self :: $trustedHeaderNames as $header => $name ) {
self :: $trustedHeaders [ $header ] = $header & $trustedHeaderSet ? $name : null ;
}
self :: $trustedHeaderSet = $trustedHeaderSet ;
2015-08-18 00:00:26 +00:00
}
/**
* Gets the list of trusted proxies .
*
2017-02-03 00:28:38 +00:00
* @ return array An array of trusted proxies
2015-08-18 00:00:26 +00:00
*/
public static function getTrustedProxies ()
{
return self :: $trustedProxies ;
}
2018-11-23 12:29:20 +00:00
/**
* Gets the set of trusted headers from trusted proxies .
*
* @ return int A bit field of Request :: HEADER_ * that defines which headers are trusted from your proxies
*/
public static function getTrustedHeaderSet ()
{
return self :: $trustedHeaderSet ;
}
2015-08-18 00:00:26 +00:00
/**
* Sets a list of trusted host patterns .
*
* You should only list the hosts you manage using regexs .
*
* @ param array $hostPatterns A list of trusted host patterns
*/
public static function setTrustedHosts ( array $hostPatterns )
{
self :: $trustedHostPatterns = array_map ( function ( $hostPattern ) {
2018-11-23 12:29:20 +00:00
return sprintf ( '{%s}i' , $hostPattern );
2015-08-18 00:00:26 +00:00
}, $hostPatterns );
// we need to reset trusted hosts on trusted host patterns change
self :: $trustedHosts = array ();
}
/**
* Gets the list of trusted host patterns .
*
2017-02-03 00:28:38 +00:00
* @ return array An array of trusted host patterns
2015-08-18 00:00:26 +00:00
*/
public static function getTrustedHosts ()
{
return self :: $trustedHostPatterns ;
}
/**
* Sets the name for trusted headers .
*
* The following header keys are supported :
*
* * Request :: HEADER_CLIENT_IP : defaults to X - Forwarded - For ( see getClientIp ())
* * Request :: HEADER_CLIENT_HOST : defaults to X - Forwarded - Host ( see getHost ())
* * Request :: HEADER_CLIENT_PORT : defaults to X - Forwarded - Port ( see getPort ())
* * Request :: HEADER_CLIENT_PROTO : defaults to X - Forwarded - Proto ( see getScheme () and isSecure ())
2017-04-13 14:53:35 +00:00
* * Request :: HEADER_FORWARDED : defaults to Forwarded ( see RFC 7239 )
2015-08-18 00:00:26 +00:00
*
* Setting an empty value allows to disable the trusted header for the given key .
*
* @ param string $key The header key
* @ param string $value The header name
*
* @ throws \InvalidArgumentException
2018-11-23 12:29:20 +00:00
*
* @ deprecated since version 3.3 , to be removed in 4.0 . Use the $trustedHeaderSet argument of the Request :: setTrustedProxies () method instead .
2015-08-18 00:00:26 +00:00
*/
public static function setTrustedHeaderName ( $key , $value )
{
2018-11-23 12:29:20 +00:00
@ 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 );
if ( 'forwarded' === $key ) {
$key = self :: HEADER_FORWARDED ;
} elseif ( 'client_ip' === $key ) {
$key = self :: HEADER_CLIENT_IP ;
} elseif ( 'client_host' === $key ) {
$key = self :: HEADER_CLIENT_HOST ;
} elseif ( 'client_proto' === $key ) {
$key = self :: HEADER_CLIENT_PROTO ;
} elseif ( 'client_port' === $key ) {
$key = self :: HEADER_CLIENT_PORT ;
} elseif ( ! array_key_exists ( $key , self :: $trustedHeaders )) {
2015-08-18 00:00:26 +00:00
throw new \InvalidArgumentException ( sprintf ( 'Unable to set the trusted header name for key "%s".' , $key ));
}
self :: $trustedHeaders [ $key ] = $value ;
2018-11-23 12:29:20 +00:00
if ( null !== $value ) {
self :: $trustedHeaderNames [ $key ] = $value ;
self :: $trustedHeaderSet |= $key ;
} else {
self :: $trustedHeaderSet &= ~ $key ;
}
2015-08-18 00:00:26 +00:00
}
/**
* Gets the trusted proxy header name .
*
* @ param string $key The header key
*
* @ return string The header name
*
* @ throws \InvalidArgumentException
2018-11-23 12:29:20 +00:00
*
* @ deprecated since version 3.3 , to be removed in 4.0 . Use the Request :: getTrustedHeaderSet () method instead .
2015-08-18 00:00:26 +00:00
*/
public static function getTrustedHeaderName ( $key )
{
2018-11-23 12:29:20 +00:00
if ( 2 > \func_num_args () || func_get_arg ( 1 )) {
@ 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 );
}
2015-08-18 00:00:26 +00:00
if ( ! array_key_exists ( $key , self :: $trustedHeaders )) {
throw new \InvalidArgumentException ( sprintf ( 'Unable to get the trusted header name for key "%s".' , $key ));
}
return self :: $trustedHeaders [ $key ];
}
/**
* Normalizes a query string .
*
* It builds a normalized query string , where keys / value pairs are alphabetized ,
* have consistent escaping and unneeded delimiters are removed .
*
* @ param string $qs Query string
*
* @ return string A normalized query string for the Request
*/
public static function normalizeQueryString ( $qs )
{
if ( '' == $qs ) {
return '' ;
}
$parts = array ();
$order = array ();
foreach ( explode ( '&' , $qs ) as $param ) {
if ( '' === $param || '=' === $param [ 0 ]) {
// Ignore useless delimiters, e.g. "x=y&".
// Also ignore pairs with empty key, even if there was a value, e.g. "=value", as such nameless values cannot be retrieved anyway.
// PHP also does not include them when building _GET.
continue ;
}
$keyValuePair = explode ( '=' , $param , 2 );
// GET parameters, that are submitted from a HTML form, encode spaces as "+" by default (as defined in enctype application/x-www-form-urlencoded).
// PHP also converts "+" to spaces when filling the global _GET or when using the function parse_str. This is why we use urldecode and then normalize to
// RFC 3986 with rawurlencode.
$parts [] = isset ( $keyValuePair [ 1 ]) ?
rawurlencode ( urldecode ( $keyValuePair [ 0 ])) . '=' . rawurlencode ( urldecode ( $keyValuePair [ 1 ])) :
rawurlencode ( urldecode ( $keyValuePair [ 0 ]));
$order [] = urldecode ( $keyValuePair [ 0 ]);
}
array_multisort ( $order , SORT_ASC , $parts );
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 .
* If the HTTP method parameter override is enabled , an html - form with method " POST " can be altered
* and used to send a " PUT " or " DELETE " request via the _method request parameter .
* If these methods are not protected against CSRF , this presents a possible vulnerability .
*
* 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 bool True when the _method request parameter is enabled , false otherwise
*/
public static function getHttpMethodParameterOverride ()
{
return self :: $httpMethodParameterOverride ;
}
/**
2018-11-23 12:29:20 +00:00
* Gets a " parameter " value from any bag .
2015-08-18 00:00:26 +00:00
*
2018-11-23 12:29:20 +00:00
* This method is mainly useful for libraries that want to provide some flexibility . If you don ' t need the
* flexibility in controllers , it is better to explicitly get request parameters from the appropriate
* public property instead ( attributes , query , request ) .
2015-08-18 00:00:26 +00:00
*
2018-11-23 12:29:20 +00:00
* Order of precedence : PATH ( routing placeholders or custom attributes ), GET , BODY
2015-08-18 00:00:26 +00:00
*
2018-11-23 12:29:20 +00:00
* @ param string $key The key
* @ param mixed $default The default value if the parameter key does not exist
2015-08-18 00:00:26 +00:00
*
* @ return mixed
*/
2018-11-23 12:29:20 +00:00
public function get ( $key , $default = null )
2015-08-18 00:00:26 +00:00
{
2018-11-23 12:29:20 +00:00
if ( $this !== $result = $this -> attributes -> get ( $key , $this )) {
2015-08-18 00:00:26 +00:00
return $result ;
}
2018-11-23 12:29:20 +00:00
if ( $this !== $result = $this -> query -> get ( $key , $this )) {
2015-08-18 00:00:26 +00:00
return $result ;
}
2018-11-23 12:29:20 +00:00
if ( $this !== $result = $this -> request -> get ( $key , $this )) {
2015-08-18 00:00:26 +00:00
return $result ;
}
return $default ;
}
/**
* Gets the Session .
*
* @ return SessionInterface | null The session
*/
public function getSession ()
{
return $this -> session ;
}
/**
* Whether the request contains a Session which was started in one of the
* previous requests .
*
* @ return bool
*/
public function hasPreviousSession ()
{
// the check for $this->session avoids malicious users trying to fake a session cookie with proper name
return $this -> hasSession () && $this -> cookies -> has ( $this -> session -> getName ());
}
/**
* Whether the request contains a Session object .
*
* This method does not give any information about the state of the session object ,
* like whether the session is started or not . It is just a way to check if this Request
* is associated with a Session instance .
*
* @ return bool true when the Request contains a Session object , false otherwise
*/
public function hasSession ()
{
return null !== $this -> session ;
}
/**
* Sets the Session .
*
* @ param SessionInterface $session The Session
*/
public function setSession ( SessionInterface $session )
{
$this -> session = $session ;
}
/**
* Returns the client IP addresses .
*
* In the returned array the most trusted IP address is first , and the
* least trusted one last . The " real " client IP address is the last one ,
* but this is also the least trusted one . Trusted proxies are stripped .
*
* Use this method carefully ; you should use getClientIp () instead .
*
* @ return array The client IP addresses
*
* @ see getClientIp ()
*/
public function getClientIps ()
{
$ip = $this -> server -> get ( 'REMOTE_ADDR' );
if ( ! $this -> isFromTrustedProxy ()) {
return array ( $ip );
}
2017-04-13 14:53:35 +00:00
return $this -> getTrustedValues ( self :: HEADER_CLIENT_IP , $ip ) ? : array ( $ip );
2015-08-18 00:00:26 +00:00
}
/**
* Returns the client IP address .
*
* This method can read the client IP address from the " X-Forwarded-For " header
* when trusted proxies were set via " setTrustedProxies() " . The " X-Forwarded-For "
* header value is a comma + space separated list of IP addresses , the left - most
* being the original client , and each successive proxy that passed the request
* adding the IP address where it received the request from .
*
* If your reverse proxy uses a different header name than " X-Forwarded-For " ,
2018-11-23 12:29:20 +00:00
* ( " Client-Ip " for instance ), configure it via the $trustedHeaderSet
* argument of the Request :: setTrustedProxies () method instead .
2015-08-18 00:00:26 +00:00
*
2017-07-03 15:47:07 +00:00
* @ return string | null The client IP address
2015-08-18 00:00:26 +00:00
*
* @ see getClientIps ()
* @ see http :// en . wikipedia . org / wiki / X - Forwarded - For
*/
public function getClientIp ()
{
$ipAddresses = $this -> getClientIps ();
return $ipAddresses [ 0 ];
}
/**
* Returns current script name .
*
* @ return string
*/
public function getScriptName ()
{
return $this -> server -> get ( 'SCRIPT_NAME' , $this -> server -> get ( 'ORIG_SCRIPT_NAME' , '' ));
}
/**
* Returns the path being requested relative to the executed script .
*
* The path info always starts with a /.
*
* Suppose this request is instantiated from / mysite on localhost :
*
* * http :// localhost / mysite returns an empty string
* * http :// localhost / mysite / about returns '/about'
* * http :// localhost / mysite / enco % 20 ded returns '/enco%20ded'
* * http :// localhost / mysite / about ? var = 1 returns '/about'
*
* @ return string The raw path ( i . e . not urldecoded )
*/
public function getPathInfo ()
{
if ( null === $this -> pathInfo ) {
$this -> pathInfo = $this -> preparePathInfo ();
}
return $this -> pathInfo ;
}
/**
* Returns the root path from which this request is executed .
*
* Suppose that an index . php file instantiates this request object :
*
* * http :// localhost / index . php returns an empty string
* * http :// localhost / index . php / page returns an empty string
* * http :// localhost / web / index . php returns '/web'
* * http :// localhost / we % 20 b / index . php returns '/we%20b'
*
* @ return string The raw path ( i . e . not urldecoded )
*/
public function getBasePath ()
{
if ( null === $this -> basePath ) {
$this -> basePath = $this -> prepareBasePath ();
}
return $this -> basePath ;
}
/**
* Returns the root URL from which this request is executed .
*
* The base URL never ends with a /.
*
* This is similar to getBasePath (), except that it also includes the
* script filename ( e . g . index . php ) if one exists .
*
* @ return string The raw URL ( i . e . not urldecoded )
*/
public function getBaseUrl ()
{
if ( null === $this -> baseUrl ) {
$this -> baseUrl = $this -> prepareBaseUrl ();
}
return $this -> baseUrl ;
}
/**
* Gets the request ' s scheme .
*
* @ return string
*/
public function getScheme ()
{
return $this -> isSecure () ? 'https' : 'http' ;
}
/**
* Returns the port on which the request is made .
*
* This method can read the client port from the " X-Forwarded-Port " header
* when trusted proxies were set via " setTrustedProxies() " .
*
* The " X-Forwarded-Port " header must contain the client port .
*
* If your reverse proxy uses a different header name than " X-Forwarded-Port " ,
2018-11-23 12:29:20 +00:00
* configure it via via the $trustedHeaderSet argument of the
* Request :: setTrustedProxies () method instead .
2015-08-18 00:00:26 +00:00
*
2017-07-03 15:47:07 +00:00
* @ return int | string can be a string if fetched from the server bag
2015-08-18 00:00:26 +00:00
*/
public function getPort ()
{
2017-04-13 14:53:35 +00:00
if ( $this -> isFromTrustedProxy () && $host = $this -> getTrustedValues ( self :: HEADER_CLIENT_PORT )) {
$host = $host [ 0 ];
} elseif ( $this -> isFromTrustedProxy () && $host = $this -> getTrustedValues ( self :: HEADER_CLIENT_HOST )) {
$host = $host [ 0 ];
} elseif ( ! $host = $this -> headers -> get ( 'HOST' )) {
return $this -> server -> get ( 'SERVER_PORT' );
2015-08-18 00:00:26 +00:00
}
2018-11-23 12:29:20 +00:00
if ( '[' === $host [ 0 ]) {
2017-04-13 14:53:35 +00:00
$pos = strpos ( $host , ':' , strrpos ( $host , ']' ));
} else {
$pos = strrpos ( $host , ':' );
}
2015-08-18 00:00:26 +00:00
2017-04-13 14:53:35 +00:00
if ( false !== $pos ) {
return ( int ) substr ( $host , $pos + 1 );
2015-08-18 00:00:26 +00:00
}
2017-04-13 14:53:35 +00:00
return 'https' === $this -> getScheme () ? 443 : 80 ;
2015-08-18 00:00:26 +00:00
}
/**
* Returns the user .
*
* @ return string | null
*/
public function getUser ()
{
return $this -> headers -> get ( 'PHP_AUTH_USER' );
}
/**
* Returns the password .
*
* @ return string | null
*/
public function getPassword ()
{
return $this -> headers -> get ( 'PHP_AUTH_PW' );
}
/**
* Gets the user info .
*
* @ return string A user name and , optionally , scheme - specific information about how to gain authorization to access the server
*/
public function getUserInfo ()
{
$userinfo = $this -> getUser ();
$pass = $this -> getPassword ();
if ( '' != $pass ) {
$userinfo .= " : $pass " ;
}
return $userinfo ;
}
/**
* Returns the HTTP host being requested .
*
* The port name will be appended to the host if it ' s non - standard .
*
* @ return string
*/
public function getHttpHost ()
{
$scheme = $this -> getScheme ();
$port = $this -> getPort ();
2018-11-23 12:29:20 +00:00
if (( 'http' == $scheme && 80 == $port ) || ( 'https' == $scheme && 443 == $port )) {
2015-08-18 00:00:26 +00:00
return $this -> getHost ();
}
return $this -> getHost () . ':' . $port ;
}
/**
* Returns the requested URI ( path and query string ) .
*
* @ return string The raw URI ( i . e . not URI decoded )
*/
public function getRequestUri ()
{
if ( null === $this -> requestUri ) {
$this -> requestUri = $this -> prepareRequestUri ();
}
return $this -> requestUri ;
}
/**
* Gets the scheme and HTTP host .
*
* If the URL was called with basic authentication , the user
* and the password are not added to the generated string .
*
* @ return string The scheme and HTTP host
*/
public function getSchemeAndHttpHost ()
{
return $this -> getScheme () . '://' . $this -> getHttpHost ();
}
/**
* Generates a normalized URI ( URL ) for the Request .
*
* @ return string A normalized URI ( URL ) for the Request
*
* @ see getQueryString ()
*/
public function getUri ()
{
if ( null !== $qs = $this -> getQueryString ()) {
$qs = '?' . $qs ;
}
return $this -> getSchemeAndHttpHost () . $this -> getBaseUrl () . $this -> getPathInfo () . $qs ;
}
/**
* Generates a normalized URI for the given path .
*
* @ param string $path A path to use instead of the current one
*
* @ return string The normalized URI for the path
*/
public function getUriForPath ( $path )
{
return $this -> getSchemeAndHttpHost () . $this -> getBaseUrl () . $path ;
}
/**
* Returns the path as relative reference from the current Request path .
*
* Only the URIs path component ( no schema , host etc . ) is relevant and must be given .
* Both paths must be absolute and not contain relative parts .
* Relative URLs from one resource to another are useful when generating self - contained downloadable document archives .
* Furthermore , they can be used to reduce the link size in documents .
*
* Example target paths , given a base path of " /a/b/c/d " :
* - " /a/b/c/d " -> " "
* - " /a/b/c/ " -> " ./ "
* - " /a/b/ " -> " ../ "
* - " /a/b/c/other " -> " other "
* - " /a/x/y " -> " ../../x/y "
*
* @ param string $path The target path
*
* @ return string The relative target path
*/
public function getRelativeUriForPath ( $path )
{
// be sure that we are dealing with an absolute path
if ( ! isset ( $path [ 0 ]) || '/' !== $path [ 0 ]) {
return $path ;
}
if ( $path === $basePath = $this -> getPathInfo ()) {
return '' ;
}
$sourceDirs = explode ( '/' , isset ( $basePath [ 0 ]) && '/' === $basePath [ 0 ] ? substr ( $basePath , 1 ) : $basePath );
2018-11-23 12:29:20 +00:00
$targetDirs = explode ( '/' , substr ( $path , 1 ));
2015-08-18 00:00:26 +00:00
array_pop ( $sourceDirs );
$targetFile = array_pop ( $targetDirs );
foreach ( $sourceDirs as $i => $dir ) {
if ( isset ( $targetDirs [ $i ]) && $dir === $targetDirs [ $i ]) {
unset ( $sourceDirs [ $i ], $targetDirs [ $i ]);
} else {
break ;
}
}
$targetDirs [] = $targetFile ;
2018-11-23 12:29:20 +00:00
$path = str_repeat ( '../' , \count ( $sourceDirs )) . implode ( '/' , $targetDirs );
2015-08-18 00:00:26 +00:00
// A reference to the same base directory or an empty subdirectory must be prefixed with "./".
// This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
// as the first segment of a relative-path reference, as it would be mistaken for a scheme name
// (see http://tools.ietf.org/html/rfc3986#section-4.2).
return ! isset ( $path [ 0 ]) || '/' === $path [ 0 ]
|| false !== ( $colonPos = strpos ( $path , ':' )) && ( $colonPos < ( $slashPos = strpos ( $path , '/' )) || false === $slashPos )
? " ./ $path " : $path ;
}
/**
* Generates the normalized query string for the Request .
*
* It builds a normalized query string , where keys / value pairs are alphabetized
* and have consistent escaping .
*
* @ return string | null A normalized query string for the Request
*/
public function getQueryString ()
{
$qs = static :: normalizeQueryString ( $this -> server -> get ( 'QUERY_STRING' ));
return '' === $qs ? null : $qs ;
}
/**
* Checks whether the request is secure or not .
*
2015-11-17 21:42:33 +00:00
* This method can read the client protocol from the " X-Forwarded-Proto " header
2015-08-18 00:00:26 +00:00
* when trusted proxies were set via " setTrustedProxies() " .
*
* The " X-Forwarded-Proto " header must contain the protocol : " https " or " http " .
*
* If your reverse proxy uses a different header name than " X-Forwarded-Proto "
2018-11-23 12:29:20 +00:00
* ( " SSL_HTTPS " for instance ), configure it via the $trustedHeaderSet
* argument of the Request :: setTrustedProxies () method instead .
2015-08-18 00:00:26 +00:00
*
* @ return bool
*/
public function isSecure ()
{
2017-04-13 14:53:35 +00:00
if ( $this -> isFromTrustedProxy () && $proto = $this -> getTrustedValues ( self :: HEADER_CLIENT_PROTO )) {
2018-11-23 12:29:20 +00:00
return \in_array ( strtolower ( $proto [ 0 ]), array ( 'https' , 'on' , 'ssl' , '1' ), true );
2015-08-18 00:00:26 +00:00
}
$https = $this -> server -> get ( 'HTTPS' );
return ! empty ( $https ) && 'off' !== strtolower ( $https );
}
/**
* Returns the host name .
*
2015-11-17 21:42:33 +00:00
* This method can read the client host name from the " X-Forwarded-Host " header
2015-08-18 00:00:26 +00:00
* when trusted proxies were set via " setTrustedProxies() " .
*
* The " X-Forwarded-Host " header must contain the client host name .
*
* If your reverse proxy uses a different header name than " X-Forwarded-Host " ,
2018-11-23 12:29:20 +00:00
* configure it via the $trustedHeaderSet argument of the
* Request :: setTrustedProxies () method instead .
2015-08-18 00:00:26 +00:00
*
* @ return string
*
2018-11-23 12:29:20 +00:00
* @ throws SuspiciousOperationException when the host name is invalid or not trusted
2015-08-18 00:00:26 +00:00
*/
public function getHost ()
{
2017-04-13 14:53:35 +00:00
if ( $this -> isFromTrustedProxy () && $host = $this -> getTrustedValues ( self :: HEADER_CLIENT_HOST )) {
$host = $host [ 0 ];
2015-08-18 00:00:26 +00:00
} elseif ( ! $host = $this -> headers -> get ( 'HOST' )) {
if ( ! $host = $this -> server -> get ( 'SERVER_NAME' )) {
$host = $this -> server -> get ( 'SERVER_ADDR' , '' );
}
}
// trim and remove port number from host
// host is lowercase as per RFC 952/2181
$host = strtolower ( preg_replace ( '/:\d+$/' , '' , trim ( $host )));
// as the host can come from the user (HTTP_HOST and depending on the configuration, SERVER_NAME too can come from the user)
// check that it does not contain forbidden characters (see RFC 952 and RFC 2181)
// use preg_replace() instead of preg_match() to prevent DoS attacks with long host names
if ( $host && '' !== preg_replace ( '/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/' , '' , $host )) {
2018-11-23 12:29:20 +00:00
if ( ! $this -> isHostValid ) {
return '' ;
}
$this -> isHostValid = false ;
throw new SuspiciousOperationException ( sprintf ( 'Invalid Host "%s".' , $host ));
2015-08-18 00:00:26 +00:00
}
2018-11-23 12:29:20 +00:00
if ( \count ( self :: $trustedHostPatterns ) > 0 ) {
2015-08-18 00:00:26 +00:00
// to avoid host header injection attacks, you should provide a list of trusted host patterns
2018-11-23 12:29:20 +00:00
if ( \in_array ( $host , self :: $trustedHosts )) {
2015-08-18 00:00:26 +00:00
return $host ;
}
foreach ( self :: $trustedHostPatterns as $pattern ) {
if ( preg_match ( $pattern , $host )) {
self :: $trustedHosts [] = $host ;
return $host ;
}
}
2018-11-23 12:29:20 +00:00
if ( ! $this -> isHostValid ) {
return '' ;
}
$this -> isHostValid = false ;
throw new SuspiciousOperationException ( sprintf ( 'Untrusted Host "%s".' , $host ));
2015-08-18 00:00:26 +00:00
}
return $host ;
}
/**
* Sets the request method .
*
* @ param string $method
*/
public function setMethod ( $method )
{
$this -> method = null ;
$this -> server -> set ( 'REQUEST_METHOD' , $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
*
* @ see getRealMethod ()
*/
public function getMethod ()
{
if ( null === $this -> method ) {
$this -> method = strtoupper ( $this -> server -> get ( 'REQUEST_METHOD' , 'GET' ));
if ( 'POST' === $this -> method ) {
if ( $method = $this -> headers -> get ( 'X-HTTP-METHOD-OVERRIDE' )) {
$this -> method = strtoupper ( $method );
} elseif ( self :: $httpMethodParameterOverride ) {
2018-11-23 12:29:20 +00:00
$method = $this -> request -> get ( '_method' , $this -> query -> get ( '_method' , 'POST' ));
if ( \is_string ( $method )) {
$this -> method = strtoupper ( $method );
}
2015-08-18 00:00:26 +00:00
}
}
}
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 .
*
* @ param string $format The format
*
2018-11-23 12:29:20 +00:00
* @ return string | null The associated mime type ( null if not found )
2015-08-18 00:00:26 +00:00
*/
public function getMimeType ( $format )
{
if ( null === static :: $formats ) {
static :: initializeFormats ();
}
return isset ( static :: $formats [ $format ]) ? static :: $formats [ $format ][ 0 ] : null ;
}
2018-11-23 12:29:20 +00:00
/**
* Gets the mime types associated with the format .
*
* @ param string $format The format
*
* @ return array The associated mime types
*/
public static function getMimeTypes ( $format )
{
if ( null === static :: $formats ) {
static :: initializeFormats ();
}
return isset ( static :: $formats [ $format ]) ? static :: $formats [ $format ] : array ();
}
2015-08-18 00:00:26 +00:00
/**
* Gets the format associated with the mime type .
*
* @ param string $mimeType The associated mime type
*
* @ return string | null The format ( null if not found )
*/
public function getFormat ( $mimeType )
{
2016-04-20 16:56:34 +00:00
$canonicalMimeType = null ;
2015-08-18 00:00:26 +00:00
if ( false !== $pos = strpos ( $mimeType , ';' )) {
2019-01-24 08:00:03 +00:00
$canonicalMimeType = trim ( substr ( $mimeType , 0 , $pos ));
2015-08-18 00:00:26 +00:00
}
if ( null === static :: $formats ) {
static :: initializeFormats ();
}
foreach ( static :: $formats as $format => $mimeTypes ) {
2018-11-23 12:29:20 +00:00
if ( \in_array ( $mimeType , ( array ) $mimeTypes )) {
2015-08-18 00:00:26 +00:00
return $format ;
}
2018-11-23 12:29:20 +00:00
if ( null !== $canonicalMimeType && \in_array ( $canonicalMimeType , ( array ) $mimeTypes )) {
2016-04-20 16:56:34 +00:00
return $format ;
}
2015-08-18 00:00:26 +00:00
}
}
/**
* Associates a format with mime types .
*
* @ param string $format The format
* @ param string | array $mimeTypes The associated mime types ( the preferred one must be the first as it will be used as the content type )
*/
public function setFormat ( $format , $mimeTypes )
{
if ( null === static :: $formats ) {
static :: initializeFormats ();
}
2018-11-23 12:29:20 +00:00
static :: $formats [ $format ] = \is_array ( $mimeTypes ) ? $mimeTypes : array ( $mimeTypes );
2015-08-18 00:00:26 +00:00
}
/**
* Gets the request format .
*
* Here is the process to determine the format :
*
* * format defined by the user ( with setRequestFormat ())
2018-11-23 12:29:20 +00:00
* * _format request attribute
2015-08-18 00:00:26 +00:00
* * $default
*
2018-11-23 12:29:20 +00:00
* @ param string | null $default The default format
2015-08-18 00:00:26 +00:00
*
* @ return string The request format
*/
public function getRequestFormat ( $default = 'html' )
{
if ( null === $this -> format ) {
2018-11-23 12:29:20 +00:00
$this -> format = $this -> attributes -> get ( '_format' );
2015-08-18 00:00:26 +00:00
}
2017-04-13 14:53:35 +00:00
return null === $this -> format ? $default : $this -> format ;
2015-08-18 00:00:26 +00:00
}
/**
* Sets the request format .
*
2017-02-03 00:28:38 +00:00
* @ param string $format The request format
2015-08-18 00:00:26 +00:00
*/
public function setRequestFormat ( $format )
{
$this -> format = $format ;
}
/**
* Gets the format associated with the request .
*
* @ return string | null The format ( null if no content type is present )
*/
public function getContentType ()
{
return $this -> getFormat ( $this -> headers -> get ( 'CONTENT_TYPE' ));
}
/**
* Sets the default locale .
*
* @ param string $locale
*/
public function setDefaultLocale ( $locale )
{
$this -> defaultLocale = $locale ;
if ( null === $this -> locale ) {
$this -> setPhpDefaultLocale ( $locale );
}
}
/**
* Get the default locale .
*
* @ return string
*/
public function getDefaultLocale ()
{
return $this -> defaultLocale ;
}
/**
* Sets the locale .
*
* @ param string $locale
*/
public function setLocale ( $locale )
{
$this -> setPhpDefaultLocale ( $this -> locale = $locale );
}
/**
* Get the locale .
*
* @ return string
*/
public function getLocale ()
{
return null === $this -> locale ? $this -> defaultLocale : $this -> locale ;
}
/**
* Checks if the request method is of specified type .
*
2017-02-03 00:28:38 +00:00
* @ param string $method Uppercase request method ( GET , POST etc )
2015-08-18 00:00:26 +00:00
*
* @ return bool
*/
public function isMethod ( $method )
{
return $this -> getMethod () === strtoupper ( $method );
}
/**
2018-11-23 12:29:20 +00:00
* Checks whether or not the method is safe .
2015-08-18 00:00:26 +00:00
*
2017-02-03 00:28:38 +00:00
* @ see https :// tools . ietf . org / html / rfc7231 #section-4.2.1
*
* @ param bool $andCacheable Adds the additional condition that the method should be cacheable . True by default .
*
* @ return bool
*/
public function isMethodSafe ( /* $andCacheable = true */ )
{
2018-11-23 12:29:20 +00:00
if ( ! \func_num_args () || func_get_arg ( 0 )) {
// This deprecation should be turned into a BadMethodCallException in 4.0 (without adding the argument in the signature)
// then setting $andCacheable to false should be deprecated in 4.1
@ 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 );
return \in_array ( $this -> getMethod (), array ( 'GET' , 'HEAD' ));
}
return \in_array ( $this -> getMethod (), array ( 'GET' , 'HEAD' , 'OPTIONS' , 'TRACE' ));
}
/**
* Checks whether or not the method is idempotent .
*
* @ return bool
*/
public function isMethodIdempotent ()
{
return \in_array ( $this -> getMethod (), array ( 'HEAD' , 'GET' , 'PUT' , 'DELETE' , 'TRACE' , 'OPTIONS' , 'PURGE' ));
2017-02-03 00:28:38 +00:00
}
/**
* Checks whether the method is cacheable or not .
*
* @ see https :// tools . ietf . org / html / rfc7231 #section-4.2.3
*
2019-01-24 08:00:03 +00:00
* @ return bool True for GET and HEAD , false otherwise
2015-08-18 00:00:26 +00:00
*/
2017-02-03 00:28:38 +00:00
public function isMethodCacheable ()
2015-08-18 00:00:26 +00:00
{
2018-11-23 12:29:20 +00:00
return \in_array ( $this -> getMethod (), array ( 'GET' , 'HEAD' ));
}
/**
* Returns the protocol version .
*
* If the application is behind a proxy , the protocol version used in the
* requests between the client and the proxy and between the proxy and the
* server might be different . This returns the former ( from the " Via " header )
* if the proxy is trusted ( see " setTrustedProxies() " ), otherwise it returns
* the latter ( from the " SERVER_PROTOCOL " server parameter ) .
*
* @ return string
*/
public function getProtocolVersion ()
{
if ( $this -> isFromTrustedProxy ()) {
preg_match ( '~^(HTTP/)?([1-9]\.[0-9]) ~' , $this -> headers -> get ( 'Via' ), $matches );
if ( $matches ) {
return 'HTTP/' . $matches [ 2 ];
}
}
return $this -> server -> get ( 'SERVER_PROTOCOL' );
2015-08-18 00:00:26 +00:00
}
/**
* Returns the request body content .
*
* @ param bool $asResource If true , a resource will be returned
*
2017-02-03 00:28:38 +00:00
* @ return string | resource The request body content or a resource to read the body stream
2015-08-18 00:00:26 +00:00
*
* @ throws \LogicException
*/
public function getContent ( $asResource = false )
{
2018-11-23 12:29:20 +00:00
$currentContentIsResource = \is_resource ( $this -> content );
2017-07-03 15:47:07 +00:00
if ( \PHP_VERSION_ID < 50600 && false === $this -> content ) {
2015-08-27 19:03:05 +00:00
throw new \LogicException ( 'getContent() can only be called once when using the resource return type and PHP below 5.6.' );
2015-08-18 00:00:26 +00:00
}
if ( true === $asResource ) {
2015-09-04 20:20:09 +00:00
if ( $currentContentIsResource ) {
rewind ( $this -> content );
return $this -> content ;
}
// Content passed in parameter (test)
2018-11-23 12:29:20 +00:00
if ( \is_string ( $this -> content )) {
2015-10-08 18:40:12 +00:00
$resource = fopen ( 'php://temp' , 'r+' );
2015-09-04 20:20:09 +00:00
fwrite ( $resource , $this -> content );
rewind ( $resource );
return $resource ;
}
2015-08-18 00:00:26 +00:00
$this -> content = false ;
return fopen ( 'php://input' , 'rb' );
}
2015-09-04 20:20:09 +00:00
if ( $currentContentIsResource ) {
rewind ( $this -> content );
return stream_get_contents ( $this -> content );
}
2017-02-03 00:28:38 +00:00
if ( null === $this -> content || false === $this -> content ) {
2015-08-18 00:00:26 +00:00
$this -> content = file_get_contents ( 'php://input' );
}
return $this -> content ;
}
/**
* Gets the Etags .
*
* @ return array The entity tags
*/
public function getETags ()
{
return preg_split ( '/\s*,\s*/' , $this -> headers -> get ( 'if_none_match' ), null , PREG_SPLIT_NO_EMPTY );
}
/**
* @ return bool
*/
public function isNoCache ()
{
return $this -> headers -> hasCacheControlDirective ( 'no-cache' ) || 'no-cache' == $this -> headers -> get ( 'Pragma' );
}
/**
* Returns the preferred language .
*
* @ param array $locales An array of ordered available locales
*
* @ return string | null The preferred locale
*/
public function getPreferredLanguage ( array $locales = null )
{
$preferredLanguages = $this -> getLanguages ();
if ( empty ( $locales )) {
return isset ( $preferredLanguages [ 0 ]) ? $preferredLanguages [ 0 ] : null ;
}
if ( ! $preferredLanguages ) {
return $locales [ 0 ];
}
$extendedPreferredLanguages = array ();
foreach ( $preferredLanguages as $language ) {
$extendedPreferredLanguages [] = $language ;
if ( false !== $position = strpos ( $language , '_' )) {
$superLanguage = substr ( $language , 0 , $position );
2018-11-23 12:29:20 +00:00
if ( ! \in_array ( $superLanguage , $preferredLanguages )) {
2015-08-18 00:00:26 +00:00
$extendedPreferredLanguages [] = $superLanguage ;
}
}
}
$preferredLanguages = array_values ( array_intersect ( $extendedPreferredLanguages , $locales ));
return isset ( $preferredLanguages [ 0 ]) ? $preferredLanguages [ 0 ] : $locales [ 0 ];
}
/**
* Gets a list of languages acceptable by the client browser .
*
* @ return array Languages ordered in the user browser preferences
*/
public function getLanguages ()
{
if ( null !== $this -> languages ) {
return $this -> languages ;
}
$languages = AcceptHeader :: fromString ( $this -> headers -> get ( 'Accept-Language' )) -> all ();
$this -> languages = array ();
foreach ( $languages as $lang => $acceptHeaderItem ) {
if ( false !== strpos ( $lang , '-' )) {
$codes = explode ( '-' , $lang );
if ( 'i' === $codes [ 0 ]) {
// Language not listed in ISO 639 that are not variants
// of any listed language, which can be registered with the
// i-prefix, such as i-cherokee
2018-11-23 12:29:20 +00:00
if ( \count ( $codes ) > 1 ) {
2015-08-18 00:00:26 +00:00
$lang = $codes [ 1 ];
}
} else {
2018-11-23 12:29:20 +00:00
for ( $i = 0 , $max = \count ( $codes ); $i < $max ; ++ $i ) {
if ( 0 === $i ) {
2015-08-18 00:00:26 +00:00
$lang = strtolower ( $codes [ 0 ]);
} else {
$lang .= '_' . strtoupper ( $codes [ $i ]);
}
}
}
}
$this -> languages [] = $lang ;
}
return $this -> languages ;
}
/**
* Gets a list of charsets acceptable by the client browser .
*
* @ return array List of charsets in preferable order
*/
public function getCharsets ()
{
if ( null !== $this -> charsets ) {
return $this -> charsets ;
}
return $this -> charsets = array_keys ( AcceptHeader :: fromString ( $this -> headers -> get ( 'Accept-Charset' )) -> all ());
}
/**
* Gets a list of encodings acceptable by the client browser .
*
* @ return array List of encodings in preferable order
*/
public function getEncodings ()
{
if ( null !== $this -> encodings ) {
return $this -> encodings ;
}
return $this -> encodings = array_keys ( AcceptHeader :: fromString ( $this -> headers -> get ( 'Accept-Encoding' )) -> all ());
}
/**
* Gets a list of content types acceptable by the client browser .
*
* @ return array List of content types in preferable order
*/
public function getAcceptableContentTypes ()
{
if ( null !== $this -> acceptableContentTypes ) {
return $this -> acceptableContentTypes ;
}
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 sets an X - Requested - With HTTP header .
* It is known to work with common JavaScript frameworks :
*
2017-02-03 00:28:38 +00:00
* @ see http :// en . wikipedia . org / wiki / List_of_Ajax_frameworks #JavaScript
2015-08-18 00:00:26 +00:00
*
* @ return bool true if the request is an XMLHttpRequest , false otherwise
*/
public function isXmlHttpRequest ()
{
return 'XMLHttpRequest' == $this -> headers -> get ( 'X-Requested-With' );
}
/*
* The following methods are derived from code of the Zend Framework ( 1.10 dev - 2010 - 01 - 24 )
*
* Code subject to the new BSD license ( http :// framework . zend . com / license / new - bsd ) .
*
* Copyright ( c ) 2005 - 2010 Zend Technologies USA Inc . ( http :// www . zend . com )
*/
protected function prepareRequestUri ()
{
$requestUri = '' ;
2018-11-23 12:29:20 +00:00
if ( '1' == $this -> server -> get ( 'IIS_WasUrlRewritten' ) && '' != $this -> server -> get ( 'UNENCODED_URL' )) {
2015-08-18 00:00:26 +00:00
// 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' );
2019-01-24 08:00:03 +00:00
if ( '' !== $requestUri && '/' === $requestUri [ 0 ]) {
// To only use path and query remove the fragment.
if ( false !== $pos = strpos ( $requestUri , '#' )) {
$requestUri = substr ( $requestUri , 0 , $pos );
}
} else {
// HTTP proxy reqs setup request URI with scheme and host [and port] + the URL path,
// only use URL path.
$uriComponents = parse_url ( $requestUri );
if ( isset ( $uriComponents [ 'path' ])) {
$requestUri = $uriComponents [ 'path' ];
}
if ( isset ( $uriComponents [ 'query' ])) {
$requestUri .= '?' . $uriComponents [ 'query' ];
}
2015-08-18 00:00:26 +00:00
}
} elseif ( $this -> server -> has ( 'ORIG_PATH_INFO' )) {
// IIS 5.0, PHP as CGI
$requestUri = $this -> server -> get ( 'ORIG_PATH_INFO' );
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 ;
}
/**
* Prepares the base URL .
*
* @ return string
*/
protected function prepareBaseUrl ()
{
$filename = basename ( $this -> server -> get ( 'SCRIPT_FILENAME' ));
if ( basename ( $this -> server -> get ( 'SCRIPT_NAME' )) === $filename ) {
$baseUrl = $this -> server -> get ( 'SCRIPT_NAME' );
} elseif ( basename ( $this -> server -> get ( 'PHP_SELF' )) === $filename ) {
$baseUrl = $this -> server -> get ( 'PHP_SELF' );
} elseif ( basename ( $this -> server -> get ( 'ORIG_SCRIPT_NAME' )) === $filename ) {
$baseUrl = $this -> server -> get ( 'ORIG_SCRIPT_NAME' ); // 1and1 shared hosting compatibility
} else {
// Backtrack up the script_filename to find the portion matching
// php_self
$path = $this -> server -> get ( 'PHP_SELF' , '' );
$file = $this -> server -> get ( 'SCRIPT_FILENAME' , '' );
$segs = explode ( '/' , trim ( $file , '/' ));
$segs = array_reverse ( $segs );
$index = 0 ;
2018-11-23 12:29:20 +00:00
$last = \count ( $segs );
2015-08-18 00:00:26 +00:00
$baseUrl = '' ;
do {
$seg = $segs [ $index ];
$baseUrl = '/' . $seg . $baseUrl ;
++ $index ;
} while ( $last > $index && ( false !== $pos = strpos ( $path , $baseUrl )) && 0 != $pos );
}
// Does the baseUrl have anything in common with the request_uri?
$requestUri = $this -> getRequestUri ();
2018-11-23 12:29:20 +00:00
if ( '' !== $requestUri && '/' !== $requestUri [ 0 ]) {
2017-04-13 14:53:35 +00:00
$requestUri = '/' . $requestUri ;
}
2015-08-18 00:00:26 +00:00
if ( $baseUrl && false !== $prefix = $this -> getUrlencodedPrefix ( $requestUri , $baseUrl )) {
// full $baseUrl matches
return $prefix ;
}
2018-11-23 12:29:20 +00:00
if ( $baseUrl && false !== $prefix = $this -> getUrlencodedPrefix ( $requestUri , rtrim ( \dirname ( $baseUrl ), '/' . \DIRECTORY_SEPARATOR ) . '/' )) {
2015-08-18 00:00:26 +00:00
// directory portion of $baseUrl matches
2018-11-23 12:29:20 +00:00
return rtrim ( $prefix , '/' . \DIRECTORY_SEPARATOR );
2015-08-18 00:00:26 +00:00
}
$truncatedRequestUri = $requestUri ;
if ( false !== $pos = strpos ( $requestUri , '?' )) {
$truncatedRequestUri = substr ( $requestUri , 0 , $pos );
}
$basename = basename ( $baseUrl );
if ( empty ( $basename ) || ! strpos ( rawurldecode ( $truncatedRequestUri ), $basename )) {
// no match whatsoever; set it blank
return '' ;
}
// If using mod_rewrite or ISAPI_Rewrite strip the script filename
// out of baseUrl. $pos !== 0 makes sure it is not matching a value
// from PATH_INFO or QUERY_STRING
2018-11-23 12:29:20 +00:00
if ( \strlen ( $requestUri ) >= \strlen ( $baseUrl ) && ( false !== $pos = strpos ( $requestUri , $baseUrl )) && 0 !== $pos ) {
$baseUrl = substr ( $requestUri , 0 , $pos + \strlen ( $baseUrl ));
2015-08-18 00:00:26 +00:00
}
2018-11-23 12:29:20 +00:00
return rtrim ( $baseUrl , '/' . \DIRECTORY_SEPARATOR );
2015-08-18 00:00:26 +00:00
}
/**
* Prepares the base path .
*
* @ return string base path
*/
protected function prepareBasePath ()
{
$baseUrl = $this -> getBaseUrl ();
if ( empty ( $baseUrl )) {
return '' ;
}
2018-11-23 12:29:20 +00:00
$filename = basename ( $this -> server -> get ( 'SCRIPT_FILENAME' ));
2015-08-18 00:00:26 +00:00
if ( basename ( $baseUrl ) === $filename ) {
2018-11-23 12:29:20 +00:00
$basePath = \dirname ( $baseUrl );
2015-08-18 00:00:26 +00:00
} else {
$basePath = $baseUrl ;
}
2018-11-23 12:29:20 +00:00
if ( '\\' === \DIRECTORY_SEPARATOR ) {
2015-08-18 00:00:26 +00:00
$basePath = str_replace ( '\\' , '/' , $basePath );
}
return rtrim ( $basePath , '/' );
}
/**
* Prepares the path info .
*
* @ return string path info
*/
protected function preparePathInfo ()
{
if ( null === ( $requestUri = $this -> getRequestUri ())) {
return '/' ;
}
// Remove the query string from REQUEST_URI
2017-04-13 14:53:35 +00:00
if ( false !== $pos = strpos ( $requestUri , '?' )) {
2015-08-18 00:00:26 +00:00
$requestUri = substr ( $requestUri , 0 , $pos );
}
2018-11-23 12:29:20 +00:00
if ( '' !== $requestUri && '/' !== $requestUri [ 0 ]) {
2017-04-13 14:53:35 +00:00
$requestUri = '/' . $requestUri ;
}
2015-08-18 00:00:26 +00:00
2018-11-23 12:29:20 +00:00
if ( null === ( $baseUrl = $this -> getBaseUrl ())) {
return $requestUri ;
}
$pathInfo = substr ( $requestUri , \strlen ( $baseUrl ));
if ( false === $pathInfo || '' === $pathInfo ) {
2015-08-18 00:00:26 +00:00
// If substr() returns false then PATH_INFO is set to an empty string
return '/' ;
}
return ( string ) $pathInfo ;
}
/**
* Initializes HTTP request formats .
*/
protected static function initializeFormats ()
{
static :: $formats = array (
'html' => array ( 'text/html' , 'application/xhtml+xml' ),
'txt' => array ( 'text/plain' ),
'js' => array ( 'application/javascript' , 'application/x-javascript' , 'text/javascript' ),
'css' => array ( 'text/css' ),
'json' => array ( 'application/json' , 'application/x-json' ),
2018-11-23 12:29:20 +00:00
'jsonld' => array ( 'application/ld+json' ),
2015-08-18 00:00:26 +00:00
'xml' => array ( 'text/xml' , 'application/xml' , 'application/x-xml' ),
'rdf' => array ( 'application/rdf+xml' ),
'atom' => array ( 'application/atom+xml' ),
'rss' => array ( 'application/rss+xml' ),
'form' => array ( 'application/x-www-form-urlencoded' ),
);
}
/**
* Sets the default PHP locale .
*
* @ param string $locale
*/
private function setPhpDefaultLocale ( $locale )
{
// if either the class Locale doesn't exist, or an exception is thrown when
// setting the default locale, the intl module is not installed, and
// the call can be ignored:
try {
if ( class_exists ( 'Locale' , false )) {
\Locale :: setDefault ( $locale );
}
} catch ( \Exception $e ) {
}
}
/*
* Returns the prefix as encoded in the string when the string starts with
* the given prefix , false otherwise .
*
* @ param string $string The urlencoded string
* @ param string $prefix The prefix not encoded
*
* @ return string | false The prefix as it is encoded in $string , or false
*/
private function getUrlencodedPrefix ( $string , $prefix )
{
if ( 0 !== strpos ( rawurldecode ( $string ), $prefix )) {
return false ;
}
2018-11-23 12:29:20 +00:00
$len = \strlen ( $prefix );
2015-08-18 00:00:26 +00:00
if ( preg_match ( sprintf ( '#^(%%[[:xdigit:]]{2}|.){%d}#' , $len ), $string , $match )) {
return $match [ 0 ];
}
return false ;
}
private static function createRequestFromFactory ( array $query = array (), array $request = array (), array $attributes = array (), array $cookies = array (), array $files = array (), array $server = array (), $content = null )
{
if ( self :: $requestFactory ) {
2018-11-23 12:29:20 +00:00
$request = \call_user_func ( self :: $requestFactory , $query , $request , $attributes , $cookies , $files , $server , $content );
2015-08-18 00:00:26 +00:00
2015-10-08 18:40:12 +00:00
if ( ! $request instanceof self ) {
2015-08-18 00:00:26 +00:00
throw new \LogicException ( 'The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.' );
}
return $request ;
}
return new static ( $query , $request , $attributes , $cookies , $files , $server , $content );
}
2018-11-23 12:29:20 +00:00
/**
* Indicates whether this request originated from a trusted proxy .
*
* This can be useful to determine whether or not to trust the
* contents of a proxy - specific header .
*
* @ return bool true if the request came from a trusted proxy , false otherwise
*/
public function isFromTrustedProxy ()
2015-08-18 00:00:26 +00:00
{
return self :: $trustedProxies && IpUtils :: checkIp ( $this -> server -> get ( 'REMOTE_ADDR' ), self :: $trustedProxies );
}
2017-02-03 00:28:38 +00:00
2017-04-13 14:53:35 +00:00
private function getTrustedValues ( $type , $ip = null )
{
$clientValues = array ();
$forwardedValues = array ();
if ( self :: $trustedHeaders [ $type ] && $this -> headers -> has ( self :: $trustedHeaders [ $type ])) {
foreach ( explode ( ',' , $this -> headers -> get ( self :: $trustedHeaders [ $type ])) as $v ) {
$clientValues [] = ( self :: HEADER_CLIENT_PORT === $type ? '0.0.0.0:' : '' ) . trim ( $v );
}
}
if ( self :: $trustedHeaders [ self :: HEADER_FORWARDED ] && $this -> headers -> has ( self :: $trustedHeaders [ self :: HEADER_FORWARDED ])) {
$forwardedValues = $this -> headers -> get ( self :: $trustedHeaders [ self :: HEADER_FORWARDED ]);
2018-11-23 12:29:20 +00:00
$forwardedValues = preg_match_all ( sprintf ( '{(?:%s)="?([a-zA-Z0-9\.:_\-/\[\]]*+)}' , self :: $forwardedParams [ $type ]), $forwardedValues , $matches ) ? $matches [ 1 ] : array ();
if ( self :: HEADER_CLIENT_PORT === $type ) {
foreach ( $forwardedValues as $k => $v ) {
if ( ']' === substr ( $v , - 1 ) || false === $v = strrchr ( $v , ':' )) {
$v = $this -> isSecure () ? ':443' : ':80' ;
}
$forwardedValues [ $k ] = '0.0.0.0' . $v ;
}
}
2017-04-13 14:53:35 +00:00
}
if ( null !== $ip ) {
$clientValues = $this -> normalizeAndFilterClientIps ( $clientValues , $ip );
$forwardedValues = $this -> normalizeAndFilterClientIps ( $forwardedValues , $ip );
}
if ( $forwardedValues === $clientValues || ! $clientValues ) {
return $forwardedValues ;
}
if ( ! $forwardedValues ) {
return $clientValues ;
}
if ( ! $this -> isForwardedValid ) {
return null !== $ip ? array ( '0.0.0.0' , $ip ) : array ();
}
$this -> isForwardedValid = false ;
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 ]));
}
2017-02-03 00:28:38 +00:00
private function normalizeAndFilterClientIps ( array $clientIps , $ip )
{
2017-04-13 14:53:35 +00:00
if ( ! $clientIps ) {
return array ();
}
2017-02-03 00:28:38 +00:00
$clientIps [] = $ip ; // Complete the IP chain with the IP the request actually came from
$firstTrustedIp = null ;
foreach ( $clientIps as $key => $clientIp ) {
2018-11-23 12:29:20 +00:00
if ( strpos ( $clientIp , '.' )) {
// Strip :port from IPv4 addresses. This is allowed in Forwarded
// and may occur in X-Forwarded-For.
$i = strpos ( $clientIp , ':' );
if ( $i ) {
$clientIps [ $key ] = $clientIp = substr ( $clientIp , 0 , $i );
}
} elseif ( 0 === strpos ( $clientIp , '[' )) {
// Strip brackets and :port from IPv6 addresses.
$i = strpos ( $clientIp , ']' , 1 );
$clientIps [ $key ] = $clientIp = substr ( $clientIp , 1 , $i - 1 );
2017-02-03 00:28:38 +00:00
}
if ( ! filter_var ( $clientIp , FILTER_VALIDATE_IP )) {
unset ( $clientIps [ $key ]);
continue ;
}
if ( IpUtils :: checkIp ( $clientIp , self :: $trustedProxies )) {
unset ( $clientIps [ $key ]);
// Fallback to this when the client IP falls into the range of trusted proxies
if ( null === $firstTrustedIp ) {
$firstTrustedIp = $clientIp ;
}
}
}
// Now the IP chain contains only untrusted proxies and the client IP
return $clientIps ? array_reverse ( $clientIps ) : array ( $firstTrustedIp );
}
2015-08-18 00:00:26 +00:00
}