Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

Request.php

Zuletzt modifiziert: 09.10.2024, 12:54 - Dateigröße: 62.12 KiB


 0001  <?php
 0002   
 0003  /*
 0004   * This file is part of the Symfony package.
 0005   *
 0006   * (c) Fabien Potencier <fabien@symfony.com>
 0007   *
 0008   * For the full copyright and license information, please view the LICENSE
 0009   * file that was distributed with this source code.
 0010   */
 0011   
 0012  namespace Symfony\Component\HttpFoundation;
 0013   
 0014  use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException;
 0015  use Symfony\Component\HttpFoundation\Session\SessionInterface;
 0016   
 0017  /**
 0018   * Request represents an HTTP request.
 0019   *
 0020   * The methods dealing with URL accept / return a raw path (% encoded):
 0021   *   * getBasePath
 0022   *   * getBaseUrl
 0023   *   * getPathInfo
 0024   *   * getRequestUri
 0025   *   * getUri
 0026   *   * getUriForPath
 0027   *
 0028   * @author Fabien Potencier <fabien@symfony.com>
 0029   */
 0030  class Request
 0031  {
 0032      const HEADER_FORWARDED = 'forwarded';
 0033      const HEADER_CLIENT_IP = 'client_ip';
 0034      const HEADER_CLIENT_HOST = 'client_host';
 0035      const HEADER_CLIENT_PROTO = 'client_proto';
 0036      const HEADER_CLIENT_PORT = 'client_port';
 0037   
 0038      const METHOD_HEAD = 'HEAD';
 0039      const METHOD_GET = 'GET';
 0040      const METHOD_POST = 'POST';
 0041      const METHOD_PUT = 'PUT';
 0042      const METHOD_PATCH = 'PATCH';
 0043      const METHOD_DELETE = 'DELETE';
 0044      const METHOD_PURGE = 'PURGE';
 0045      const METHOD_OPTIONS = 'OPTIONS';
 0046      const METHOD_TRACE = 'TRACE';
 0047      const METHOD_CONNECT = 'CONNECT';
 0048   
 0049      /**
 0050       * @var string[]
 0051       */
 0052      protected static $trustedProxies = array();
 0053   
 0054      /**
 0055       * @var string[]
 0056       */
 0057      protected static $trustedHostPatterns = array();
 0058   
 0059      /**
 0060       * @var string[]
 0061       */
 0062      protected static $trustedHosts = array();
 0063   
 0064      /**
 0065       * Names for headers that can be trusted when
 0066       * using trusted proxies.
 0067       *
 0068       * The FORWARDED header is the standard as of rfc7239.
 0069       *
 0070       * The other headers are non-standard, but widely used
 0071       * by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
 0072       */
 0073      protected static $trustedHeaders = array(
 0074          self::HEADER_FORWARDED => 'FORWARDED',
 0075          self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
 0076          self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
 0077          self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
 0078          self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
 0079      );
 0080   
 0081      protected static $httpMethodParameterOverride = false;
 0082   
 0083      /**
 0084       * Custom parameters.
 0085       *
 0086       * @var \Symfony\Component\HttpFoundation\ParameterBag
 0087       */
 0088      public $attributes;
 0089   
 0090      /**
 0091       * Request body parameters ($_POST).
 0092       *
 0093       * @var \Symfony\Component\HttpFoundation\ParameterBag
 0094       */
 0095      public $request;
 0096   
 0097      /**
 0098       * Query string parameters ($_GET).
 0099       *
 0100       * @var \Symfony\Component\HttpFoundation\ParameterBag
 0101       */
 0102      public $query;
 0103   
 0104      /**
 0105       * Server and execution environment parameters ($_SERVER).
 0106       *
 0107       * @var \Symfony\Component\HttpFoundation\ServerBag
 0108       */
 0109      public $server;
 0110   
 0111      /**
 0112       * Uploaded files ($_FILES).
 0113       *
 0114       * @var \Symfony\Component\HttpFoundation\FileBag
 0115       */
 0116      public $files;
 0117   
 0118      /**
 0119       * Cookies ($_COOKIE).
 0120       *
 0121       * @var \Symfony\Component\HttpFoundation\ParameterBag
 0122       */
 0123      public $cookies;
 0124   
 0125      /**
 0126       * Headers (taken from the $_SERVER).
 0127       *
 0128       * @var \Symfony\Component\HttpFoundation\HeaderBag
 0129       */
 0130      public $headers;
 0131   
 0132      /**
 0133       * @var string
 0134       */
 0135      protected $content;
 0136   
 0137      /**
 0138       * @var array
 0139       */
 0140      protected $languages;
 0141   
 0142      /**
 0143       * @var array
 0144       */
 0145      protected $charsets;
 0146   
 0147      /**
 0148       * @var array
 0149       */
 0150      protected $encodings;
 0151   
 0152      /**
 0153       * @var array
 0154       */
 0155      protected $acceptableContentTypes;
 0156   
 0157      /**
 0158       * @var string
 0159       */
 0160      protected $pathInfo;
 0161   
 0162      /**
 0163       * @var string
 0164       */
 0165      protected $requestUri;
 0166   
 0167      /**
 0168       * @var string
 0169       */
 0170      protected $baseUrl;
 0171   
 0172      /**
 0173       * @var string
 0174       */
 0175      protected $basePath;
 0176   
 0177      /**
 0178       * @var string
 0179       */
 0180      protected $method;
 0181   
 0182      /**
 0183       * @var string
 0184       */
 0185      protected $format;
 0186   
 0187      /**
 0188       * @var \Symfony\Component\HttpFoundation\Session\SessionInterface
 0189       */
 0190      protected $session;
 0191   
 0192      /**
 0193       * @var string
 0194       */
 0195      protected $locale;
 0196   
 0197      /**
 0198       * @var string
 0199       */
 0200      protected $defaultLocale = 'en';
 0201   
 0202      /**
 0203       * @var array
 0204       */
 0205      protected static $formats;
 0206   
 0207      protected static $requestFactory;
 0208   
 0209      /**
 0210       * Constructor.
 0211       *
 0212       * @param array           $query      The GET parameters
 0213       * @param array           $request    The POST parameters
 0214       * @param array           $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
 0215       * @param array           $cookies    The COOKIE parameters
 0216       * @param array           $files      The FILES parameters
 0217       * @param array           $server     The SERVER parameters
 0218       * @param string|resource $content    The raw body data
 0219       */
 0220      public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
 0221      {
 0222          $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content);
 0223      }
 0224   
 0225      /**
 0226       * Sets the parameters for this request.
 0227       *
 0228       * This method also re-initializes all properties.
 0229       *
 0230       * @param array           $query      The GET parameters
 0231       * @param array           $request    The POST parameters
 0232       * @param array           $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
 0233       * @param array           $cookies    The COOKIE parameters
 0234       * @param array           $files      The FILES parameters
 0235       * @param array           $server     The SERVER parameters
 0236       * @param string|resource $content    The raw body data
 0237       */
 0238      public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
 0239      {
 0240          $this->request = new ParameterBag($request);
 0241          $this->query = new ParameterBag($query);
 0242          $this->attributes = new ParameterBag($attributes);
 0243          $this->cookies = new ParameterBag($cookies);
 0244          $this->files = new FileBag($files);
 0245          $this->server = new ServerBag($server);
 0246          $this->headers = new HeaderBag($this->server->getHeaders());
 0247   
 0248          $this->content = $content;
 0249          $this->languages = null;
 0250          $this->charsets = null;
 0251          $this->encodings = null;
 0252          $this->acceptableContentTypes = null;
 0253          $this->pathInfo = null;
 0254          $this->requestUri = null;
 0255          $this->baseUrl = null;
 0256          $this->basePath = null;
 0257          $this->method = null;
 0258          $this->format = null;
 0259      }
 0260   
 0261      /**
 0262       * Creates a new request with values from PHP's super globals.
 0263       *
 0264       * @return Request A new request
 0265       */
 0266      public static function createFromGlobals()
 0267      {
 0268          // With the php's bug #66606, the php's built-in web server
 0269          // stores the Content-Type and Content-Length header values in
 0270          // HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH fields.
 0271          $server = $_SERVER;
 0272          if ('cli-server' === PHP_SAPI) {
 0273              if (array_key_exists('HTTP_CONTENT_LENGTH', $_SERVER)) {
 0274                  $server['CONTENT_LENGTH'] = $_SERVER['HTTP_CONTENT_LENGTH'];
 0275              }
 0276              if (array_key_exists('HTTP_CONTENT_TYPE', $_SERVER)) {
 0277                  $server['CONTENT_TYPE'] = $_SERVER['HTTP_CONTENT_TYPE'];
 0278              }
 0279          }
 0280   
 0281          $request = self::createRequestFromFactory($_GET, $_POST, array(), $_COOKIE, $_FILES, $server);
 0282   
 0283          if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
 0284              && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH'))
 0285          ) {
 0286              parse_str($request->getContent(), $data);
 0287              $request->request = new ParameterBag($data);
 0288          }
 0289   
 0290          return $request;
 0291      }
 0292   
 0293      /**
 0294       * Creates a Request based on a given URI and configuration.
 0295       *
 0296       * The information contained in the URI always take precedence
 0297       * over the other information (server and parameters).
 0298       *
 0299       * @param string $uri        The URI
 0300       * @param string $method     The HTTP method
 0301       * @param array  $parameters The query (GET) or request (POST) parameters
 0302       * @param array  $cookies    The request cookies ($_COOKIE)
 0303       * @param array  $files      The request files ($_FILES)
 0304       * @param array  $server     The server parameters ($_SERVER)
 0305       * @param string $content    The raw body data
 0306       *
 0307       * @return Request A Request instance
 0308       */
 0309      public static function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null)
 0310      {
 0311          $server = array_replace(array(
 0312              'SERVER_NAME' => 'localhost',
 0313              'SERVER_PORT' => 80,
 0314              'HTTP_HOST' => 'localhost',
 0315              'HTTP_USER_AGENT' => 'Symfony/2.X',
 0316              'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
 0317              'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5',
 0318              'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
 0319              'REMOTE_ADDR' => '127.0.0.1',
 0320              'SCRIPT_NAME' => '',
 0321              'SCRIPT_FILENAME' => '',
 0322              'SERVER_PROTOCOL' => 'HTTP/1.1',
 0323              'REQUEST_TIME' => time(),
 0324          ), $server);
 0325   
 0326          $server['PATH_INFO'] = '';
 0327          $server['REQUEST_METHOD'] = strtoupper($method);
 0328   
 0329          $components = parse_url($uri);
 0330          if (isset($components['host'])) {
 0331              $server['SERVER_NAME'] = $components['host'];
 0332              $server['HTTP_HOST'] = $components['host'];
 0333          }
 0334   
 0335          if (isset($components['scheme'])) {
 0336              if ('https' === $components['scheme']) {
 0337                  $server['HTTPS'] = 'on';
 0338                  $server['SERVER_PORT'] = 443;
 0339              } else {
 0340                  unset($server['HTTPS']);
 0341                  $server['SERVER_PORT'] = 80;
 0342              }
 0343          }
 0344   
 0345          if (isset($components['port'])) {
 0346              $server['SERVER_PORT'] = $components['port'];
 0347              $server['HTTP_HOST'] = $server['HTTP_HOST'].':'.$components['port'];
 0348          }
 0349   
 0350          if (isset($components['user'])) {
 0351              $server['PHP_AUTH_USER'] = $components['user'];
 0352          }
 0353   
 0354          if (isset($components['pass'])) {
 0355              $server['PHP_AUTH_PW'] = $components['pass'];
 0356          }
 0357   
 0358          if (!isset($components['path'])) {
 0359              $components['path'] = '/';
 0360          }
 0361   
 0362          switch (strtoupper($method)) {
 0363              case 'POST':
 0364              case 'PUT':
 0365              case 'DELETE':
 0366                  if (!isset($server['CONTENT_TYPE'])) {
 0367                      $server['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
 0368                  }
 0369                  // no break
 0370              case 'PATCH':
 0371                  $request = $parameters;
 0372                  $query = array();
 0373                  break;
 0374              default:
 0375                  $request = array();
 0376                  $query = $parameters;
 0377                  break;
 0378          }
 0379   
 0380          $queryString = '';
 0381          if (isset($components['query'])) {
 0382              parse_str(html_entity_decode($components['query']), $qs);
 0383   
 0384              if ($query) {
 0385                  $query = array_replace($qs, $query);
 0386                  $queryString = http_build_query($query, '', '&');
 0387              } else {
 0388                  $query = $qs;
 0389                  $queryString = $components['query'];
 0390              }
 0391          } elseif ($query) {
 0392              $queryString = http_build_query($query, '', '&');
 0393          }
 0394   
 0395          $server['REQUEST_URI'] = $components['path'].('' !== $queryString ? '?'.$queryString : '');
 0396          $server['QUERY_STRING'] = $queryString;
 0397   
 0398          return self::createRequestFromFactory($query, $request, array(), $cookies, $files, $server, $content);
 0399      }
 0400   
 0401      /**
 0402       * Sets a callable able to create a Request instance.
 0403       *
 0404       * This is mainly useful when you need to override the Request class
 0405       * to keep BC with an existing system. It should not be used for any
 0406       * other purpose.
 0407       *
 0408       * @param callable|null $callable A PHP callable
 0409       */
 0410      public static function setFactory($callable)
 0411      {
 0412          self::$requestFactory = $callable;
 0413      }
 0414   
 0415      /**
 0416       * Clones a request and overrides some of its parameters.
 0417       *
 0418       * @param array $query      The GET parameters
 0419       * @param array $request    The POST parameters
 0420       * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
 0421       * @param array $cookies    The COOKIE parameters
 0422       * @param array $files      The FILES parameters
 0423       * @param array $server     The SERVER parameters
 0424       *
 0425       * @return Request The duplicated request
 0426       */
 0427      public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null)
 0428      {
 0429          $dup = clone $this;
 0430          if ($query !== null) {
 0431              $dup->query = new ParameterBag($query);
 0432          }
 0433          if ($request !== null) {
 0434              $dup->request = new ParameterBag($request);
 0435          }
 0436          if ($attributes !== null) {
 0437              $dup->attributes = new ParameterBag($attributes);
 0438          }
 0439          if ($cookies !== null) {
 0440              $dup->cookies = new ParameterBag($cookies);
 0441          }
 0442          if ($files !== null) {
 0443              $dup->files = new FileBag($files);
 0444          }
 0445          if ($server !== null) {
 0446              $dup->server = new ServerBag($server);
 0447              $dup->headers = new HeaderBag($dup->server->getHeaders());
 0448          }
 0449          $dup->languages = null;
 0450          $dup->charsets = null;
 0451          $dup->encodings = null;
 0452          $dup->acceptableContentTypes = null;
 0453          $dup->pathInfo = null;
 0454          $dup->requestUri = null;
 0455          $dup->baseUrl = null;
 0456          $dup->basePath = null;
 0457          $dup->method = null;
 0458          $dup->format = null;
 0459   
 0460          if (!$dup->get('_format') && $this->get('_format')) {
 0461              $dup->attributes->set('_format', $this->get('_format'));
 0462          }
 0463   
 0464          if (!$dup->getRequestFormat(null)) {
 0465              $dup->setRequestFormat($this->getRequestFormat(null));
 0466          }
 0467   
 0468          return $dup;
 0469      }
 0470   
 0471      /**
 0472       * Clones the current request.
 0473       *
 0474       * Note that the session is not cloned as duplicated requests
 0475       * are most of the time sub-requests of the main one.
 0476       */
 0477      public function __clone()
 0478      {
 0479          $this->query = clone $this->query;
 0480          $this->request = clone $this->request;
 0481          $this->attributes = clone $this->attributes;
 0482          $this->cookies = clone $this->cookies;
 0483          $this->files = clone $this->files;
 0484          $this->server = clone $this->server;
 0485          $this->headers = clone $this->headers;
 0486      }
 0487   
 0488      /**
 0489       * Returns the request as a string.
 0490       *
 0491       * @return string The request
 0492       */
 0493      public function __toString()
 0494      {
 0495          try {
 0496              $content = $this->getContent();
 0497          } catch (\LogicException $e) {
 0498              return trigger_error($e, E_USER_ERROR);
 0499          }
 0500   
 0501          return
 0502              sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n".
 0503              $this->headers."\r\n".
 0504              $content;
 0505      }
 0506   
 0507      /**
 0508       * Overrides the PHP global variables according to this request instance.
 0509       *
 0510       * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE.
 0511       * $_FILES is never overridden, see rfc1867
 0512       */
 0513      public function overrideGlobals()
 0514      {
 0515          $this->server->set('QUERY_STRING', static::normalizeQueryString(http_build_query($this->query->all(), null, '&')));
 0516   
 0517          $_GET = $this->query->all();
 0518          $_POST = $this->request->all();
 0519          $_SERVER = $this->server->all();
 0520          $_COOKIE = $this->cookies->all();
 0521   
 0522          foreach ($this->headers->all() as $key => $value) {
 0523              $key = strtoupper(str_replace('-', '_', $key));
 0524              if (in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH'))) {
 0525                  $_SERVER[$key] = implode(', ', $value);
 0526              } else {
 0527                  $_SERVER['HTTP_'.$key] = implode(', ', $value);
 0528              }
 0529          }
 0530   
 0531          $request = array('g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE);
 0532   
 0533          $requestOrder = ini_get('request_order') ?: ini_get('variables_order');
 0534          $requestOrder = preg_replace('#[^cgp]#', '', strtolower($requestOrder)) ?: 'gp';
 0535   
 0536          $_REQUEST = array();
 0537          foreach (str_split($requestOrder) as $order) {
 0538              $_REQUEST = array_merge($_REQUEST, $request[$order]);
 0539          }
 0540      }
 0541   
 0542      /**
 0543       * Sets a list of trusted proxies.
 0544       *
 0545       * You should only list the reverse proxies that you manage directly.
 0546       *
 0547       * @param array $proxies A list of trusted proxies
 0548       */
 0549      public static function setTrustedProxies(array $proxies)
 0550      {
 0551          self::$trustedProxies = $proxies;
 0552      }
 0553   
 0554      /**
 0555       * Gets the list of trusted proxies.
 0556       *
 0557       * @return array An array of trusted proxies
 0558       */
 0559      public static function getTrustedProxies()
 0560      {
 0561          return self::$trustedProxies;
 0562      }
 0563   
 0564      /**
 0565       * Sets a list of trusted host patterns.
 0566       *
 0567       * You should only list the hosts you manage using regexs.
 0568       *
 0569       * @param array $hostPatterns A list of trusted host patterns
 0570       */
 0571      public static function setTrustedHosts(array $hostPatterns)
 0572      {
 0573          self::$trustedHostPatterns = array_map(function ($hostPattern) {
 0574              return sprintf('#%s#i', $hostPattern);
 0575          }, $hostPatterns);
 0576          // we need to reset trusted hosts on trusted host patterns change
 0577          self::$trustedHosts = array();
 0578      }
 0579   
 0580      /**
 0581       * Gets the list of trusted host patterns.
 0582       *
 0583       * @return array An array of trusted host patterns
 0584       */
 0585      public static function getTrustedHosts()
 0586      {
 0587          return self::$trustedHostPatterns;
 0588      }
 0589   
 0590      /**
 0591       * Sets the name for trusted headers.
 0592       *
 0593       * The following header keys are supported:
 0594       *
 0595       *  * Request::HEADER_CLIENT_IP:    defaults to X-Forwarded-For   (see getClientIp())
 0596       *  * Request::HEADER_CLIENT_HOST:  defaults to X-Forwarded-Host  (see getHost())
 0597       *  * Request::HEADER_CLIENT_PORT:  defaults to X-Forwarded-Port  (see getPort())
 0598       *  * Request::HEADER_CLIENT_PROTO: defaults to X-Forwarded-Proto (see getScheme() and isSecure())
 0599       *
 0600       * Setting an empty value allows to disable the trusted header for the given key.
 0601       *
 0602       * @param string $key   The header key
 0603       * @param string $value The header name
 0604       *
 0605       * @throws \InvalidArgumentException
 0606       */
 0607      public static function setTrustedHeaderName($key, $value)
 0608      {
 0609          if (!array_key_exists($key, self::$trustedHeaders)) {
 0610              throw new \InvalidArgumentException(sprintf('Unable to set the trusted header name for key "%s".', $key));
 0611          }
 0612   
 0613          self::$trustedHeaders[$key] = $value;
 0614      }
 0615   
 0616      /**
 0617       * Gets the trusted proxy header name.
 0618       *
 0619       * @param string $key The header key
 0620       *
 0621       * @return string The header name
 0622       *
 0623       * @throws \InvalidArgumentException
 0624       */
 0625      public static function getTrustedHeaderName($key)
 0626      {
 0627          if (!array_key_exists($key, self::$trustedHeaders)) {
 0628              throw new \InvalidArgumentException(sprintf('Unable to get the trusted header name for key "%s".', $key));
 0629          }
 0630   
 0631          return self::$trustedHeaders[$key];
 0632      }
 0633   
 0634      /**
 0635       * Normalizes a query string.
 0636       *
 0637       * It builds a normalized query string, where keys/value pairs are alphabetized,
 0638       * have consistent escaping and unneeded delimiters are removed.
 0639       *
 0640       * @param string $qs Query string
 0641       *
 0642       * @return string A normalized query string for the Request
 0643       */
 0644      public static function normalizeQueryString($qs)
 0645      {
 0646          if ('' == $qs) {
 0647              return '';
 0648          }
 0649   
 0650          $parts = array();
 0651          $order = array();
 0652   
 0653          foreach (explode('&', $qs) as $param) {
 0654              if ('' === $param || '=' === $param[0]) {
 0655                  // Ignore useless delimiters, e.g. "x=y&".
 0656                  // Also ignore pairs with empty key, even if there was a value, e.g. "=value", as such nameless values cannot be retrieved anyway.
 0657                  // PHP also does not include them when building _GET.
 0658                  continue;
 0659              }
 0660   
 0661              $keyValuePair = explode('=', $param, 2);
 0662   
 0663              // GET parameters, that are submitted from a HTML form, encode spaces as "+" by default (as defined in enctype application/x-www-form-urlencoded).
 0664              // 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
 0665              // RFC 3986 with rawurlencode.
 0666              $parts[] = isset($keyValuePair[1]) ?
 0667                  rawurlencode(urldecode($keyValuePair[0])).'='.rawurlencode(urldecode($keyValuePair[1])) :
 0668                  rawurlencode(urldecode($keyValuePair[0]));
 0669              $order[] = urldecode($keyValuePair[0]);
 0670          }
 0671   
 0672          array_multisort($order, SORT_ASC, $parts);
 0673   
 0674          return implode('&', $parts);
 0675      }
 0676   
 0677      /**
 0678       * Enables support for the _method request parameter to determine the intended HTTP method.
 0679       *
 0680       * Be warned that enabling this feature might lead to CSRF issues in your code.
 0681       * Check that you are using CSRF tokens when required.
 0682       * If the HTTP method parameter override is enabled, an html-form with method "POST" can be altered
 0683       * and used to send a "PUT" or "DELETE" request via the _method request parameter.
 0684       * If these methods are not protected against CSRF, this presents a possible vulnerability.
 0685       *
 0686       * The HTTP method can only be overridden when the real HTTP method is POST.
 0687       */
 0688      public static function enableHttpMethodParameterOverride()
 0689      {
 0690          self::$httpMethodParameterOverride = true;
 0691      }
 0692   
 0693      /**
 0694       * Checks whether support for the _method request parameter is enabled.
 0695       *
 0696       * @return bool True when the _method request parameter is enabled, false otherwise
 0697       */
 0698      public static function getHttpMethodParameterOverride()
 0699      {
 0700          return self::$httpMethodParameterOverride;
 0701      }
 0702   
 0703      /**
 0704       * Gets a "parameter" value.
 0705       *
 0706       * This method is mainly useful for libraries that want to provide some flexibility.
 0707       *
 0708       * Order of precedence: GET, PATH, POST
 0709       *
 0710       * Avoid using this method in controllers:
 0711       *
 0712       *  * slow
 0713       *  * prefer to get from a "named" source
 0714       *
 0715       * It is better to explicitly get request parameters from the appropriate
 0716       * public property instead (query, attributes, request).
 0717       *
 0718       * Note: Finding deep items is deprecated since version 2.8, to be removed in 3.0.
 0719       *
 0720       * @param string $key     the key
 0721       * @param mixed  $default the default value if the parameter key does not exist
 0722       * @param bool   $deep    is parameter deep in multidimensional array
 0723       *
 0724       * @return mixed
 0725       */
 0726      public function get($key, $default = null, $deep = false)
 0727      {
 0728          if ($deep) {
 0729              @trigger_error('Using paths to find deeper items in '.__METHOD__.' is deprecated since version 2.8 and will be removed in 3.0. Filter the returned value in your own code instead.', E_USER_DEPRECATED);
 0730          }
 0731   
 0732          if ($this !== $result = $this->query->get($key, $this, $deep)) {
 0733              return $result;
 0734          }
 0735   
 0736          if ($this !== $result = $this->attributes->get($key, $this, $deep)) {
 0737              return $result;
 0738          }
 0739   
 0740          if ($this !== $result = $this->request->get($key, $this, $deep)) {
 0741              return $result;
 0742          }
 0743   
 0744          return $default;
 0745      }
 0746   
 0747      /**
 0748       * Gets the Session.
 0749       *
 0750       * @return SessionInterface|null The session
 0751       */
 0752      public function getSession()
 0753      {
 0754          return $this->session;
 0755      }
 0756   
 0757      /**
 0758       * Whether the request contains a Session which was started in one of the
 0759       * previous requests.
 0760       *
 0761       * @return bool
 0762       */
 0763      public function hasPreviousSession()
 0764      {
 0765          // the check for $this->session avoids malicious users trying to fake a session cookie with proper name
 0766          return $this->hasSession() && $this->cookies->has($this->session->getName());
 0767      }
 0768   
 0769      /**
 0770       * Whether the request contains a Session object.
 0771       *
 0772       * This method does not give any information about the state of the session object,
 0773       * like whether the session is started or not. It is just a way to check if this Request
 0774       * is associated with a Session instance.
 0775       *
 0776       * @return bool true when the Request contains a Session object, false otherwise
 0777       */
 0778      public function hasSession()
 0779      {
 0780          return null !== $this->session;
 0781      }
 0782   
 0783      /**
 0784       * Sets the Session.
 0785       *
 0786       * @param SessionInterface $session The Session
 0787       */
 0788      public function setSession(SessionInterface $session)
 0789      {
 0790          $this->session = $session;
 0791      }
 0792   
 0793      /**
 0794       * Returns the client IP addresses.
 0795       *
 0796       * In the returned array the most trusted IP address is first, and the
 0797       * least trusted one last. The "real" client IP address is the last one,
 0798       * but this is also the least trusted one. Trusted proxies are stripped.
 0799       *
 0800       * Use this method carefully; you should use getClientIp() instead.
 0801       *
 0802       * @return array The client IP addresses
 0803       *
 0804       * @see getClientIp()
 0805       */
 0806      public function getClientIps()
 0807      {
 0808          $clientIps = array();
 0809          $ip = $this->server->get('REMOTE_ADDR');
 0810   
 0811          if (!$this->isFromTrustedProxy()) {
 0812              return array($ip);
 0813          }
 0814   
 0815          $hasTrustedForwardedHeader = self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED]);
 0816          $hasTrustedClientIpHeader = self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP]);
 0817   
 0818          if ($hasTrustedForwardedHeader) {
 0819              $forwardedHeader = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
 0820              preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches);
 0821              $forwardedClientIps = $matches[3];
 0822   
 0823              $forwardedClientIps = $this->normalizeAndFilterClientIps($forwardedClientIps, $ip);
 0824              $clientIps = $forwardedClientIps;
 0825          }
 0826   
 0827          if ($hasTrustedClientIpHeader) {
 0828              $xForwardedForClientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP])));
 0829   
 0830              $xForwardedForClientIps = $this->normalizeAndFilterClientIps($xForwardedForClientIps, $ip);
 0831              $clientIps = $xForwardedForClientIps;
 0832          }
 0833   
 0834          if ($hasTrustedForwardedHeader && $hasTrustedClientIpHeader && $forwardedClientIps !== $xForwardedForClientIps) {
 0835              throw new ConflictingHeadersException('The request has both a trusted Forwarded header and a trusted Client IP header, conflicting with each other with regards to the originating IP addresses of the request. This is the result of a misconfiguration. You should either configure your proxy only to send one of these headers, or configure Symfony to distrust one of them.');
 0836          }
 0837   
 0838          if (!$hasTrustedForwardedHeader && !$hasTrustedClientIpHeader) {
 0839              return $this->normalizeAndFilterClientIps(array(), $ip);
 0840          }
 0841   
 0842          return $clientIps;
 0843      }
 0844   
 0845      /**
 0846       * Returns the client IP address.
 0847       *
 0848       * This method can read the client IP address from the "X-Forwarded-For" header
 0849       * when trusted proxies were set via "setTrustedProxies()". The "X-Forwarded-For"
 0850       * header value is a comma+space separated list of IP addresses, the left-most
 0851       * being the original client, and each successive proxy that passed the request
 0852       * adding the IP address where it received the request from.
 0853       *
 0854       * If your reverse proxy uses a different header name than "X-Forwarded-For",
 0855       * ("Client-Ip" for instance), configure it via "setTrustedHeaderName()" with
 0856       * the "client-ip" key.
 0857       *
 0858       * @return string The client IP address
 0859       *
 0860       * @see getClientIps()
 0861       * @see http://en.wikipedia.org/wiki/X-Forwarded-For
 0862       */
 0863      public function getClientIp()
 0864      {
 0865          $ipAddresses = $this->getClientIps();
 0866   
 0867          return $ipAddresses[0];
 0868      }
 0869   
 0870      /**
 0871       * Returns current script name.
 0872       *
 0873       * @return string
 0874       */
 0875      public function getScriptName()
 0876      {
 0877          return $this->server->get('SCRIPT_NAME', $this->server->get('ORIG_SCRIPT_NAME', ''));
 0878      }
 0879   
 0880      /**
 0881       * Returns the path being requested relative to the executed script.
 0882       *
 0883       * The path info always starts with a /.
 0884       *
 0885       * Suppose this request is instantiated from /mysite on localhost:
 0886       *
 0887       *  * http://localhost/mysite              returns an empty string
 0888       *  * http://localhost/mysite/about        returns '/about'
 0889       *  * http://localhost/mysite/enco%20ded   returns '/enco%20ded'
 0890       *  * http://localhost/mysite/about?var=1  returns '/about'
 0891       *
 0892       * @return string The raw path (i.e. not urldecoded)
 0893       */
 0894      public function getPathInfo()
 0895      {
 0896          if (null === $this->pathInfo) {
 0897              $this->pathInfo = $this->preparePathInfo();
 0898          }
 0899   
 0900          return $this->pathInfo;
 0901      }
 0902   
 0903      /**
 0904       * Returns the root path from which this request is executed.
 0905       *
 0906       * Suppose that an index.php file instantiates this request object:
 0907       *
 0908       *  * http://localhost/index.php         returns an empty string
 0909       *  * http://localhost/index.php/page    returns an empty string
 0910       *  * http://localhost/web/index.php     returns '/web'
 0911       *  * http://localhost/we%20b/index.php  returns '/we%20b'
 0912       *
 0913       * @return string The raw path (i.e. not urldecoded)
 0914       */
 0915      public function getBasePath()
 0916      {
 0917          if (null === $this->basePath) {
 0918              $this->basePath = $this->prepareBasePath();
 0919          }
 0920   
 0921          return $this->basePath;
 0922      }
 0923   
 0924      /**
 0925       * Returns the root URL from which this request is executed.
 0926       *
 0927       * The base URL never ends with a /.
 0928       *
 0929       * This is similar to getBasePath(), except that it also includes the
 0930       * script filename (e.g. index.php) if one exists.
 0931       *
 0932       * @return string The raw URL (i.e. not urldecoded)
 0933       */
 0934      public function getBaseUrl()
 0935      {
 0936          if (null === $this->baseUrl) {
 0937              $this->baseUrl = $this->prepareBaseUrl();
 0938          }
 0939   
 0940          return $this->baseUrl;
 0941      }
 0942   
 0943      /**
 0944       * Gets the request's scheme.
 0945       *
 0946       * @return string
 0947       */
 0948      public function getScheme()
 0949      {
 0950          return $this->isSecure() ? 'https' : 'http';
 0951      }
 0952   
 0953      /**
 0954       * Returns the port on which the request is made.
 0955       *
 0956       * This method can read the client port from the "X-Forwarded-Port" header
 0957       * when trusted proxies were set via "setTrustedProxies()".
 0958       *
 0959       * The "X-Forwarded-Port" header must contain the client port.
 0960       *
 0961       * If your reverse proxy uses a different header name than "X-Forwarded-Port",
 0962       * configure it via "setTrustedHeaderName()" with the "client-port" key.
 0963       *
 0964       * @return string
 0965       */
 0966      public function getPort()
 0967      {
 0968          if ($this->isFromTrustedProxy()) {
 0969              if (self::$trustedHeaders[self::HEADER_CLIENT_PORT] && $port = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PORT])) {
 0970                  return $port;
 0971              }
 0972   
 0973              if (self::$trustedHeaders[self::HEADER_CLIENT_PROTO] && 'https' === $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PROTO], 'http')) {
 0974                  return 443;
 0975              }
 0976          }
 0977   
 0978          if ($host = $this->headers->get('HOST')) {
 0979              if ($host[0] === '[') {
 0980                  $pos = strpos($host, ':', strrpos($host, ']'));
 0981              } else {
 0982                  $pos = strrpos($host, ':');
 0983              }
 0984   
 0985              if (false !== $pos) {
 0986                  return (int) substr($host, $pos + 1);
 0987              }
 0988   
 0989              return 'https' === $this->getScheme() ? 443 : 80;
 0990          }
 0991   
 0992          return $this->server->get('SERVER_PORT');
 0993      }
 0994   
 0995      /**
 0996       * Returns the user.
 0997       *
 0998       * @return string|null
 0999       */
 1000      public function getUser()
 1001      {
 1002          return $this->headers->get('PHP_AUTH_USER');
 1003      }
 1004   
 1005      /**
 1006       * Returns the password.
 1007       *
 1008       * @return string|null
 1009       */
 1010      public function getPassword()
 1011      {
 1012          return $this->headers->get('PHP_AUTH_PW');
 1013      }
 1014   
 1015      /**
 1016       * Gets the user info.
 1017       *
 1018       * @return string A user name and, optionally, scheme-specific information about how to gain authorization to access the server
 1019       */
 1020      public function getUserInfo()
 1021      {
 1022          $userinfo = $this->getUser();
 1023   
 1024          $pass = $this->getPassword();
 1025          if ('' != $pass) {
 1026              $userinfo .= ":$pass";
 1027          }
 1028   
 1029          return $userinfo;
 1030      }
 1031   
 1032      /**
 1033       * Returns the HTTP host being requested.
 1034       *
 1035       * The port name will be appended to the host if it's non-standard.
 1036       *
 1037       * @return string
 1038       */
 1039      public function getHttpHost()
 1040      {
 1041          $scheme = $this->getScheme();
 1042          $port = $this->getPort();
 1043   
 1044          if (('http' == $scheme && $port == 80) || ('https' == $scheme && $port == 443)) {
 1045              return $this->getHost();
 1046          }
 1047   
 1048          return $this->getHost().':'.$port;
 1049      }
 1050   
 1051      /**
 1052       * Returns the requested URI (path and query string).
 1053       *
 1054       * @return string The raw URI (i.e. not URI decoded)
 1055       */
 1056      public function getRequestUri()
 1057      {
 1058          if (null === $this->requestUri) {
 1059              $this->requestUri = $this->prepareRequestUri();
 1060          }
 1061   
 1062          return $this->requestUri;
 1063      }
 1064   
 1065      /**
 1066       * Gets the scheme and HTTP host.
 1067       *
 1068       * If the URL was called with basic authentication, the user
 1069       * and the password are not added to the generated string.
 1070       *
 1071       * @return string The scheme and HTTP host
 1072       */
 1073      public function getSchemeAndHttpHost()
 1074      {
 1075          return $this->getScheme().'://'.$this->getHttpHost();
 1076      }
 1077   
 1078      /**
 1079       * Generates a normalized URI (URL) for the Request.
 1080       *
 1081       * @return string A normalized URI (URL) for the Request
 1082       *
 1083       * @see getQueryString()
 1084       */
 1085      public function getUri()
 1086      {
 1087          if (null !== $qs = $this->getQueryString()) {
 1088              $qs = '?'.$qs;
 1089          }
 1090   
 1091          return $this->getSchemeAndHttpHost().$this->getBaseUrl().$this->getPathInfo().$qs;
 1092      }
 1093   
 1094      /**
 1095       * Generates a normalized URI for the given path.
 1096       *
 1097       * @param string $path A path to use instead of the current one
 1098       *
 1099       * @return string The normalized URI for the path
 1100       */
 1101      public function getUriForPath($path)
 1102      {
 1103          return $this->getSchemeAndHttpHost().$this->getBaseUrl().$path;
 1104      }
 1105   
 1106      /**
 1107       * Returns the path as relative reference from the current Request path.
 1108       *
 1109       * Only the URIs path component (no schema, host etc.) is relevant and must be given.
 1110       * Both paths must be absolute and not contain relative parts.
 1111       * Relative URLs from one resource to another are useful when generating self-contained downloadable document archives.
 1112       * Furthermore, they can be used to reduce the link size in documents.
 1113       *
 1114       * Example target paths, given a base path of "/a/b/c/d":
 1115       * - "/a/b/c/d"     -> ""
 1116       * - "/a/b/c/"      -> "./"
 1117       * - "/a/b/"        -> "../"
 1118       * - "/a/b/c/other" -> "other"
 1119       * - "/a/x/y"       -> "../../x/y"
 1120       *
 1121       * @param string $path The target path
 1122       *
 1123       * @return string The relative target path
 1124       */
 1125      public function getRelativeUriForPath($path)
 1126      {
 1127          // be sure that we are dealing with an absolute path
 1128          if (!isset($path[0]) || '/' !== $path[0]) {
 1129              return $path;
 1130          }
 1131   
 1132          if ($path === $basePath = $this->getPathInfo()) {
 1133              return '';
 1134          }
 1135   
 1136          $sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath);
 1137          $targetDirs = explode('/', isset($path[0]) && '/' === $path[0] ? substr($path, 1) : $path);
 1138          array_pop($sourceDirs);
 1139          $targetFile = array_pop($targetDirs);
 1140   
 1141          foreach ($sourceDirs as $i => $dir) {
 1142              if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) {
 1143                  unset($sourceDirs[$i], $targetDirs[$i]);
 1144              } else {
 1145                  break;
 1146              }
 1147          }
 1148   
 1149          $targetDirs[] = $targetFile;
 1150          $path = str_repeat('../', count($sourceDirs)).implode('/', $targetDirs);
 1151   
 1152          // A reference to the same base directory or an empty subdirectory must be prefixed with "./".
 1153          // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
 1154          // as the first segment of a relative-path reference, as it would be mistaken for a scheme name
 1155          // (see http://tools.ietf.org/html/rfc3986#section-4.2).
 1156          return !isset($path[0]) || '/' === $path[0]
 1157              || false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos)
 1158              ? "./$path" : $path;
 1159      }
 1160   
 1161      /**
 1162       * Generates the normalized query string for the Request.
 1163       *
 1164       * It builds a normalized query string, where keys/value pairs are alphabetized
 1165       * and have consistent escaping.
 1166       *
 1167       * @return string|null A normalized query string for the Request
 1168       */
 1169      public function getQueryString()
 1170      {
 1171          $qs = static::normalizeQueryString($this->server->get('QUERY_STRING'));
 1172   
 1173          return '' === $qs ? null : $qs;
 1174      }
 1175   
 1176      /**
 1177       * Checks whether the request is secure or not.
 1178       *
 1179       * This method can read the client protocol from the "X-Forwarded-Proto" header
 1180       * when trusted proxies were set via "setTrustedProxies()".
 1181       *
 1182       * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http".
 1183       *
 1184       * If your reverse proxy uses a different header name than "X-Forwarded-Proto"
 1185       * ("SSL_HTTPS" for instance), configure it via "setTrustedHeaderName()" with
 1186       * the "client-proto" key.
 1187       *
 1188       * @return bool
 1189       */
 1190      public function isSecure()
 1191      {
 1192          if ($this->isFromTrustedProxy() && self::$trustedHeaders[self::HEADER_CLIENT_PROTO] && $proto = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PROTO])) {
 1193              return in_array(strtolower(current(explode(',', $proto))), array('https', 'on', 'ssl', '1'));
 1194          }
 1195   
 1196          $https = $this->server->get('HTTPS');
 1197   
 1198          return !empty($https) && 'off' !== strtolower($https);
 1199      }
 1200   
 1201      /**
 1202       * Returns the host name.
 1203       *
 1204       * This method can read the client host name from the "X-Forwarded-Host" header
 1205       * when trusted proxies were set via "setTrustedProxies()".
 1206       *
 1207       * The "X-Forwarded-Host" header must contain the client host name.
 1208       *
 1209       * If your reverse proxy uses a different header name than "X-Forwarded-Host",
 1210       * configure it via "setTrustedHeaderName()" with the "client-host" key.
 1211       *
 1212       * @return string
 1213       *
 1214       * @throws \UnexpectedValueException when the host name is invalid
 1215       */
 1216      public function getHost()
 1217      {
 1218          if ($this->isFromTrustedProxy() && self::$trustedHeaders[self::HEADER_CLIENT_HOST] && $host = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_HOST])) {
 1219              $elements = explode(',', $host);
 1220   
 1221              $host = $elements[count($elements) - 1];
 1222          } elseif (!$host = $this->headers->get('HOST')) {
 1223              if (!$host = $this->server->get('SERVER_NAME')) {
 1224                  $host = $this->server->get('SERVER_ADDR', '');
 1225              }
 1226          }
 1227   
 1228          // trim and remove port number from host
 1229          // host is lowercase as per RFC 952/2181
 1230          $host = strtolower(preg_replace('/:\d+$/', '', trim($host)));
 1231   
 1232          // as the host can come from the user (HTTP_HOST and depending on the configuration, SERVER_NAME too can come from the user)
 1233          // check that it does not contain forbidden characters (see RFC 952 and RFC 2181)
 1234          // use preg_replace() instead of preg_match() to prevent DoS attacks with long host names
 1235          if ($host && '' !== preg_replace('/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/', '', $host)) {
 1236              throw new \UnexpectedValueException(sprintf('Invalid Host "%s"', $host));
 1237          }
 1238   
 1239          if (count(self::$trustedHostPatterns) > 0) {
 1240              // to avoid host header injection attacks, you should provide a list of trusted host patterns
 1241   
 1242              if (in_array($host, self::$trustedHosts)) {
 1243                  return $host;
 1244              }
 1245   
 1246              foreach (self::$trustedHostPatterns as $pattern) {
 1247                  if (preg_match($pattern, $host)) {
 1248                      self::$trustedHosts[] = $host;
 1249   
 1250                      return $host;
 1251                  }
 1252              }
 1253   
 1254              throw new \UnexpectedValueException(sprintf('Untrusted Host "%s"', $host));
 1255          }
 1256   
 1257          return $host;
 1258      }
 1259   
 1260      /**
 1261       * Sets the request method.
 1262       *
 1263       * @param string $method
 1264       */
 1265      public function setMethod($method)
 1266      {
 1267          $this->method = null;
 1268          $this->server->set('REQUEST_METHOD', $method);
 1269      }
 1270   
 1271      /**
 1272       * Gets the request "intended" method.
 1273       *
 1274       * If the X-HTTP-Method-Override header is set, and if the method is a POST,
 1275       * then it is used to determine the "real" intended HTTP method.
 1276       *
 1277       * The _method request parameter can also be used to determine the HTTP method,
 1278       * but only if enableHttpMethodParameterOverride() has been called.
 1279       *
 1280       * The method is always an uppercased string.
 1281       *
 1282       * @return string The request method
 1283       *
 1284       * @see getRealMethod()
 1285       */
 1286      public function getMethod()
 1287      {
 1288          if (null === $this->method) {
 1289              $this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
 1290   
 1291              if ('POST' === $this->method) {
 1292                  if ($method = $this->headers->get('X-HTTP-METHOD-OVERRIDE')) {
 1293                      $this->method = strtoupper($method);
 1294                  } elseif (self::$httpMethodParameterOverride) {
 1295                      $this->method = strtoupper($this->request->get('_method', $this->query->get('_method', 'POST')));
 1296                  }
 1297              }
 1298          }
 1299   
 1300          return $this->method;
 1301      }
 1302   
 1303      /**
 1304       * Gets the "real" request method.
 1305       *
 1306       * @return string The request method
 1307       *
 1308       * @see getMethod()
 1309       */
 1310      public function getRealMethod()
 1311      {
 1312          return strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
 1313      }
 1314   
 1315      /**
 1316       * Gets the mime type associated with the format.
 1317       *
 1318       * @param string $format The format
 1319       *
 1320       * @return string The associated mime type (null if not found)
 1321       */
 1322      public function getMimeType($format)
 1323      {
 1324          if (null === static::$formats) {
 1325              static::initializeFormats();
 1326          }
 1327   
 1328          return isset(static::$formats[$format]) ? static::$formats[$format][0] : null;
 1329      }
 1330   
 1331      /**
 1332       * Gets the format associated with the mime type.
 1333       *
 1334       * @param string $mimeType The associated mime type
 1335       *
 1336       * @return string|null The format (null if not found)
 1337       */
 1338      public function getFormat($mimeType)
 1339      {
 1340          $canonicalMimeType = null;
 1341          if (false !== $pos = strpos($mimeType, ';')) {
 1342              $canonicalMimeType = substr($mimeType, 0, $pos);
 1343          }
 1344   
 1345          if (null === static::$formats) {
 1346              static::initializeFormats();
 1347          }
 1348   
 1349          foreach (static::$formats as $format => $mimeTypes) {
 1350              if (in_array($mimeType, (array) $mimeTypes)) {
 1351                  return $format;
 1352              }
 1353              if (null !== $canonicalMimeType && in_array($canonicalMimeType, (array) $mimeTypes)) {
 1354                  return $format;
 1355              }
 1356          }
 1357      }
 1358   
 1359      /**
 1360       * Associates a format with mime types.
 1361       *
 1362       * @param string       $format    The format
 1363       * @param string|array $mimeTypes The associated mime types (the preferred one must be the first as it will be used as the content type)
 1364       */
 1365      public function setFormat($format, $mimeTypes)
 1366      {
 1367          if (null === static::$formats) {
 1368              static::initializeFormats();
 1369          }
 1370   
 1371          static::$formats[$format] = is_array($mimeTypes) ? $mimeTypes : array($mimeTypes);
 1372      }
 1373   
 1374      /**
 1375       * Gets the request format.
 1376       *
 1377       * Here is the process to determine the format:
 1378       *
 1379       *  * format defined by the user (with setRequestFormat())
 1380       *  * _format request parameter
 1381       *  * $default
 1382       *
 1383       * @param string $default The default format
 1384       *
 1385       * @return string The request format
 1386       */
 1387      public function getRequestFormat($default = 'html')
 1388      {
 1389          if (null === $this->format) {
 1390              $this->format = $this->get('_format', $default);
 1391          }
 1392   
 1393          return $this->format;
 1394      }
 1395   
 1396      /**
 1397       * Sets the request format.
 1398       *
 1399       * @param string $format The request format
 1400       */
 1401      public function setRequestFormat($format)
 1402      {
 1403          $this->format = $format;
 1404      }
 1405   
 1406      /**
 1407       * Gets the format associated with the request.
 1408       *
 1409       * @return string|null The format (null if no content type is present)
 1410       */
 1411      public function getContentType()
 1412      {
 1413          return $this->getFormat($this->headers->get('CONTENT_TYPE'));
 1414      }
 1415   
 1416      /**
 1417       * Sets the default locale.
 1418       *
 1419       * @param string $locale
 1420       */
 1421      public function setDefaultLocale($locale)
 1422      {
 1423          $this->defaultLocale = $locale;
 1424   
 1425          if (null === $this->locale) {
 1426              $this->setPhpDefaultLocale($locale);
 1427          }
 1428      }
 1429   
 1430      /**
 1431       * Get the default locale.
 1432       *
 1433       * @return string
 1434       */
 1435      public function getDefaultLocale()
 1436      {
 1437          return $this->defaultLocale;
 1438      }
 1439   
 1440      /**
 1441       * Sets the locale.
 1442       *
 1443       * @param string $locale
 1444       */
 1445      public function setLocale($locale)
 1446      {
 1447          $this->setPhpDefaultLocale($this->locale = $locale);
 1448      }
 1449   
 1450      /**
 1451       * Get the locale.
 1452       *
 1453       * @return string
 1454       */
 1455      public function getLocale()
 1456      {
 1457          return null === $this->locale ? $this->defaultLocale : $this->locale;
 1458      }
 1459   
 1460      /**
 1461       * Checks if the request method is of specified type.
 1462       *
 1463       * @param string $method Uppercase request method (GET, POST etc)
 1464       *
 1465       * @return bool
 1466       */
 1467      public function isMethod($method)
 1468      {
 1469          return $this->getMethod() === strtoupper($method);
 1470      }
 1471   
 1472      /**
 1473       * Checks whether the method is safe or not.
 1474       *
 1475       * @return bool
 1476       */
 1477      public function isMethodSafe()
 1478      {
 1479          return in_array($this->getMethod(), array('GET', 'HEAD', 'OPTIONS', 'TRACE'));
 1480      }
 1481   
 1482      /**
 1483       * Checks whether the method is cacheable or not.
 1484       *
 1485       * @return bool
 1486       */
 1487      public function isMethodCacheable()
 1488      {
 1489          return in_array($this->getMethod(), array('GET', 'HEAD'));
 1490      }
 1491   
 1492      /**
 1493       * Returns the request body content.
 1494       *
 1495       * @param bool $asResource If true, a resource will be returned
 1496       *
 1497       * @return string|resource The request body content or a resource to read the body stream
 1498       *
 1499       * @throws \LogicException
 1500       */
 1501      public function getContent($asResource = false)
 1502      {
 1503          $currentContentIsResource = is_resource($this->content);
 1504          if (PHP_VERSION_ID < 50600 && false === $this->content) {
 1505              throw new \LogicException('getContent() can only be called once when using the resource return type and PHP below 5.6.');
 1506          }
 1507   
 1508          if (true === $asResource) {
 1509              if ($currentContentIsResource) {
 1510                  rewind($this->content);
 1511   
 1512                  return $this->content;
 1513              }
 1514   
 1515              // Content passed in parameter (test)
 1516              if (is_string($this->content)) {
 1517                  $resource = fopen('php://temp', 'r+');
 1518                  fwrite($resource, $this->content);
 1519                  rewind($resource);
 1520   
 1521                  return $resource;
 1522              }
 1523   
 1524              $this->content = false;
 1525   
 1526              return fopen('php://input', 'rb');
 1527          }
 1528   
 1529          if ($currentContentIsResource) {
 1530              rewind($this->content);
 1531   
 1532              return stream_get_contents($this->content);
 1533          }
 1534   
 1535          if (null === $this->content || false === $this->content) {
 1536              $this->content = file_get_contents('php://input');
 1537          }
 1538   
 1539          return $this->content;
 1540      }
 1541   
 1542      /**
 1543       * Gets the Etags.
 1544       *
 1545       * @return array The entity tags
 1546       */
 1547      public function getETags()
 1548      {
 1549          return preg_split('/\s*,\s*/', $this->headers->get('if_none_match'), null, PREG_SPLIT_NO_EMPTY);
 1550      }
 1551   
 1552      /**
 1553       * @return bool
 1554       */
 1555      public function isNoCache()
 1556      {
 1557          return $this->headers->hasCacheControlDirective('no-cache') || 'no-cache' == $this->headers->get('Pragma');
 1558      }
 1559   
 1560      /**
 1561       * Returns the preferred language.
 1562       *
 1563       * @param array $locales An array of ordered available locales
 1564       *
 1565       * @return string|null The preferred locale
 1566       */
 1567      public function getPreferredLanguage(array $locales = null)
 1568      {
 1569          $preferredLanguages = $this->getLanguages();
 1570   
 1571          if (empty($locales)) {
 1572              return isset($preferredLanguages[0]) ? $preferredLanguages[0] : null;
 1573          }
 1574   
 1575          if (!$preferredLanguages) {
 1576              return $locales[0];
 1577          }
 1578   
 1579          $extendedPreferredLanguages = array();
 1580          foreach ($preferredLanguages as $language) {
 1581              $extendedPreferredLanguages[] = $language;
 1582              if (false !== $position = strpos($language, '_')) {
 1583                  $superLanguage = substr($language, 0, $position);
 1584                  if (!in_array($superLanguage, $preferredLanguages)) {
 1585                      $extendedPreferredLanguages[] = $superLanguage;
 1586                  }
 1587              }
 1588          }
 1589   
 1590          $preferredLanguages = array_values(array_intersect($extendedPreferredLanguages, $locales));
 1591   
 1592          return isset($preferredLanguages[0]) ? $preferredLanguages[0] : $locales[0];
 1593      }
 1594   
 1595      /**
 1596       * Gets a list of languages acceptable by the client browser.
 1597       *
 1598       * @return array Languages ordered in the user browser preferences
 1599       */
 1600      public function getLanguages()
 1601      {
 1602          if (null !== $this->languages) {
 1603              return $this->languages;
 1604          }
 1605   
 1606          $languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all();
 1607          $this->languages = array();
 1608          foreach ($languages as $lang => $acceptHeaderItem) {
 1609              if (false !== strpos($lang, '-')) {
 1610                  $codes = explode('-', $lang);
 1611                  if ('i' === $codes[0]) {
 1612                      // Language not listed in ISO 639 that are not variants
 1613                      // of any listed language, which can be registered with the
 1614                      // i-prefix, such as i-cherokee
 1615                      if (count($codes) > 1) {
 1616                          $lang = $codes[1];
 1617                      }
 1618                  } else {
 1619                      for ($i = 0, $max = count($codes); $i < $max; ++$i) {
 1620                          if ($i === 0) {
 1621                              $lang = strtolower($codes[0]);
 1622                          } else {
 1623                              $lang .= '_'.strtoupper($codes[$i]);
 1624                          }
 1625                      }
 1626                  }
 1627              }
 1628   
 1629              $this->languages[] = $lang;
 1630          }
 1631   
 1632          return $this->languages;
 1633      }
 1634   
 1635      /**
 1636       * Gets a list of charsets acceptable by the client browser.
 1637       *
 1638       * @return array List of charsets in preferable order
 1639       */
 1640      public function getCharsets()
 1641      {
 1642          if (null !== $this->charsets) {
 1643              return $this->charsets;
 1644          }
 1645   
 1646          return $this->charsets = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all());
 1647      }
 1648   
 1649      /**
 1650       * Gets a list of encodings acceptable by the client browser.
 1651       *
 1652       * @return array List of encodings in preferable order
 1653       */
 1654      public function getEncodings()
 1655      {
 1656          if (null !== $this->encodings) {
 1657              return $this->encodings;
 1658          }
 1659   
 1660          return $this->encodings = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Encoding'))->all());
 1661      }
 1662   
 1663      /**
 1664       * Gets a list of content types acceptable by the client browser.
 1665       *
 1666       * @return array List of content types in preferable order
 1667       */
 1668      public function getAcceptableContentTypes()
 1669      {
 1670          if (null !== $this->acceptableContentTypes) {
 1671              return $this->acceptableContentTypes;
 1672          }
 1673   
 1674          return $this->acceptableContentTypes = array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all());
 1675      }
 1676   
 1677      /**
 1678       * Returns true if the request is a XMLHttpRequest.
 1679       *
 1680       * It works if your JavaScript library sets an X-Requested-With HTTP header.
 1681       * It is known to work with common JavaScript frameworks:
 1682       *
 1683       * @see http://en.wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript
 1684       *
 1685       * @return bool true if the request is an XMLHttpRequest, false otherwise
 1686       */
 1687      public function isXmlHttpRequest()
 1688      {
 1689          return 'XMLHttpRequest' == $this->headers->get('X-Requested-With');
 1690      }
 1691   
 1692      /*
 1693       * The following methods are derived from code of the Zend Framework (1.10dev - 2010-01-24)
 1694       *
 1695       * Code subject to the new BSD license (http://framework.zend.com/license/new-bsd).
 1696       *
 1697       * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 1698       */
 1699   
 1700      protected function prepareRequestUri()
 1701      {
 1702          $requestUri = '';
 1703   
 1704          if ($this->headers->has('X_ORIGINAL_URL')) {
 1705              // IIS with Microsoft Rewrite Module
 1706              $requestUri = $this->headers->get('X_ORIGINAL_URL');
 1707              $this->headers->remove('X_ORIGINAL_URL');
 1708              $this->server->remove('HTTP_X_ORIGINAL_URL');
 1709              $this->server->remove('UNENCODED_URL');
 1710              $this->server->remove('IIS_WasUrlRewritten');
 1711          } elseif ($this->headers->has('X_REWRITE_URL')) {
 1712              // IIS with ISAPI_Rewrite
 1713              $requestUri = $this->headers->get('X_REWRITE_URL');
 1714              $this->headers->remove('X_REWRITE_URL');
 1715          } elseif ($this->server->get('IIS_WasUrlRewritten') == '1' && $this->server->get('UNENCODED_URL') != '') {
 1716              // IIS7 with URL Rewrite: make sure we get the unencoded URL (double slash problem)
 1717              $requestUri = $this->server->get('UNENCODED_URL');
 1718              $this->server->remove('UNENCODED_URL');
 1719              $this->server->remove('IIS_WasUrlRewritten');
 1720          } elseif ($this->server->has('REQUEST_URI')) {
 1721              $requestUri = $this->server->get('REQUEST_URI');
 1722              // HTTP proxy reqs setup request URI with scheme and host [and port] + the URL path, only use URL path
 1723              $schemeAndHttpHost = $this->getSchemeAndHttpHost();
 1724              if (strpos($requestUri, $schemeAndHttpHost) === 0) {
 1725                  $requestUri = substr($requestUri, strlen($schemeAndHttpHost));
 1726              }
 1727          } elseif ($this->server->has('ORIG_PATH_INFO')) {
 1728              // IIS 5.0, PHP as CGI
 1729              $requestUri = $this->server->get('ORIG_PATH_INFO');
 1730              if ('' != $this->server->get('QUERY_STRING')) {
 1731                  $requestUri .= '?'.$this->server->get('QUERY_STRING');
 1732              }
 1733              $this->server->remove('ORIG_PATH_INFO');
 1734          }
 1735   
 1736          // normalize the request URI to ease creating sub-requests from this request
 1737          $this->server->set('REQUEST_URI', $requestUri);
 1738   
 1739          return $requestUri;
 1740      }
 1741   
 1742      /**
 1743       * Prepares the base URL.
 1744       *
 1745       * @return string
 1746       */
 1747      protected function prepareBaseUrl()
 1748      {
 1749          $filename = basename($this->server->get('SCRIPT_FILENAME'));
 1750   
 1751          if (basename($this->server->get('SCRIPT_NAME')) === $filename) {
 1752              $baseUrl = $this->server->get('SCRIPT_NAME');
 1753          } elseif (basename($this->server->get('PHP_SELF')) === $filename) {
 1754              $baseUrl = $this->server->get('PHP_SELF');
 1755          } elseif (basename($this->server->get('ORIG_SCRIPT_NAME')) === $filename) {
 1756              $baseUrl = $this->server->get('ORIG_SCRIPT_NAME'); // 1and1 shared hosting compatibility
 1757          } else {
 1758              // Backtrack up the script_filename to find the portion matching
 1759              // php_self
 1760              $path = $this->server->get('PHP_SELF', '');
 1761              $file = $this->server->get('SCRIPT_FILENAME', '');
 1762              $segs = explode('/', trim($file, '/'));
 1763              $segs = array_reverse($segs);
 1764              $index = 0;
 1765              $last = count($segs);
 1766              $baseUrl = '';
 1767              do {
 1768                  $seg = $segs[$index];
 1769                  $baseUrl = '/'.$seg.$baseUrl;
 1770                  ++$index;
 1771              } while ($last > $index && (false !== $pos = strpos($path, $baseUrl)) && 0 != $pos);
 1772          }
 1773   
 1774          // Does the baseUrl have anything in common with the request_uri?
 1775          $requestUri = $this->getRequestUri();
 1776   
 1777          if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) {
 1778              // full $baseUrl matches
 1779              return $prefix;
 1780          }
 1781   
 1782          if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, rtrim(dirname($baseUrl), '/'.DIRECTORY_SEPARATOR).'/')) {
 1783              // directory portion of $baseUrl matches
 1784              return rtrim($prefix, '/'.DIRECTORY_SEPARATOR);
 1785          }
 1786   
 1787          $truncatedRequestUri = $requestUri;
 1788          if (false !== $pos = strpos($requestUri, '?')) {
 1789              $truncatedRequestUri = substr($requestUri, 0, $pos);
 1790          }
 1791   
 1792          $basename = basename($baseUrl);
 1793          if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri), $basename)) {
 1794              // no match whatsoever; set it blank
 1795              return '';
 1796          }
 1797   
 1798          // If using mod_rewrite or ISAPI_Rewrite strip the script filename
 1799          // out of baseUrl. $pos !== 0 makes sure it is not matching a value
 1800          // from PATH_INFO or QUERY_STRING
 1801          if (strlen($requestUri) >= strlen($baseUrl) && (false !== $pos = strpos($requestUri, $baseUrl)) && $pos !== 0) {
 1802              $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl));
 1803          }
 1804   
 1805          return rtrim($baseUrl, '/'.DIRECTORY_SEPARATOR);
 1806      }
 1807   
 1808      /**
 1809       * Prepares the base path.
 1810       *
 1811       * @return string base path
 1812       */
 1813      protected function prepareBasePath()
 1814      {
 1815          $filename = basename($this->server->get('SCRIPT_FILENAME'));
 1816          $baseUrl = $this->getBaseUrl();
 1817          if (empty($baseUrl)) {
 1818              return '';
 1819          }
 1820   
 1821          if (basename($baseUrl) === $filename) {
 1822              $basePath = dirname($baseUrl);
 1823          } else {
 1824              $basePath = $baseUrl;
 1825          }
 1826   
 1827          if ('\\' === DIRECTORY_SEPARATOR) {
 1828              $basePath = str_replace('\\', '/', $basePath);
 1829          }
 1830   
 1831          return rtrim($basePath, '/');
 1832      }
 1833   
 1834      /**
 1835       * Prepares the path info.
 1836       *
 1837       * @return string path info
 1838       */
 1839      protected function preparePathInfo()
 1840      {
 1841          $baseUrl = $this->getBaseUrl();
 1842   
 1843          if (null === ($requestUri = $this->getRequestUri())) {
 1844              return '/';
 1845          }
 1846   
 1847          // Remove the query string from REQUEST_URI
 1848          if ($pos = strpos($requestUri, '?')) {
 1849              $requestUri = substr($requestUri, 0, $pos);
 1850          }
 1851   
 1852          $pathInfo = substr($requestUri, strlen($baseUrl));
 1853          if (null !== $baseUrl && (false === $pathInfo || '' === $pathInfo)) {
 1854              // If substr() returns false then PATH_INFO is set to an empty string
 1855              return '/';
 1856          } elseif (null === $baseUrl) {
 1857              return $requestUri;
 1858          }
 1859   
 1860          return (string) $pathInfo;
 1861      }
 1862   
 1863      /**
 1864       * Initializes HTTP request formats.
 1865       */
 1866      protected static function initializeFormats()
 1867      {
 1868          static::$formats = array(
 1869              'html' => array('text/html', 'application/xhtml+xml'),
 1870              'txt' => array('text/plain'),
 1871              'js' => array('application/javascript', 'application/x-javascript', 'text/javascript'),
 1872              'css' => array('text/css'),
 1873              'json' => array('application/json', 'application/x-json'),
 1874              'xml' => array('text/xml', 'application/xml', 'application/x-xml'),
 1875              'rdf' => array('application/rdf+xml'),
 1876              'atom' => array('application/atom+xml'),
 1877              'rss' => array('application/rss+xml'),
 1878              'form' => array('application/x-www-form-urlencoded'),
 1879          );
 1880      }
 1881   
 1882      /**
 1883       * Sets the default PHP locale.
 1884       *
 1885       * @param string $locale
 1886       */
 1887      private function setPhpDefaultLocale($locale)
 1888      {
 1889          // if either the class Locale doesn't exist, or an exception is thrown when
 1890          // setting the default locale, the intl module is not installed, and
 1891          // the call can be ignored:
 1892          try {
 1893              if (class_exists('Locale', false)) {
 1894                  \Locale::setDefault($locale);
 1895              }
 1896          } catch (\Exception $e) {
 1897          }
 1898      }
 1899   
 1900      /*
 1901       * Returns the prefix as encoded in the string when the string starts with
 1902       * the given prefix, false otherwise.
 1903       *
 1904       * @param string $string The urlencoded string
 1905       * @param string $prefix The prefix not encoded
 1906       *
 1907       * @return string|false The prefix as it is encoded in $string, or false
 1908       */
 1909      private function getUrlencodedPrefix($string, $prefix)
 1910      {
 1911          if (0 !== strpos(rawurldecode($string), $prefix)) {
 1912              return false;
 1913          }
 1914   
 1915          $len = strlen($prefix);
 1916   
 1917          if (preg_match(sprintf('#^(%%[[:xdigit:]]{2}|.){%d}#', $len), $string, $match)) {
 1918              return $match[0];
 1919          }
 1920   
 1921          return false;
 1922      }
 1923   
 1924      private static function createRequestFromFactory(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
 1925      {
 1926          if (self::$requestFactory) {
 1927              $request = call_user_func(self::$requestFactory, $query, $request, $attributes, $cookies, $files, $server, $content);
 1928   
 1929              if (!$request instanceof self) {
 1930                  throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.');
 1931              }
 1932   
 1933              return $request;
 1934          }
 1935   
 1936          return new static($query, $request, $attributes, $cookies, $files, $server, $content);
 1937      }
 1938   
 1939      private function isFromTrustedProxy()
 1940      {
 1941          return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR'), self::$trustedProxies);
 1942      }
 1943   
 1944      private function normalizeAndFilterClientIps(array $clientIps, $ip)
 1945      {
 1946          $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
 1947          $firstTrustedIp = null;
 1948   
 1949          foreach ($clientIps as $key => $clientIp) {
 1950              // Remove port (unfortunately, it does happen)
 1951              if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) {
 1952                  $clientIps[$key] = $clientIp = $match[1];
 1953              }
 1954   
 1955              if (!filter_var($clientIp, FILTER_VALIDATE_IP)) {
 1956                  unset($clientIps[$key]);
 1957   
 1958                  continue;
 1959              }
 1960   
 1961              if (IpUtils::checkIp($clientIp, self::$trustedProxies)) {
 1962                  unset($clientIps[$key]);
 1963   
 1964                  // Fallback to this when the client IP falls into the range of trusted proxies
 1965                  if (null === $firstTrustedIp) {
 1966                      $firstTrustedIp = $clientIp;
 1967                  }
 1968              }
 1969          }
 1970   
 1971          // Now the IP chain contains only untrusted proxies and the client IP
 1972          return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp);
 1973      }
 1974  }
 1975