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