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