Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

So funktioniert es


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

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

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

Request.php

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


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