Verzeichnisstruktur phpBB-3.1.0


Veröffentlicht
27.10.2014

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