vendor/symfony/src/Symfony/Component/HttpFoundation/Request.php
changeset 0 7f95f8617b0b
equal deleted inserted replaced
-1:000000000000 0:7f95f8617b0b
       
     1 <?php
       
     2 
       
     3 /*
       
     4  * This file is part of the Symfony package.
       
     5  *
       
     6  * (c) Fabien Potencier <fabien@symfony.com>
       
     7  *
       
     8  * For the full copyright and license information, please view the LICENSE
       
     9  * file that was distributed with this source code.
       
    10  */
       
    11 
       
    12 namespace Symfony\Component\HttpFoundation;
       
    13 
       
    14 use Symfony\Component\HttpFoundation\SessionStorage\NativeSessionStorage;
       
    15 
       
    16 /**
       
    17  * Request represents an HTTP request.
       
    18  *
       
    19  * @author Fabien Potencier <fabien@symfony.com>
       
    20  *
       
    21  * @api
       
    22  */
       
    23 class Request
       
    24 {
       
    25     static protected $trustProxy = false;
       
    26 
       
    27     /**
       
    28      * @var \Symfony\Component\HttpFoundation\ParameterBag
       
    29      *
       
    30      * @api
       
    31      */
       
    32     public $attributes;
       
    33 
       
    34     /**
       
    35      * @var \Symfony\Component\HttpFoundation\ParameterBag
       
    36      *
       
    37      * @api
       
    38      */
       
    39     public $request;
       
    40 
       
    41     /**
       
    42      * @var \Symfony\Component\HttpFoundation\ParameterBag
       
    43      *
       
    44      * @api
       
    45      */
       
    46     public $query;
       
    47 
       
    48     /**
       
    49      * @var \Symfony\Component\HttpFoundation\ParameterBag
       
    50      *
       
    51      * @api
       
    52      */
       
    53     public $server;
       
    54 
       
    55     /**
       
    56      * @var \Symfony\Component\HttpFoundation\ParameterBag
       
    57      *
       
    58      * @api
       
    59      */
       
    60     public $files;
       
    61 
       
    62     /**
       
    63      * @var \Symfony\Component\HttpFoundation\ParameterBag
       
    64      *
       
    65      * @api
       
    66      */
       
    67     public $cookies;
       
    68 
       
    69     /**
       
    70      * @var \Symfony\Component\HttpFoundation\HeaderBag
       
    71      *
       
    72      * @api
       
    73      */
       
    74     public $headers;
       
    75 
       
    76     protected $content;
       
    77     protected $languages;
       
    78     protected $charsets;
       
    79     protected $acceptableContentTypes;
       
    80     protected $pathInfo;
       
    81     protected $requestUri;
       
    82     protected $baseUrl;
       
    83     protected $basePath;
       
    84     protected $method;
       
    85     protected $format;
       
    86     protected $session;
       
    87 
       
    88     static protected $formats;
       
    89 
       
    90     /**
       
    91      * Constructor.
       
    92      *
       
    93      * @param array  $query      The GET parameters
       
    94      * @param array  $request    The POST parameters
       
    95      * @param array  $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
       
    96      * @param array  $cookies    The COOKIE parameters
       
    97      * @param array  $files      The FILES parameters
       
    98      * @param array  $server     The SERVER parameters
       
    99      * @param string $content    The raw body data
       
   100      *
       
   101      * @api
       
   102      */
       
   103     public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
       
   104     {
       
   105         $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content);
       
   106     }
       
   107 
       
   108     /**
       
   109      * Sets the parameters for this request.
       
   110      *
       
   111      * This method also re-initializes all properties.
       
   112      *
       
   113      * @param array  $query      The GET parameters
       
   114      * @param array  $request    The POST parameters
       
   115      * @param array  $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
       
   116      * @param array  $cookies    The COOKIE parameters
       
   117      * @param array  $files      The FILES parameters
       
   118      * @param array  $server     The SERVER parameters
       
   119      * @param string $content    The raw body data
       
   120      *
       
   121      * @api
       
   122      */
       
   123     public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
       
   124     {
       
   125         $this->request = new ParameterBag($request);
       
   126         $this->query = new ParameterBag($query);
       
   127         $this->attributes = new ParameterBag($attributes);
       
   128         $this->cookies = new ParameterBag($cookies);
       
   129         $this->files = new FileBag($files);
       
   130         $this->server = new ServerBag($server);
       
   131         $this->headers = new HeaderBag($this->server->getHeaders());
       
   132 
       
   133         $this->content = $content;
       
   134         $this->languages = null;
       
   135         $this->charsets = null;
       
   136         $this->acceptableContentTypes = null;
       
   137         $this->pathInfo = null;
       
   138         $this->requestUri = null;
       
   139         $this->baseUrl = null;
       
   140         $this->basePath = null;
       
   141         $this->method = null;
       
   142         $this->format = null;
       
   143     }
       
   144 
       
   145     /**
       
   146      * Creates a new request with values from PHP's super globals.
       
   147      *
       
   148      * @return Request A new request
       
   149      *
       
   150      * @api
       
   151      */
       
   152     static public function createFromGlobals()
       
   153     {
       
   154         $request = new static($_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER);
       
   155 
       
   156         if (0 === strpos($request->server->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
       
   157             && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE'))
       
   158         ) {
       
   159             parse_str($request->getContent(), $data);
       
   160             $request->request = new ParameterBag($data);
       
   161         }
       
   162 
       
   163         return $request;
       
   164     }
       
   165 
       
   166     /**
       
   167      * Creates a Request based on a given URI and configuration.
       
   168      *
       
   169      * @param string $uri        The URI
       
   170      * @param string $method     The HTTP method
       
   171      * @param array  $parameters The request (GET) or query (POST) parameters
       
   172      * @param array  $cookies    The request cookies ($_COOKIE)
       
   173      * @param array  $files      The request files ($_FILES)
       
   174      * @param array  $server     The server parameters ($_SERVER)
       
   175      * @param string $content    The raw body data
       
   176      *
       
   177      * @return Request A Request instance
       
   178      *
       
   179      * @api
       
   180      */
       
   181     static public function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null)
       
   182     {
       
   183         $defaults = array(
       
   184             'SERVER_NAME'          => 'localhost',
       
   185             'SERVER_PORT'          => 80,
       
   186             'HTTP_HOST'            => 'localhost',
       
   187             'HTTP_USER_AGENT'      => 'Symfony/2.X',
       
   188             'HTTP_ACCEPT'          => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
       
   189             'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5',
       
   190             'HTTP_ACCEPT_CHARSET'  => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
       
   191             'REMOTE_ADDR'          => '127.0.0.1',
       
   192             'SCRIPT_NAME'          => '',
       
   193             'SCRIPT_FILENAME'      => '',
       
   194             'SERVER_PROTOCOL'      => 'HTTP/1.1',
       
   195             'REQUEST_TIME'         => time(),
       
   196         );
       
   197 
       
   198         $components = parse_url($uri);
       
   199         if (isset($components['host'])) {
       
   200             $defaults['SERVER_NAME'] = $components['host'];
       
   201             $defaults['HTTP_HOST'] = $components['host'];
       
   202         }
       
   203 
       
   204         if (isset($components['scheme'])) {
       
   205             if ('https' === $components['scheme']) {
       
   206                 $defaults['HTTPS'] = 'on';
       
   207                 $defaults['SERVER_PORT'] = 443;
       
   208             }
       
   209         }
       
   210 
       
   211         if (isset($components['port'])) {
       
   212             $defaults['SERVER_PORT'] = $components['port'];
       
   213             $defaults['HTTP_HOST'] = $defaults['HTTP_HOST'].':'.$components['port'];
       
   214         }
       
   215 
       
   216         if (!isset($components['path'])) {
       
   217             $components['path'] = '';
       
   218         }
       
   219 
       
   220         if (in_array(strtoupper($method), array('POST', 'PUT', 'DELETE'))) {
       
   221             $request = $parameters;
       
   222             $query = array();
       
   223             $defaults['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
       
   224         } else {
       
   225             $request = array();
       
   226             $query = $parameters;
       
   227             if (false !== $pos = strpos($uri, '?')) {
       
   228                 $qs = substr($uri, $pos + 1);
       
   229                 parse_str($qs, $params);
       
   230 
       
   231                 $query = array_merge($params, $query);
       
   232             }
       
   233         }
       
   234 
       
   235         $queryString = isset($components['query']) ? html_entity_decode($components['query']) : '';
       
   236         parse_str($queryString, $qs);
       
   237         if (is_array($qs)) {
       
   238             $query = array_replace($qs, $query);
       
   239         }
       
   240 
       
   241         $uri = $components['path'].($queryString ? '?'.$queryString : '');
       
   242 
       
   243         $server = array_replace($defaults, $server, array(
       
   244             'REQUEST_METHOD'       => strtoupper($method),
       
   245             'PATH_INFO'            => '',
       
   246             'REQUEST_URI'          => $uri,
       
   247             'QUERY_STRING'         => $queryString,
       
   248         ));
       
   249 
       
   250         return new static($query, $request, array(), $cookies, $files, $server, $content);
       
   251     }
       
   252 
       
   253     /**
       
   254      * Clones a request and overrides some of its parameters.
       
   255      *
       
   256      * @param array $query      The GET parameters
       
   257      * @param array $request    The POST parameters
       
   258      * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
       
   259      * @param array $cookies    The COOKIE parameters
       
   260      * @param array $files      The FILES parameters
       
   261      * @param array $server     The SERVER parameters
       
   262      *
       
   263      * @api
       
   264      */
       
   265     public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null)
       
   266     {
       
   267         $dup = clone $this;
       
   268         if ($query !== null) {
       
   269             $dup->query = new ParameterBag($query);
       
   270         }
       
   271         if ($request !== null) {
       
   272             $dup->request = new ParameterBag($request);
       
   273         }
       
   274         if ($attributes !== null) {
       
   275             $dup->attributes = new ParameterBag($attributes);
       
   276         }
       
   277         if ($cookies !== null) {
       
   278             $dup->cookies = new ParameterBag($cookies);
       
   279         }
       
   280         if ($files !== null) {
       
   281             $dup->files = new FileBag($files);
       
   282         }
       
   283         if ($server !== null) {
       
   284             $dup->server = new ServerBag($server);
       
   285             $dup->headers = new HeaderBag($dup->server->getHeaders());
       
   286         }
       
   287         $this->languages = null;
       
   288         $this->charsets = null;
       
   289         $this->acceptableContentTypes = null;
       
   290         $this->pathInfo = null;
       
   291         $this->requestUri = null;
       
   292         $this->baseUrl = null;
       
   293         $this->basePath = null;
       
   294         $this->method = null;
       
   295         $this->format = null;
       
   296 
       
   297         return $dup;
       
   298     }
       
   299 
       
   300     /**
       
   301      * Clones the current request.
       
   302      *
       
   303      * Note that the session is not cloned as duplicated requests
       
   304      * are most of the time sub-requests of the main one.
       
   305      */
       
   306     public function __clone()
       
   307     {
       
   308         $this->query      = clone $this->query;
       
   309         $this->request    = clone $this->request;
       
   310         $this->attributes = clone $this->attributes;
       
   311         $this->cookies    = clone $this->cookies;
       
   312         $this->files      = clone $this->files;
       
   313         $this->server     = clone $this->server;
       
   314         $this->headers    = clone $this->headers;
       
   315     }
       
   316 
       
   317     /**
       
   318      * Returns the request as a string.
       
   319      *
       
   320      * @return string The request
       
   321      */
       
   322     public function __toString()
       
   323     {
       
   324         return
       
   325             sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n".
       
   326             $this->headers."\r\n".
       
   327             $this->getContent();
       
   328     }
       
   329 
       
   330     /**
       
   331      * Overrides the PHP global variables according to this request instance.
       
   332      *
       
   333      * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE, and $_FILES.
       
   334      *
       
   335      * @api
       
   336      */
       
   337     public function overrideGlobals()
       
   338     {
       
   339         $_GET = $this->query->all();
       
   340         $_POST = $this->request->all();
       
   341         $_SERVER = $this->server->all();
       
   342         $_COOKIE = $this->cookies->all();
       
   343         // FIXME: populate $_FILES
       
   344 
       
   345         foreach ($this->headers->all() as $key => $value) {
       
   346             $key = strtoupper(str_replace('-', '_', $key));
       
   347             if (in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH'))) {
       
   348                 $_SERVER[$key] = implode(', ', $value);
       
   349             } else {
       
   350                 $_SERVER['HTTP_'.$key] = implode(', ', $value);
       
   351             }
       
   352         }
       
   353 
       
   354         // FIXME: should read variables_order and request_order
       
   355         // to know which globals to merge and in which order
       
   356         $_REQUEST = array_merge($_GET, $_POST);
       
   357     }
       
   358 
       
   359     /**
       
   360      * Trusts $_SERVER entries coming from proxies.
       
   361      *
       
   362      * You should only call this method if your application
       
   363      * is hosted behind a reverse proxy that you manage.
       
   364      *
       
   365      * @api
       
   366      */
       
   367     static public function trustProxyData()
       
   368     {
       
   369         self::$trustProxy = true;
       
   370     }
       
   371 
       
   372     /**
       
   373      * Gets a "parameter" value.
       
   374      *
       
   375      * This method is mainly useful for libraries that want to provide some flexibility.
       
   376      *
       
   377      * Order of precedence: GET, PATH, POST, COOKIE
       
   378      * Avoid using this method in controllers:
       
   379      *  * slow
       
   380      *  * prefer to get from a "named" source
       
   381      *
       
   382      * @param string    $key        the key
       
   383      * @param mixed     $default    the default value
       
   384      * @param type      $deep       is parameter deep in multidimensional array
       
   385      *
       
   386      * @return mixed
       
   387      */
       
   388     public function get($key, $default = null, $deep = false)
       
   389     {
       
   390         return $this->query->get($key, $this->attributes->get($key, $this->request->get($key, $default, $deep), $deep), $deep);
       
   391     }
       
   392 
       
   393     /**
       
   394      * Gets the Session.
       
   395      *
       
   396      * @return Session|null The session
       
   397      *
       
   398      * @api
       
   399      */
       
   400     public function getSession()
       
   401     {
       
   402         return $this->session;
       
   403     }
       
   404 
       
   405     /**
       
   406      * Whether the request contains a Session which was started in one of the
       
   407      * previous requests.
       
   408      *
       
   409      * @return boolean
       
   410      *
       
   411      * @api
       
   412      */
       
   413     public function hasPreviousSession()
       
   414     {
       
   415         // the check for $this->session avoids malicious users trying to fake a session cookie with proper name
       
   416         return $this->cookies->has(session_name()) && null !== $this->session;
       
   417     }
       
   418 
       
   419     /**
       
   420      * Whether the request contains a Session object.
       
   421      *
       
   422      * @return boolean
       
   423      *
       
   424      * @api
       
   425      */
       
   426     public function hasSession()
       
   427     {
       
   428         return null !== $this->session;
       
   429     }
       
   430 
       
   431     /**
       
   432      * Sets the Session.
       
   433      *
       
   434      * @param Session $session The Session
       
   435      *
       
   436      * @api
       
   437      */
       
   438     public function setSession(Session $session)
       
   439     {
       
   440         $this->session = $session;
       
   441     }
       
   442 
       
   443     /**
       
   444      * Returns the client IP address.
       
   445      *
       
   446      * @param  Boolean $proxy Whether the current request has been made behind a proxy or not
       
   447      *
       
   448      * @return string The client IP address
       
   449      *
       
   450      * @api
       
   451      */
       
   452     public function getClientIp($proxy = false)
       
   453     {
       
   454         if ($proxy) {
       
   455             if ($this->server->has('HTTP_CLIENT_IP')) {
       
   456                 return $this->server->get('HTTP_CLIENT_IP');
       
   457             } elseif (self::$trustProxy && $this->server->has('HTTP_X_FORWARDED_FOR')) {
       
   458                 return $this->server->get('HTTP_X_FORWARDED_FOR');
       
   459             }
       
   460         }
       
   461 
       
   462         return $this->server->get('REMOTE_ADDR');
       
   463     }
       
   464 
       
   465     /**
       
   466      * Returns current script name.
       
   467      *
       
   468      * @return string
       
   469      *
       
   470      * @api
       
   471      */
       
   472     public function getScriptName()
       
   473     {
       
   474         return $this->server->get('SCRIPT_NAME', $this->server->get('ORIG_SCRIPT_NAME', ''));
       
   475     }
       
   476 
       
   477     /**
       
   478      * Returns the path being requested relative to the executed script.
       
   479      *
       
   480      * The path info always starts with a /.
       
   481      *
       
   482      * Suppose this request is instantiated from /mysite on localhost:
       
   483      *
       
   484      *  * http://localhost/mysite              returns an empty string
       
   485      *  * http://localhost/mysite/about        returns '/about'
       
   486      *  * http://localhost/mysite/about?var=1  returns '/about'
       
   487      *
       
   488      * @return string
       
   489      *
       
   490      * @api
       
   491      */
       
   492     public function getPathInfo()
       
   493     {
       
   494         if (null === $this->pathInfo) {
       
   495             $this->pathInfo = $this->preparePathInfo();
       
   496         }
       
   497 
       
   498         return $this->pathInfo;
       
   499     }
       
   500 
       
   501     /**
       
   502      * Returns the root path from which this request is executed.
       
   503      *
       
   504      * Suppose that an index.php file instantiates this request object:
       
   505      *
       
   506      *  * http://localhost/index.php        returns an empty string
       
   507      *  * http://localhost/index.php/page   returns an empty string
       
   508      *  * http://localhost/web/index.php    return '/web'
       
   509      *
       
   510      * @return string
       
   511      *
       
   512      * @api
       
   513      */
       
   514     public function getBasePath()
       
   515     {
       
   516         if (null === $this->basePath) {
       
   517             $this->basePath = $this->prepareBasePath();
       
   518         }
       
   519 
       
   520         return $this->basePath;
       
   521     }
       
   522 
       
   523     /**
       
   524      * Returns the root url from which this request is executed.
       
   525      *
       
   526      * The base URL never ends with a /.
       
   527      *
       
   528      * This is similar to getBasePath(), except that it also includes the
       
   529      * script filename (e.g. index.php) if one exists.
       
   530      *
       
   531      * @return string
       
   532      *
       
   533      * @api
       
   534      */
       
   535     public function getBaseUrl()
       
   536     {
       
   537         if (null === $this->baseUrl) {
       
   538             $this->baseUrl = $this->prepareBaseUrl();
       
   539         }
       
   540 
       
   541         return $this->baseUrl;
       
   542     }
       
   543 
       
   544     /**
       
   545      * Gets the request's scheme.
       
   546      *
       
   547      * @return string
       
   548      *
       
   549      * @api
       
   550      */
       
   551     public function getScheme()
       
   552     {
       
   553         return $this->isSecure() ? 'https' : 'http';
       
   554     }
       
   555 
       
   556     /**
       
   557      * Returns the port on which the request is made.
       
   558      *
       
   559      * @return string
       
   560      *
       
   561      * @api
       
   562      */
       
   563     public function getPort()
       
   564     {
       
   565         return $this->headers->get('X-Forwarded-Port') ?: $this->server->get('SERVER_PORT');
       
   566     }
       
   567 
       
   568     /**
       
   569      * Returns the HTTP host being requested.
       
   570      *
       
   571      * The port name will be appended to the host if it's non-standard.
       
   572      *
       
   573      * @return string
       
   574      *
       
   575      * @api
       
   576      */
       
   577     public function getHttpHost()
       
   578     {
       
   579         $scheme = $this->getScheme();
       
   580         $port   = $this->getPort();
       
   581 
       
   582         if (('http' == $scheme && $port == 80) || ('https' == $scheme && $port == 443)) {
       
   583             return $this->getHost();
       
   584         }
       
   585 
       
   586         return $this->getHost().':'.$port;
       
   587     }
       
   588 
       
   589     /**
       
   590      * Returns the requested URI.
       
   591      *
       
   592      * @return string
       
   593      *
       
   594      * @api
       
   595      */
       
   596     public function getRequestUri()
       
   597     {
       
   598         if (null === $this->requestUri) {
       
   599             $this->requestUri = $this->prepareRequestUri();
       
   600         }
       
   601 
       
   602         return $this->requestUri;
       
   603     }
       
   604 
       
   605     /**
       
   606      * Generates a normalized URI for the Request.
       
   607      *
       
   608      * @return string A normalized URI for the Request
       
   609      *
       
   610      * @see getQueryString()
       
   611      *
       
   612      * @api
       
   613      */
       
   614     public function getUri()
       
   615     {
       
   616         $qs = $this->getQueryString();
       
   617         if (null !== $qs) {
       
   618             $qs = '?'.$qs;
       
   619         }
       
   620 
       
   621         return $this->getScheme().'://'.$this->getHttpHost().$this->getBaseUrl().$this->getPathInfo().$qs;
       
   622     }
       
   623 
       
   624     /**
       
   625      * Generates a normalized URI for the given path.
       
   626      *
       
   627      * @param string $path A path to use instead of the current one
       
   628      *
       
   629      * @return string The normalized URI for the path
       
   630      *
       
   631      * @api
       
   632      */
       
   633     public function getUriForPath($path)
       
   634     {
       
   635         return $this->getScheme().'://'.$this->getHttpHost().$this->getBaseUrl().$path;
       
   636     }
       
   637 
       
   638     /**
       
   639      * Generates the normalized query string for the Request.
       
   640      *
       
   641      * It builds a normalized query string, where keys/value pairs are alphabetized
       
   642      * and have consistent escaping.
       
   643      *
       
   644      * @return string A normalized query string for the Request
       
   645      *
       
   646      * @api
       
   647      */
       
   648     public function getQueryString()
       
   649     {
       
   650         if (!$qs = $this->server->get('QUERY_STRING')) {
       
   651             return null;
       
   652         }
       
   653 
       
   654         $parts = array();
       
   655         $order = array();
       
   656 
       
   657         foreach (explode('&', $qs) as $segment) {
       
   658             if (false === strpos($segment, '=')) {
       
   659                 $parts[] = $segment;
       
   660                 $order[] = $segment;
       
   661             } else {
       
   662                 $tmp = explode('=', rawurldecode($segment), 2);
       
   663                 $parts[] = rawurlencode($tmp[0]).'='.rawurlencode($tmp[1]);
       
   664                 $order[] = $tmp[0];
       
   665             }
       
   666         }
       
   667         array_multisort($order, SORT_ASC, $parts);
       
   668 
       
   669         return implode('&', $parts);
       
   670     }
       
   671 
       
   672     /**
       
   673      * Checks whether the request is secure or not.
       
   674      *
       
   675      * @return Boolean
       
   676      *
       
   677      * @api
       
   678      */
       
   679     public function isSecure()
       
   680     {
       
   681         return (
       
   682             (strtolower($this->server->get('HTTPS')) == 'on' || $this->server->get('HTTPS') == 1)
       
   683             ||
       
   684             (self::$trustProxy && strtolower($this->headers->get('SSL_HTTPS')) == 'on' || $this->headers->get('SSL_HTTPS') == 1)
       
   685             ||
       
   686             (self::$trustProxy && strtolower($this->headers->get('X_FORWARDED_PROTO')) == 'https')
       
   687         );
       
   688     }
       
   689 
       
   690     /**
       
   691      * Returns the host name.
       
   692      *
       
   693      * @return string
       
   694      *
       
   695      * @api
       
   696      */
       
   697     public function getHost()
       
   698     {
       
   699         if (self::$trustProxy && $host = $this->headers->get('X_FORWARDED_HOST')) {
       
   700             $elements = explode(',', $host);
       
   701 
       
   702             $host = trim($elements[count($elements) - 1]);
       
   703         } else {
       
   704             if (!$host = $this->headers->get('HOST')) {
       
   705                 if (!$host = $this->server->get('SERVER_NAME')) {
       
   706                     $host = $this->server->get('SERVER_ADDR', '');
       
   707                 }
       
   708             }
       
   709         }
       
   710 
       
   711         // Remove port number from host
       
   712         $host = preg_replace('/:\d+$/', '', $host);
       
   713 
       
   714         return trim($host);
       
   715     }
       
   716 
       
   717     /**
       
   718      * Sets the request method.
       
   719      *
       
   720      * @param string $method
       
   721      *
       
   722      * @api
       
   723      */
       
   724     public function setMethod($method)
       
   725     {
       
   726         $this->method = null;
       
   727         $this->server->set('REQUEST_METHOD', $method);
       
   728     }
       
   729 
       
   730     /**
       
   731      * Gets the request method.
       
   732      *
       
   733      * The method is always an uppercased string.
       
   734      *
       
   735      * @return string The request method
       
   736      *
       
   737      * @api
       
   738      */
       
   739     public function getMethod()
       
   740     {
       
   741         if (null === $this->method) {
       
   742             $this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
       
   743             if ('POST' === $this->method) {
       
   744                 $this->method = strtoupper($this->headers->get('X-HTTP-METHOD-OVERRIDE', $this->request->get('_method', 'POST')));
       
   745             }
       
   746         }
       
   747 
       
   748         return $this->method;
       
   749     }
       
   750 
       
   751     /**
       
   752      * Gets the mime type associated with the format.
       
   753      *
       
   754      * @param  string $format  The format
       
   755      *
       
   756      * @return string The associated mime type (null if not found)
       
   757      *
       
   758      * @api
       
   759      */
       
   760     public function getMimeType($format)
       
   761     {
       
   762         if (null === static::$formats) {
       
   763             static::initializeFormats();
       
   764         }
       
   765 
       
   766         return isset(static::$formats[$format]) ? static::$formats[$format][0] : null;
       
   767     }
       
   768 
       
   769     /**
       
   770      * Gets the format associated with the mime type.
       
   771      *
       
   772      * @param  string $mimeType  The associated mime type
       
   773      *
       
   774      * @return string The format (null if not found)
       
   775      *
       
   776      * @api
       
   777      */
       
   778     public function getFormat($mimeType)
       
   779     {
       
   780         if (false !== $pos = strpos($mimeType, ';')) {
       
   781             $mimeType = substr($mimeType, 0, $pos);
       
   782         }
       
   783 
       
   784         if (null === static::$formats) {
       
   785             static::initializeFormats();
       
   786         }
       
   787 
       
   788         foreach (static::$formats as $format => $mimeTypes) {
       
   789             if (in_array($mimeType, (array) $mimeTypes)) {
       
   790                 return $format;
       
   791             }
       
   792         }
       
   793 
       
   794         return null;
       
   795     }
       
   796 
       
   797     /**
       
   798      * Associates a format with mime types.
       
   799      *
       
   800      * @param string       $format     The format
       
   801      * @param string|array $mimeTypes  The associated mime types (the preferred one must be the first as it will be used as the content type)
       
   802      *
       
   803      * @api
       
   804      */
       
   805     public function setFormat($format, $mimeTypes)
       
   806     {
       
   807         if (null === static::$formats) {
       
   808             static::initializeFormats();
       
   809         }
       
   810 
       
   811         static::$formats[$format] = is_array($mimeTypes) ? $mimeTypes : array($mimeTypes);
       
   812     }
       
   813 
       
   814     /**
       
   815      * Gets the request format.
       
   816      *
       
   817      * Here is the process to determine the format:
       
   818      *
       
   819      *  * format defined by the user (with setRequestFormat())
       
   820      *  * _format request parameter
       
   821      *  * $default
       
   822      *
       
   823      * @param string  $default     The default format
       
   824      *
       
   825      * @return string The request format
       
   826      *
       
   827      * @api
       
   828      */
       
   829     public function getRequestFormat($default = 'html')
       
   830     {
       
   831         if (null === $this->format) {
       
   832             $this->format = $this->get('_format', $default);
       
   833         }
       
   834 
       
   835         return $this->format;
       
   836     }
       
   837 
       
   838     /**
       
   839      * Sets the request format.
       
   840      *
       
   841      * @param string $format The request format.
       
   842      *
       
   843      * @api
       
   844      */
       
   845     public function setRequestFormat($format)
       
   846     {
       
   847         $this->format = $format;
       
   848     }
       
   849 
       
   850     /**
       
   851      * Checks whether the method is safe or not.
       
   852      *
       
   853      * @return Boolean
       
   854      *
       
   855      * @api
       
   856      */
       
   857     public function isMethodSafe()
       
   858     {
       
   859         return in_array($this->getMethod(), array('GET', 'HEAD'));
       
   860     }
       
   861 
       
   862     /**
       
   863      * Returns the request body content.
       
   864      *
       
   865      * @param  Boolean $asResource If true, a resource will be returned
       
   866      *
       
   867      * @return string|resource The request body content or a resource to read the body stream.
       
   868      */
       
   869     public function getContent($asResource = false)
       
   870     {
       
   871         if (false === $this->content || (true === $asResource && null !== $this->content)) {
       
   872             throw new \LogicException('getContent() can only be called once when using the resource return type.');
       
   873         }
       
   874 
       
   875         if (true === $asResource) {
       
   876             $this->content = false;
       
   877 
       
   878             return fopen('php://input', 'rb');
       
   879         }
       
   880 
       
   881         if (null === $this->content) {
       
   882             $this->content = file_get_contents('php://input');
       
   883         }
       
   884 
       
   885         return $this->content;
       
   886     }
       
   887 
       
   888     /**
       
   889      * Gets the Etags.
       
   890      *
       
   891      * @return array The entity tags
       
   892      */
       
   893     public function getETags()
       
   894     {
       
   895         return preg_split('/\s*,\s*/', $this->headers->get('if_none_match'), null, PREG_SPLIT_NO_EMPTY);
       
   896     }
       
   897 
       
   898     public function isNoCache()
       
   899     {
       
   900         return $this->headers->hasCacheControlDirective('no-cache') || 'no-cache' == $this->headers->get('Pragma');
       
   901     }
       
   902 
       
   903     /**
       
   904      * Returns the preferred language.
       
   905      *
       
   906      * @param  array  $locales  An array of ordered available locales
       
   907      *
       
   908      * @return string The preferred locale
       
   909      *
       
   910      * @api
       
   911      */
       
   912     public function getPreferredLanguage(array $locales = null)
       
   913     {
       
   914         $preferredLanguages = $this->getLanguages();
       
   915 
       
   916         if (null === $locales) {
       
   917             return isset($preferredLanguages[0]) ? $preferredLanguages[0] : null;
       
   918         }
       
   919 
       
   920         if (!$preferredLanguages) {
       
   921             return $locales[0];
       
   922         }
       
   923 
       
   924         $preferredLanguages = array_values(array_intersect($preferredLanguages, $locales));
       
   925 
       
   926         return isset($preferredLanguages[0]) ? $preferredLanguages[0] : $locales[0];
       
   927     }
       
   928 
       
   929     /**
       
   930      * Gets a list of languages acceptable by the client browser.
       
   931      *
       
   932      * @return array Languages ordered in the user browser preferences
       
   933      *
       
   934      * @api
       
   935      */
       
   936     public function getLanguages()
       
   937     {
       
   938         if (null !== $this->languages) {
       
   939             return $this->languages;
       
   940         }
       
   941 
       
   942         $languages = $this->splitHttpAcceptHeader($this->headers->get('Accept-Language'));
       
   943         $this->languages = array();
       
   944         foreach ($languages as $lang => $q) {
       
   945             if (strstr($lang, '-')) {
       
   946                 $codes = explode('-', $lang);
       
   947                 if ($codes[0] == 'i') {
       
   948                     // Language not listed in ISO 639 that are not variants
       
   949                     // of any listed language, which can be registered with the
       
   950                     // i-prefix, such as i-cherokee
       
   951                     if (count($codes) > 1) {
       
   952                         $lang = $codes[1];
       
   953                     }
       
   954                 } else {
       
   955                     for ($i = 0, $max = count($codes); $i < $max; $i++) {
       
   956                         if ($i == 0) {
       
   957                             $lang = strtolower($codes[0]);
       
   958                         } else {
       
   959                             $lang .= '_'.strtoupper($codes[$i]);
       
   960                         }
       
   961                     }
       
   962                 }
       
   963             }
       
   964 
       
   965             $this->languages[] = $lang;
       
   966         }
       
   967 
       
   968         return $this->languages;
       
   969     }
       
   970 
       
   971     /**
       
   972      * Gets a list of charsets acceptable by the client browser.
       
   973      *
       
   974      * @return array List of charsets in preferable order
       
   975      *
       
   976      * @api
       
   977      */
       
   978     public function getCharsets()
       
   979     {
       
   980         if (null !== $this->charsets) {
       
   981             return $this->charsets;
       
   982         }
       
   983 
       
   984         return $this->charsets = array_keys($this->splitHttpAcceptHeader($this->headers->get('Accept-Charset')));
       
   985     }
       
   986 
       
   987     /**
       
   988      * Gets a list of content types acceptable by the client browser
       
   989      *
       
   990      * @return array List of content types in preferable order
       
   991      *
       
   992      * @api
       
   993      */
       
   994     public function getAcceptableContentTypes()
       
   995     {
       
   996         if (null !== $this->acceptableContentTypes) {
       
   997             return $this->acceptableContentTypes;
       
   998         }
       
   999 
       
  1000         return $this->acceptableContentTypes = array_keys($this->splitHttpAcceptHeader($this->headers->get('Accept')));
       
  1001     }
       
  1002 
       
  1003     /**
       
  1004      * Returns true if the request is a XMLHttpRequest.
       
  1005      *
       
  1006      * It works if your JavaScript library set an X-Requested-With HTTP header.
       
  1007      * It is known to work with Prototype, Mootools, jQuery.
       
  1008      *
       
  1009      * @return Boolean true if the request is an XMLHttpRequest, false otherwise
       
  1010      *
       
  1011      * @api
       
  1012      */
       
  1013     public function isXmlHttpRequest()
       
  1014     {
       
  1015         return 'XMLHttpRequest' == $this->headers->get('X-Requested-With');
       
  1016     }
       
  1017 
       
  1018     /**
       
  1019      * Splits an Accept-* HTTP header.
       
  1020      *
       
  1021      * @param string $header  Header to split
       
  1022      */
       
  1023     public function splitHttpAcceptHeader($header)
       
  1024     {
       
  1025         if (!$header) {
       
  1026             return array();
       
  1027         }
       
  1028 
       
  1029         $values = array();
       
  1030         foreach (array_filter(explode(',', $header)) as $value) {
       
  1031             // Cut off any q-value that might come after a semi-colon
       
  1032             if ($pos = strpos($value, ';')) {
       
  1033                 $q     = (float) trim(substr($value, strpos($value, '=') + 1));
       
  1034                 $value = trim(substr($value, 0, $pos));
       
  1035             } else {
       
  1036                 $q = 1;
       
  1037             }
       
  1038 
       
  1039             if (0 < $q) {
       
  1040                 $values[trim($value)] = $q;
       
  1041             }
       
  1042         }
       
  1043 
       
  1044         arsort($values);
       
  1045         reset($values);
       
  1046 
       
  1047         return $values;
       
  1048     }
       
  1049 
       
  1050     /*
       
  1051      * The following methods are derived from code of the Zend Framework (1.10dev - 2010-01-24)
       
  1052      *
       
  1053      * Code subject to the new BSD license (http://framework.zend.com/license/new-bsd).
       
  1054      *
       
  1055      * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
  1056      */
       
  1057 
       
  1058     protected function prepareRequestUri()
       
  1059     {
       
  1060         $requestUri = '';
       
  1061 
       
  1062         if ($this->headers->has('X_REWRITE_URL')) {
       
  1063             // check this first so IIS will catch
       
  1064             $requestUri = $this->headers->get('X_REWRITE_URL');
       
  1065         } elseif ($this->server->get('IIS_WasUrlRewritten') == '1' && $this->server->get('UNENCODED_URL') != '') {
       
  1066             // IIS7 with URL Rewrite: make sure we get the unencoded url (double slash problem)
       
  1067             $requestUri = $this->server->get('UNENCODED_URL');
       
  1068         } elseif ($this->server->has('REQUEST_URI')) {
       
  1069             $requestUri = $this->server->get('REQUEST_URI');
       
  1070             // HTTP proxy reqs setup request uri with scheme and host [and port] + the url path, only use url path
       
  1071             $schemeAndHttpHost = $this->getScheme().'://'.$this->getHttpHost();
       
  1072             if (strpos($requestUri, $schemeAndHttpHost) === 0) {
       
  1073                 $requestUri = substr($requestUri, strlen($schemeAndHttpHost));
       
  1074             }
       
  1075         } elseif ($this->server->has('ORIG_PATH_INFO')) {
       
  1076             // IIS 5.0, PHP as CGI
       
  1077             $requestUri = $this->server->get('ORIG_PATH_INFO');
       
  1078             if ($this->server->get('QUERY_STRING')) {
       
  1079                 $requestUri .= '?'.$this->server->get('QUERY_STRING');
       
  1080             }
       
  1081         }
       
  1082 
       
  1083         return $requestUri;
       
  1084     }
       
  1085 
       
  1086     protected function prepareBaseUrl()
       
  1087     {
       
  1088         $filename = basename($this->server->get('SCRIPT_FILENAME'));
       
  1089 
       
  1090         if (basename($this->server->get('SCRIPT_NAME')) === $filename) {
       
  1091             $baseUrl = $this->server->get('SCRIPT_NAME');
       
  1092         } elseif (basename($this->server->get('PHP_SELF')) === $filename) {
       
  1093             $baseUrl = $this->server->get('PHP_SELF');
       
  1094         } elseif (basename($this->server->get('ORIG_SCRIPT_NAME')) === $filename) {
       
  1095             $baseUrl = $this->server->get('ORIG_SCRIPT_NAME'); // 1and1 shared hosting compatibility
       
  1096         } else {
       
  1097             // Backtrack up the script_filename to find the portion matching
       
  1098             // php_self
       
  1099             $path    = $this->server->get('PHP_SELF', '');
       
  1100             $file    = $this->server->get('SCRIPT_FILENAME', '');
       
  1101             $segs    = explode('/', trim($file, '/'));
       
  1102             $segs    = array_reverse($segs);
       
  1103             $index   = 0;
       
  1104             $last    = count($segs);
       
  1105             $baseUrl = '';
       
  1106             do {
       
  1107                 $seg     = $segs[$index];
       
  1108                 $baseUrl = '/'.$seg.$baseUrl;
       
  1109                 ++$index;
       
  1110             } while (($last > $index) && (false !== ($pos = strpos($path, $baseUrl))) && (0 != $pos));
       
  1111         }
       
  1112 
       
  1113         // Does the baseUrl have anything in common with the request_uri?
       
  1114         $requestUri = $this->getRequestUri();
       
  1115 
       
  1116         if ($baseUrl && 0 === strpos($requestUri, $baseUrl)) {
       
  1117             // full $baseUrl matches
       
  1118             return $baseUrl;
       
  1119         }
       
  1120 
       
  1121         if ($baseUrl && 0 === strpos($requestUri, dirname($baseUrl))) {
       
  1122             // directory portion of $baseUrl matches
       
  1123             return rtrim(dirname($baseUrl), '/');
       
  1124         }
       
  1125 
       
  1126         $truncatedRequestUri = $requestUri;
       
  1127         if (($pos = strpos($requestUri, '?')) !== false) {
       
  1128             $truncatedRequestUri = substr($requestUri, 0, $pos);
       
  1129         }
       
  1130 
       
  1131         $basename = basename($baseUrl);
       
  1132         if (empty($basename) || !strpos($truncatedRequestUri, $basename)) {
       
  1133             // no match whatsoever; set it blank
       
  1134             return '';
       
  1135         }
       
  1136 
       
  1137         // If using mod_rewrite or ISAPI_Rewrite strip the script filename
       
  1138         // out of baseUrl. $pos !== 0 makes sure it is not matching a value
       
  1139         // from PATH_INFO or QUERY_STRING
       
  1140         if ((strlen($requestUri) >= strlen($baseUrl)) && ((false !== ($pos = strpos($requestUri, $baseUrl))) && ($pos !== 0))) {
       
  1141             $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl));
       
  1142         }
       
  1143 
       
  1144         return rtrim($baseUrl, '/');
       
  1145     }
       
  1146 
       
  1147     /**
       
  1148      * Prepares base path.
       
  1149      *
       
  1150      * @return string base path
       
  1151      */
       
  1152     protected function prepareBasePath()
       
  1153     {
       
  1154         $filename = basename($this->server->get('SCRIPT_FILENAME'));
       
  1155         $baseUrl = $this->getBaseUrl();
       
  1156         if (empty($baseUrl)) {
       
  1157             return '';
       
  1158         }
       
  1159 
       
  1160         if (basename($baseUrl) === $filename) {
       
  1161             $basePath = dirname($baseUrl);
       
  1162         } else {
       
  1163             $basePath = $baseUrl;
       
  1164         }
       
  1165 
       
  1166         if ('\\' === DIRECTORY_SEPARATOR) {
       
  1167             $basePath = str_replace('\\', '/', $basePath);
       
  1168         }
       
  1169 
       
  1170         return rtrim($basePath, '/');
       
  1171     }
       
  1172 
       
  1173     /**
       
  1174      * Prepares path info.
       
  1175      *
       
  1176      * @return string path info
       
  1177      */
       
  1178     protected function preparePathInfo()
       
  1179     {
       
  1180         $baseUrl = $this->getBaseUrl();
       
  1181 
       
  1182         if (null === ($requestUri = $this->getRequestUri())) {
       
  1183             return '/';
       
  1184         }
       
  1185 
       
  1186         $pathInfo = '/';
       
  1187 
       
  1188         // Remove the query string from REQUEST_URI
       
  1189         if ($pos = strpos($requestUri, '?')) {
       
  1190             $requestUri = substr($requestUri, 0, $pos);
       
  1191         }
       
  1192 
       
  1193         if ((null !== $baseUrl) && (false === ($pathInfo = substr($requestUri, strlen($baseUrl))))) {
       
  1194             // If substr() returns false then PATH_INFO is set to an empty string
       
  1195             return '/';
       
  1196         } elseif (null === $baseUrl) {
       
  1197             return $requestUri;
       
  1198         }
       
  1199 
       
  1200         return (string) $pathInfo;
       
  1201     }
       
  1202 
       
  1203     /**
       
  1204      * Initializes HTTP request formats.
       
  1205      */
       
  1206     static protected function initializeFormats()
       
  1207     {
       
  1208         static::$formats = array(
       
  1209             'html' => array('text/html', 'application/xhtml+xml'),
       
  1210             'txt'  => array('text/plain'),
       
  1211             'js'   => array('application/javascript', 'application/x-javascript', 'text/javascript'),
       
  1212             'css'  => array('text/css'),
       
  1213             'json' => array('application/json', 'application/x-json'),
       
  1214             'xml'  => array('text/xml', 'application/xml', 'application/x-xml'),
       
  1215             'rdf'  => array('application/rdf+xml'),
       
  1216             'atom' => array('application/atom+xml'),
       
  1217         );
       
  1218     }
       
  1219 }