Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

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