PageRenderTime 58ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Symfony/Component/HttpFoundation/Request.php

https://github.com/stepanets/symfony
PHP | 1657 lines | 728 code | 188 blank | 741 comment | 131 complexity | e717a8aa8c57d08fa2b2996a0f7dbf7b MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\HttpFoundation;
  11. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  12. /**
  13. * Request represents an HTTP request.
  14. *
  15. * The methods dealing with URL accept / return a raw path (% encoded):
  16. * * getBasePath
  17. * * getBaseUrl
  18. * * getPathInfo
  19. * * getRequestUri
  20. * * getUri
  21. * * getUriForPath
  22. *
  23. * @author Fabien Potencier <fabien@symfony.com>
  24. *
  25. * @api
  26. */
  27. class Request
  28. {
  29. const HEADER_CLIENT_IP = 'client_ip';
  30. const HEADER_CLIENT_HOST = 'client_host';
  31. const HEADER_CLIENT_PROTO = 'client_proto';
  32. const HEADER_CLIENT_PORT = 'client_port';
  33. protected static $trustProxy = false;
  34. protected static $trustedProxies = array();
  35. /**
  36. * Names for headers that can be trusted when
  37. * using trusted proxies.
  38. *
  39. * The default names are non-standard, but widely used
  40. * by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
  41. */
  42. protected static $trustedHeaders = array(
  43. self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
  44. self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
  45. self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
  46. self::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
  47. );
  48. protected static $httpMethodParameterOverride = false;
  49. /**
  50. * @var \Symfony\Component\HttpFoundation\ParameterBag
  51. *
  52. * @api
  53. */
  54. public $attributes;
  55. /**
  56. * @var \Symfony\Component\HttpFoundation\ParameterBag
  57. *
  58. * @api
  59. */
  60. public $request;
  61. /**
  62. * @var \Symfony\Component\HttpFoundation\ParameterBag
  63. *
  64. * @api
  65. */
  66. public $query;
  67. /**
  68. * @var \Symfony\Component\HttpFoundation\ServerBag
  69. *
  70. * @api
  71. */
  72. public $server;
  73. /**
  74. * @var \Symfony\Component\HttpFoundation\FileBag
  75. *
  76. * @api
  77. */
  78. public $files;
  79. /**
  80. * @var \Symfony\Component\HttpFoundation\ParameterBag
  81. *
  82. * @api
  83. */
  84. public $cookies;
  85. /**
  86. * @var \Symfony\Component\HttpFoundation\HeaderBag
  87. *
  88. * @api
  89. */
  90. public $headers;
  91. /**
  92. * @var string
  93. */
  94. protected $content;
  95. /**
  96. * @var array
  97. */
  98. protected $languages;
  99. /**
  100. * @var array
  101. */
  102. protected $charsets;
  103. /**
  104. * @var array
  105. */
  106. protected $acceptableContentTypes;
  107. /**
  108. * @var string
  109. */
  110. protected $pathInfo;
  111. /**
  112. * @var string
  113. */
  114. protected $requestUri;
  115. /**
  116. * @var string
  117. */
  118. protected $baseUrl;
  119. /**
  120. * @var string
  121. */
  122. protected $basePath;
  123. /**
  124. * @var string
  125. */
  126. protected $method;
  127. /**
  128. * @var string
  129. */
  130. protected $format;
  131. /**
  132. * @var \Symfony\Component\HttpFoundation\Session\SessionInterface
  133. */
  134. protected $session;
  135. /**
  136. * @var string
  137. */
  138. protected $locale;
  139. /**
  140. * @var string
  141. */
  142. protected $defaultLocale = 'en';
  143. /**
  144. * @var array
  145. */
  146. protected static $formats;
  147. /**
  148. * Constructor.
  149. *
  150. * @param array $query The GET parameters
  151. * @param array $request The POST parameters
  152. * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
  153. * @param array $cookies The COOKIE parameters
  154. * @param array $files The FILES parameters
  155. * @param array $server The SERVER parameters
  156. * @param string $content The raw body data
  157. *
  158. * @api
  159. */
  160. public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
  161. {
  162. $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content);
  163. }
  164. /**
  165. * Sets the parameters for this request.
  166. *
  167. * This method also re-initializes all properties.
  168. *
  169. * @param array $query The GET parameters
  170. * @param array $request The POST parameters
  171. * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
  172. * @param array $cookies The COOKIE parameters
  173. * @param array $files The FILES parameters
  174. * @param array $server The SERVER parameters
  175. * @param string $content The raw body data
  176. *
  177. * @api
  178. */
  179. public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
  180. {
  181. $this->request = new ParameterBag($request);
  182. $this->query = new ParameterBag($query);
  183. $this->attributes = new ParameterBag($attributes);
  184. $this->cookies = new ParameterBag($cookies);
  185. $this->files = new FileBag($files);
  186. $this->server = new ServerBag($server);
  187. $this->headers = new HeaderBag($this->server->getHeaders());
  188. $this->content = $content;
  189. $this->languages = null;
  190. $this->charsets = null;
  191. $this->acceptableContentTypes = null;
  192. $this->pathInfo = null;
  193. $this->requestUri = null;
  194. $this->baseUrl = null;
  195. $this->basePath = null;
  196. $this->method = null;
  197. $this->format = null;
  198. }
  199. /**
  200. * Creates a new request with values from PHP's super globals.
  201. *
  202. * @return Request A new request
  203. *
  204. * @api
  205. */
  206. public static function createFromGlobals()
  207. {
  208. $request = new static($_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER);
  209. if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
  210. && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH'))
  211. ) {
  212. parse_str($request->getContent(), $data);
  213. $request->request = new ParameterBag($data);
  214. }
  215. return $request;
  216. }
  217. /**
  218. * Creates a Request based on a given URI and configuration.
  219. *
  220. * The information contained in the URI always take precedence
  221. * over the other information (server and parameters).
  222. *
  223. * @param string $uri The URI
  224. * @param string $method The HTTP method
  225. * @param array $parameters The query (GET) or request (POST) parameters
  226. * @param array $cookies The request cookies ($_COOKIE)
  227. * @param array $files The request files ($_FILES)
  228. * @param array $server The server parameters ($_SERVER)
  229. * @param string $content The raw body data
  230. *
  231. * @return Request A Request instance
  232. *
  233. * @api
  234. */
  235. public static function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null)
  236. {
  237. $server = array_replace(array(
  238. 'SERVER_NAME' => 'localhost',
  239. 'SERVER_PORT' => 80,
  240. 'HTTP_HOST' => 'localhost',
  241. 'HTTP_USER_AGENT' => 'Symfony/2.X',
  242. 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  243. 'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5',
  244. 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
  245. 'REMOTE_ADDR' => '127.0.0.1',
  246. 'SCRIPT_NAME' => '',
  247. 'SCRIPT_FILENAME' => '',
  248. 'SERVER_PROTOCOL' => 'HTTP/1.1',
  249. 'REQUEST_TIME' => time(),
  250. ), $server);
  251. $server['PATH_INFO'] = '';
  252. $server['REQUEST_METHOD'] = strtoupper($method);
  253. $components = parse_url($uri);
  254. if (isset($components['host'])) {
  255. $server['SERVER_NAME'] = $components['host'];
  256. $server['HTTP_HOST'] = $components['host'];
  257. }
  258. if (isset($components['scheme'])) {
  259. if ('https' === $components['scheme']) {
  260. $server['HTTPS'] = 'on';
  261. $server['SERVER_PORT'] = 443;
  262. } else {
  263. unset($server['HTTPS']);
  264. $server['SERVER_PORT'] = 80;
  265. }
  266. }
  267. if (isset($components['port'])) {
  268. $server['SERVER_PORT'] = $components['port'];
  269. $server['HTTP_HOST'] = $server['HTTP_HOST'].':'.$components['port'];
  270. }
  271. if (isset($components['user'])) {
  272. $server['PHP_AUTH_USER'] = $components['user'];
  273. }
  274. if (isset($components['pass'])) {
  275. $server['PHP_AUTH_PW'] = $components['pass'];
  276. }
  277. if (!isset($components['path'])) {
  278. $components['path'] = '/';
  279. }
  280. switch (strtoupper($method)) {
  281. case 'POST':
  282. case 'PUT':
  283. case 'DELETE':
  284. if (!isset($server['CONTENT_TYPE'])) {
  285. $server['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
  286. }
  287. case 'PATCH':
  288. $request = $parameters;
  289. $query = array();
  290. break;
  291. default:
  292. $request = array();
  293. $query = $parameters;
  294. break;
  295. }
  296. if (isset($components['query'])) {
  297. parse_str(html_entity_decode($components['query']), $qs);
  298. $query = array_replace($qs, $query);
  299. }
  300. $queryString = http_build_query($query, '', '&');
  301. $server['REQUEST_URI'] = $components['path'].('' !== $queryString ? '?'.$queryString : '');
  302. $server['QUERY_STRING'] = $queryString;
  303. return new static($query, $request, array(), $cookies, $files, $server, $content);
  304. }
  305. /**
  306. * Clones a request and overrides some of its parameters.
  307. *
  308. * @param array $query The GET parameters
  309. * @param array $request The POST parameters
  310. * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
  311. * @param array $cookies The COOKIE parameters
  312. * @param array $files The FILES parameters
  313. * @param array $server The SERVER parameters
  314. *
  315. * @return Request The duplicated request
  316. *
  317. * @api
  318. */
  319. public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null)
  320. {
  321. $dup = clone $this;
  322. if ($query !== null) {
  323. $dup->query = new ParameterBag($query);
  324. }
  325. if ($request !== null) {
  326. $dup->request = new ParameterBag($request);
  327. }
  328. if ($attributes !== null) {
  329. $dup->attributes = new ParameterBag($attributes);
  330. }
  331. if ($cookies !== null) {
  332. $dup->cookies = new ParameterBag($cookies);
  333. }
  334. if ($files !== null) {
  335. $dup->files = new FileBag($files);
  336. }
  337. if ($server !== null) {
  338. $dup->server = new ServerBag($server);
  339. $dup->headers = new HeaderBag($dup->server->getHeaders());
  340. }
  341. $dup->languages = null;
  342. $dup->charsets = null;
  343. $dup->acceptableContentTypes = null;
  344. $dup->pathInfo = null;
  345. $dup->requestUri = null;
  346. $dup->baseUrl = null;
  347. $dup->basePath = null;
  348. $dup->method = null;
  349. $dup->format = null;
  350. return $dup;
  351. }
  352. /**
  353. * Clones the current request.
  354. *
  355. * Note that the session is not cloned as duplicated requests
  356. * are most of the time sub-requests of the main one.
  357. */
  358. public function __clone()
  359. {
  360. $this->query = clone $this->query;
  361. $this->request = clone $this->request;
  362. $this->attributes = clone $this->attributes;
  363. $this->cookies = clone $this->cookies;
  364. $this->files = clone $this->files;
  365. $this->server = clone $this->server;
  366. $this->headers = clone $this->headers;
  367. }
  368. /**
  369. * Returns the request as a string.
  370. *
  371. * @return string The request
  372. */
  373. public function __toString()
  374. {
  375. return
  376. sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n".
  377. $this->headers."\r\n".
  378. $this->getContent();
  379. }
  380. /**
  381. * Overrides the PHP global variables according to this request instance.
  382. *
  383. * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE.
  384. * $_FILES is never override, see rfc1867
  385. *
  386. * @api
  387. */
  388. public function overrideGlobals()
  389. {
  390. $_GET = $this->query->all();
  391. $_POST = $this->request->all();
  392. $_SERVER = $this->server->all();
  393. $_COOKIE = $this->cookies->all();
  394. foreach ($this->headers->all() as $key => $value) {
  395. $key = strtoupper(str_replace('-', '_', $key));
  396. if (in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH'))) {
  397. $_SERVER[$key] = implode(', ', $value);
  398. } else {
  399. $_SERVER['HTTP_'.$key] = implode(', ', $value);
  400. }
  401. }
  402. $request = array('g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE);
  403. $requestOrder = ini_get('request_order') ?: ini_get('variable_order');
  404. $requestOrder = preg_replace('#[^cgp]#', '', strtolower($requestOrder)) ?: 'gp';
  405. $_REQUEST = array();
  406. foreach (str_split($requestOrder) as $order) {
  407. $_REQUEST = array_merge($_REQUEST, $request[$order]);
  408. }
  409. }
  410. /**
  411. * Sets a list of trusted proxies.
  412. *
  413. * You should only list the reverse proxies that you manage directly.
  414. *
  415. * @param array $proxies A list of trusted proxies
  416. *
  417. * @api
  418. */
  419. public static function setTrustedProxies(array $proxies)
  420. {
  421. self::$trustedProxies = $proxies;
  422. self::$trustProxy = $proxies ? true : false;
  423. }
  424. /**
  425. * Gets the list of trusted proxies.
  426. *
  427. * @return array An array of trusted proxies.
  428. */
  429. public static function getTrustedProxies()
  430. {
  431. return self::$trustedProxies;
  432. }
  433. /**
  434. * Sets the name for trusted headers.
  435. *
  436. * The following header keys are supported:
  437. *
  438. * * Request::HEADER_CLIENT_IP: defaults to X-Forwarded-For (see getClientIp())
  439. * * Request::HEADER_CLIENT_HOST: defaults to X-Forwarded-Host (see getClientHost())
  440. * * Request::HEADER_CLIENT_PORT: defaults to X-Forwarded-Port (see getClientPort())
  441. * * Request::HEADER_CLIENT_PROTO: defaults to X-Forwarded-Proto (see getScheme() and isSecure())
  442. *
  443. * Setting an empty value allows to disable the trusted header for the given key.
  444. *
  445. * @param string $key The header key
  446. * @param string $value The header name
  447. *
  448. * @throws \InvalidArgumentException
  449. */
  450. public static function setTrustedHeaderName($key, $value)
  451. {
  452. if (!array_key_exists($key, self::$trustedHeaders)) {
  453. throw new \InvalidArgumentException(sprintf('Unable to set the trusted header name for key "%s".', $key));
  454. }
  455. self::$trustedHeaders[$key] = $value;
  456. }
  457. /**
  458. * Normalizes a query string.
  459. *
  460. * It builds a normalized query string, where keys/value pairs are alphabetized,
  461. * have consistent escaping and unneeded delimiters are removed.
  462. *
  463. * @param string $qs Query string
  464. *
  465. * @return string A normalized query string for the Request
  466. */
  467. public static function normalizeQueryString($qs)
  468. {
  469. if ('' == $qs) {
  470. return '';
  471. }
  472. $parts = array();
  473. $order = array();
  474. foreach (explode('&', $qs) as $param) {
  475. if ('' === $param || '=' === $param[0]) {
  476. // Ignore useless delimiters, e.g. "x=y&".
  477. // Also ignore pairs with empty key, even if there was a value, e.g. "=value", as such nameless values cannot be retrieved anyway.
  478. // PHP also does not include them when building _GET.
  479. continue;
  480. }
  481. $keyValuePair = explode('=', $param, 2);
  482. // GET parameters, that are submitted from a HTML form, encode spaces as "+" by default (as defined in enctype application/x-www-form-urlencoded).
  483. // 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
  484. // RFC 3986 with rawurlencode.
  485. $parts[] = isset($keyValuePair[1]) ?
  486. rawurlencode(urldecode($keyValuePair[0])).'='.rawurlencode(urldecode($keyValuePair[1])) :
  487. rawurlencode(urldecode($keyValuePair[0]));
  488. $order[] = urldecode($keyValuePair[0]);
  489. }
  490. array_multisort($order, SORT_ASC, $parts);
  491. return implode('&', $parts);
  492. }
  493. /**
  494. * Enables support for the _method request parameter to determine the intended HTTP method.
  495. *
  496. * Be warned that enabling this feature might lead to CSRF issues in your code.
  497. * Check that you are using CSRF tokens when required.
  498. *
  499. * The HTTP method can only be overridden when the real HTTP method is POST.
  500. */
  501. public static function enableHttpMethodParameterOverride()
  502. {
  503. self::$httpMethodParameterOverride = true;
  504. }
  505. /**
  506. * Checks whether support for the _method request parameter is enabled.
  507. *
  508. * @return Boolean True when the _method request parameter is enabled, false otherwise
  509. */
  510. public static function getHttpMethodParameterOverride()
  511. {
  512. return self::$httpMethodParameterOverride;
  513. }
  514. /**
  515. * Gets a "parameter" value.
  516. *
  517. * This method is mainly useful for libraries that want to provide some flexibility.
  518. *
  519. * Order of precedence: GET, PATH, POST
  520. *
  521. * Avoid using this method in controllers:
  522. *
  523. * * slow
  524. * * prefer to get from a "named" source
  525. *
  526. * It is better to explicitly get request parameters from the appropriate
  527. * public property instead (query, attributes, request).
  528. *
  529. * @param string $key the key
  530. * @param mixed $default the default value
  531. * @param Boolean $deep is parameter deep in multidimensional array
  532. *
  533. * @return mixed
  534. */
  535. public function get($key, $default = null, $deep = false)
  536. {
  537. return $this->query->get($key, $this->attributes->get($key, $this->request->get($key, $default, $deep), $deep), $deep);
  538. }
  539. /**
  540. * Gets the Session.
  541. *
  542. * @return SessionInterface|null The session
  543. *
  544. * @api
  545. */
  546. public function getSession()
  547. {
  548. return $this->session;
  549. }
  550. /**
  551. * Whether the request contains a Session which was started in one of the
  552. * previous requests.
  553. *
  554. * @return Boolean
  555. *
  556. * @api
  557. */
  558. public function hasPreviousSession()
  559. {
  560. // the check for $this->session avoids malicious users trying to fake a session cookie with proper name
  561. return $this->hasSession() && $this->cookies->has($this->session->getName());
  562. }
  563. /**
  564. * Whether the request contains a Session object.
  565. *
  566. * This method does not give any information about the state of the session object,
  567. * like whether the session is started or not. It is just a way to check if this Request
  568. * is associated with a Session instance.
  569. *
  570. * @return Boolean true when the Request contains a Session object, false otherwise
  571. *
  572. * @api
  573. */
  574. public function hasSession()
  575. {
  576. return null !== $this->session;
  577. }
  578. /**
  579. * Sets the Session.
  580. *
  581. * @param SessionInterface $session The Session
  582. *
  583. * @api
  584. */
  585. public function setSession(SessionInterface $session)
  586. {
  587. $this->session = $session;
  588. }
  589. /**
  590. * Returns the client IP address.
  591. *
  592. * This method can read the client IP address from the "X-Forwarded-For" header
  593. * when trusted proxies were set via "setTrustedProxies()". The "X-Forwarded-For"
  594. * header value is a comma+space separated list of IP addresses, the left-most
  595. * being the original client, and each successive proxy that passed the request
  596. * adding the IP address where it received the request from.
  597. *
  598. * If your reverse proxy uses a different header name than "X-Forwarded-For",
  599. * ("Client-Ip" for instance), configure it via "setTrustedHeaderName()" with
  600. * the "client-ip" key.
  601. *
  602. * @return string The client IP address
  603. *
  604. * @see http://en.wikipedia.org/wiki/X-Forwarded-For
  605. *
  606. * @api
  607. */
  608. public function getClientIp()
  609. {
  610. $ip = $this->server->get('REMOTE_ADDR');
  611. if (!self::$trustProxy) {
  612. return $ip;
  613. }
  614. if (!self::$trustedHeaders[self::HEADER_CLIENT_IP] || !$this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) {
  615. return $ip;
  616. }
  617. $clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP])));
  618. $clientIps[] = $ip;
  619. $trustedProxies = self::$trustProxy && !self::$trustedProxies ? array($ip) : self::$trustedProxies;
  620. $clientIps = array_diff($clientIps, $trustedProxies);
  621. return array_pop($clientIps);
  622. }
  623. /**
  624. * Returns current script name.
  625. *
  626. * @return string
  627. *
  628. * @api
  629. */
  630. public function getScriptName()
  631. {
  632. return $this->server->get('SCRIPT_NAME', $this->server->get('ORIG_SCRIPT_NAME', ''));
  633. }
  634. /**
  635. * Returns the path being requested relative to the executed script.
  636. *
  637. * The path info always starts with a /.
  638. *
  639. * Suppose this request is instantiated from /mysite on localhost:
  640. *
  641. * * http://localhost/mysite returns an empty string
  642. * * http://localhost/mysite/about returns '/about'
  643. * * http://localhost/mysite/enco%20ded returns '/enco%20ded'
  644. * * http://localhost/mysite/about?var=1 returns '/about'
  645. *
  646. * @return string The raw path (i.e. not urldecoded)
  647. *
  648. * @api
  649. */
  650. public function getPathInfo()
  651. {
  652. if (null === $this->pathInfo) {
  653. $this->pathInfo = $this->preparePathInfo();
  654. }
  655. return $this->pathInfo;
  656. }
  657. /**
  658. * Returns the root path from which this request is executed.
  659. *
  660. * Suppose that an index.php file instantiates this request object:
  661. *
  662. * * http://localhost/index.php returns an empty string
  663. * * http://localhost/index.php/page returns an empty string
  664. * * http://localhost/web/index.php returns '/web'
  665. * * http://localhost/we%20b/index.php returns '/we%20b'
  666. *
  667. * @return string The raw path (i.e. not urldecoded)
  668. *
  669. * @api
  670. */
  671. public function getBasePath()
  672. {
  673. if (null === $this->basePath) {
  674. $this->basePath = $this->prepareBasePath();
  675. }
  676. return $this->basePath;
  677. }
  678. /**
  679. * Returns the root url from which this request is executed.
  680. *
  681. * The base URL never ends with a /.
  682. *
  683. * This is similar to getBasePath(), except that it also includes the
  684. * script filename (e.g. index.php) if one exists.
  685. *
  686. * @return string The raw url (i.e. not urldecoded)
  687. *
  688. * @api
  689. */
  690. public function getBaseUrl()
  691. {
  692. if (null === $this->baseUrl) {
  693. $this->baseUrl = $this->prepareBaseUrl();
  694. }
  695. return $this->baseUrl;
  696. }
  697. /**
  698. * Gets the request's scheme.
  699. *
  700. * @return string
  701. *
  702. * @api
  703. */
  704. public function getScheme()
  705. {
  706. return $this->isSecure() ? 'https' : 'http';
  707. }
  708. /**
  709. * Returns the port on which the request is made.
  710. *
  711. * This method can read the client port from the "X-Forwarded-Port" header
  712. * when trusted proxies were set via "setTrustedProxies()".
  713. *
  714. * The "X-Forwarded-Port" header must contain the client port.
  715. *
  716. * If your reverse proxy uses a different header name than "X-Forwarded-Port",
  717. * configure it via "setTrustedHeaderName()" with the "client-port" key.
  718. *
  719. * @return string
  720. *
  721. * @api
  722. */
  723. public function getPort()
  724. {
  725. if (self::$trustProxy && self::$trustedHeaders[self::HEADER_CLIENT_PORT] && $port = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PORT])) {
  726. return $port;
  727. }
  728. return $this->server->get('SERVER_PORT');
  729. }
  730. /**
  731. * Returns the user.
  732. *
  733. * @return string|null
  734. */
  735. public function getUser()
  736. {
  737. return $this->server->get('PHP_AUTH_USER');
  738. }
  739. /**
  740. * Returns the password.
  741. *
  742. * @return string|null
  743. */
  744. public function getPassword()
  745. {
  746. return $this->server->get('PHP_AUTH_PW');
  747. }
  748. /**
  749. * Gets the user info.
  750. *
  751. * @return string A user name and, optionally, scheme-specific information about how to gain authorization to access the server
  752. */
  753. public function getUserInfo()
  754. {
  755. $userinfo = $this->getUser();
  756. $pass = $this->getPassword();
  757. if ('' != $pass) {
  758. $userinfo .= ":$pass";
  759. }
  760. return $userinfo;
  761. }
  762. /**
  763. * Returns the HTTP host being requested.
  764. *
  765. * The port name will be appended to the host if it's non-standard.
  766. *
  767. * @return string
  768. *
  769. * @api
  770. */
  771. public function getHttpHost()
  772. {
  773. $scheme = $this->getScheme();
  774. $port = $this->getPort();
  775. if (('http' == $scheme && $port == 80) || ('https' == $scheme && $port == 443)) {
  776. return $this->getHost();
  777. }
  778. return $this->getHost().':'.$port;
  779. }
  780. /**
  781. * Returns the requested URI.
  782. *
  783. * @return string The raw URI (i.e. not urldecoded)
  784. *
  785. * @api
  786. */
  787. public function getRequestUri()
  788. {
  789. if (null === $this->requestUri) {
  790. $this->requestUri = $this->prepareRequestUri();
  791. }
  792. return $this->requestUri;
  793. }
  794. /**
  795. * Gets the scheme and HTTP host.
  796. *
  797. * If the URL was called with basic authentication, the user
  798. * and the password are not added to the generated string.
  799. *
  800. * @return string The scheme and HTTP host
  801. */
  802. public function getSchemeAndHttpHost()
  803. {
  804. return $this->getScheme().'://'.$this->getHttpHost();
  805. }
  806. /**
  807. * Generates a normalized URI for the Request.
  808. *
  809. * @return string A normalized URI for the Request
  810. *
  811. * @see getQueryString()
  812. *
  813. * @api
  814. */
  815. public function getUri()
  816. {
  817. if (null !== $qs = $this->getQueryString()) {
  818. $qs = '?'.$qs;
  819. }
  820. return $this->getSchemeAndHttpHost().$this->getBaseUrl().$this->getPathInfo().$qs;
  821. }
  822. /**
  823. * Generates a normalized URI for the given path.
  824. *
  825. * @param string $path A path to use instead of the current one
  826. *
  827. * @return string The normalized URI for the path
  828. *
  829. * @api
  830. */
  831. public function getUriForPath($path)
  832. {
  833. return $this->getSchemeAndHttpHost().$this->getBaseUrl().$path;
  834. }
  835. /**
  836. * Generates the normalized query string for the Request.
  837. *
  838. * It builds a normalized query string, where keys/value pairs are alphabetized
  839. * and have consistent escaping.
  840. *
  841. * @return string|null A normalized query string for the Request
  842. *
  843. * @api
  844. */
  845. public function getQueryString()
  846. {
  847. $qs = static::normalizeQueryString($this->server->get('QUERY_STRING'));
  848. return '' === $qs ? null : $qs;
  849. }
  850. /**
  851. * Checks whether the request is secure or not.
  852. *
  853. * This method can read the client port from the "X-Forwarded-Proto" header
  854. * when trusted proxies were set via "setTrustedProxies()".
  855. *
  856. * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http".
  857. *
  858. * If your reverse proxy uses a different header name than "X-Forwarded-Proto"
  859. * ("SSL_HTTPS" for instance), configure it via "setTrustedHeaderName()" with
  860. * the "client-proto" key.
  861. *
  862. * @return Boolean
  863. *
  864. * @api
  865. */
  866. public function isSecure()
  867. {
  868. if (self::$trustProxy && self::$trustedHeaders[self::HEADER_CLIENT_PROTO] && $proto = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_PROTO])) {
  869. return in_array(strtolower($proto), array('https', 'on', '1'));
  870. }
  871. return 'on' == strtolower($this->server->get('HTTPS')) || 1 == $this->server->get('HTTPS');
  872. }
  873. /**
  874. * Returns the host name.
  875. *
  876. * This method can read the client port from the "X-Forwarded-Host" header
  877. * when trusted proxies were set via "setTrustedProxies()".
  878. *
  879. * The "X-Forwarded-Host" header must contain the client host name.
  880. *
  881. * If your reverse proxy uses a different header name than "X-Forwarded-Host",
  882. * configure it via "setTrustedHeaderName()" with the "client-host" key.
  883. *
  884. * @return string
  885. *
  886. * @throws \UnexpectedValueException when the host name is invalid
  887. *
  888. * @api
  889. */
  890. public function getHost()
  891. {
  892. if (self::$trustProxy && self::$trustedHeaders[self::HEADER_CLIENT_HOST] && $host = $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_HOST])) {
  893. $elements = explode(',', $host);
  894. $host = $elements[count($elements) - 1];
  895. } elseif (!$host = $this->headers->get('HOST')) {
  896. if (!$host = $this->server->get('SERVER_NAME')) {
  897. $host = $this->server->get('SERVER_ADDR', '');
  898. }
  899. }
  900. // trim and remove port number from host
  901. // host is lowercase as per RFC 952/2181
  902. $host = strtolower(preg_replace('/:\d+$/', '', trim($host)));
  903. // as the host can come from the user (HTTP_HOST and depending on the configuration, SERVER_NAME too can come from the user)
  904. // check that it does not contain forbidden characters (see RFC 952 and RFC 2181)
  905. if ($host && !preg_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host)) {
  906. throw new \UnexpectedValueException('Invalid Host');
  907. }
  908. return $host;
  909. }
  910. /**
  911. * Sets the request method.
  912. *
  913. * @param string $method
  914. *
  915. * @api
  916. */
  917. public function setMethod($method)
  918. {
  919. $this->method = null;
  920. $this->server->set('REQUEST_METHOD', $method);
  921. }
  922. /**
  923. * Gets the request "intended" method.
  924. *
  925. * If the X-HTTP-Method-Override header is set, and if the method is a POST,
  926. * then it is used to determine the "real" intended HTTP method.
  927. *
  928. * The _method request parameter can also be used to determine the HTTP method,
  929. * but only if enableHttpMethodParameterOverride() has been called.
  930. *
  931. * The method is always an uppercased string.
  932. *
  933. * @return string The request method
  934. *
  935. * @api
  936. *
  937. * @see getRealMethod
  938. */
  939. public function getMethod()
  940. {
  941. if (null === $this->method) {
  942. $this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
  943. if ('POST' === $this->method) {
  944. if ($method = $this->headers->get('X-HTTP-METHOD-OVERRIDE')) {
  945. $this->method = strtoupper($method);
  946. } elseif (self::$httpMethodParameterOverride) {
  947. $this->method = strtoupper($this->request->get('_method', $this->query->get('_method', 'POST')));
  948. }
  949. }
  950. }
  951. return $this->method;
  952. }
  953. /**
  954. * Gets the "real" request method.
  955. *
  956. * @return string The request method
  957. *
  958. * @see getMethod
  959. */
  960. public function getRealMethod()
  961. {
  962. return strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
  963. }
  964. /**
  965. * Gets the mime type associated with the format.
  966. *
  967. * @param string $format The format
  968. *
  969. * @return string The associated mime type (null if not found)
  970. *
  971. * @api
  972. */
  973. public function getMimeType($format)
  974. {
  975. if (null === static::$formats) {
  976. static::initializeFormats();
  977. }
  978. return isset(static::$formats[$format]) ? static::$formats[$format][0] : null;
  979. }
  980. /**
  981. * Gets the format associated with the mime type.
  982. *
  983. * @param string $mimeType The associated mime type
  984. *
  985. * @return string|null The format (null if not found)
  986. *
  987. * @api
  988. */
  989. public function getFormat($mimeType)
  990. {
  991. if (false !== $pos = strpos($mimeType, ';')) {
  992. $mimeType = substr($mimeType, 0, $pos);
  993. }
  994. if (null === static::$formats) {
  995. static::initializeFormats();
  996. }
  997. foreach (static::$formats as $format => $mimeTypes) {
  998. if (in_array($mimeType, (array) $mimeTypes)) {
  999. return $format;
  1000. }
  1001. }
  1002. return null;
  1003. }
  1004. /**
  1005. * Associates a format with mime types.
  1006. *
  1007. * @param string $format The format
  1008. * @param string|array $mimeTypes The associated mime types (the preferred one must be the first as it will be used as the content type)
  1009. *
  1010. * @api
  1011. */
  1012. public function setFormat($format, $mimeTypes)
  1013. {
  1014. if (null === static::$formats) {
  1015. static::initializeFormats();
  1016. }
  1017. static::$formats[$format] = is_array($mimeTypes) ? $mimeTypes : array($mimeTypes);
  1018. }
  1019. /**
  1020. * Gets the request format.
  1021. *
  1022. * Here is the process to determine the format:
  1023. *
  1024. * * format defined by the user (with setRequestFormat())
  1025. * * _format request parameter
  1026. * * $default
  1027. *
  1028. * @param string $default The default format
  1029. *
  1030. * @return string The request format
  1031. *
  1032. * @api
  1033. */
  1034. public function getRequestFormat($default = 'html')
  1035. {
  1036. if (null === $this->format) {
  1037. $this->format = $this->get('_format', $default);
  1038. }
  1039. return $this->format;
  1040. }
  1041. /**
  1042. * Sets the request format.
  1043. *
  1044. * @param string $format The request format.
  1045. *
  1046. * @api
  1047. */
  1048. public function setRequestFormat($format)
  1049. {
  1050. $this->format = $format;
  1051. }
  1052. /**
  1053. * Gets the format associated with the request.
  1054. *
  1055. * @return string|null The format (null if no content type is present)
  1056. *
  1057. * @api
  1058. */
  1059. public function getContentType()
  1060. {
  1061. return $this->getFormat($this->headers->get('CONTENT_TYPE'));
  1062. }
  1063. /**
  1064. * Sets the default locale.
  1065. *
  1066. * @param string $locale
  1067. *
  1068. * @api
  1069. */
  1070. public function setDefaultLocale($locale)
  1071. {
  1072. $this->defaultLocale = $locale;
  1073. if (null === $this->locale) {
  1074. $this->setPhpDefaultLocale($locale);
  1075. }
  1076. }
  1077. /**
  1078. * Sets the locale.
  1079. *
  1080. * @param string $locale
  1081. *
  1082. * @api
  1083. */
  1084. public function setLocale($locale)
  1085. {
  1086. $this->setPhpDefaultLocale($this->locale = $locale);
  1087. }
  1088. /**
  1089. * Get the locale.
  1090. *
  1091. * @return string
  1092. */
  1093. public function getLocale()
  1094. {
  1095. return null === $this->locale ? $this->defaultLocale : $this->locale;
  1096. }
  1097. /**
  1098. * Checks if the request method is of specified type.
  1099. *
  1100. * @param string $method Uppercase request method (GET, POST etc).
  1101. *
  1102. * @return Boolean
  1103. */
  1104. public function isMethod($method)
  1105. {
  1106. return $this->getMethod() === strtoupper($method);
  1107. }
  1108. /**
  1109. * Checks whether the method is safe or not.
  1110. *
  1111. * @return Boolean
  1112. *
  1113. * @api
  1114. */
  1115. public function isMethodSafe()
  1116. {
  1117. return in_array($this->getMethod(), array('GET', 'HEAD'));
  1118. }
  1119. /**
  1120. * Returns the request body content.
  1121. *
  1122. * @param Boolean $asResource If true, a resource will be returned
  1123. *
  1124. * @return string|resource The request body content or a resource to read the body stream.
  1125. *
  1126. * @throws \LogicException
  1127. */
  1128. public function getContent($asResource = false)
  1129. {
  1130. if (false === $this->content || (true === $asResource && null !== $this->content)) {
  1131. throw new \LogicException('getContent() can only be called once when using the resource return type.');
  1132. }
  1133. if (true === $asResource) {
  1134. $this->content = false;
  1135. return fopen('php://input', 'rb');
  1136. }
  1137. if (null === $this->content) {
  1138. $this->content = file_get_contents('php://input');
  1139. }
  1140. return $this->content;
  1141. }
  1142. /**
  1143. * Gets the Etags.
  1144. *
  1145. * @return array The entity tags
  1146. */
  1147. public function getETags()
  1148. {
  1149. return preg_split('/\s*,\s*/', $this->headers->get('if_none_match'), null, PREG_SPLIT_NO_EMPTY);
  1150. }
  1151. /**
  1152. * @return Boolean
  1153. */
  1154. public function isNoCache()
  1155. {
  1156. return $this->headers->hasCacheControlDirective('no-cache') || 'no-cache' == $this->headers->get('Pragma');
  1157. }
  1158. /**
  1159. * Returns the preferred language.
  1160. *
  1161. * @param array $locales An array of ordered available locales
  1162. *
  1163. * @return string|null The preferred locale
  1164. *
  1165. * @api
  1166. */
  1167. public function getPreferredLanguage(array $locales = null)
  1168. {
  1169. $preferredLanguages = $this->getLanguages();
  1170. if (empty($locales)) {
  1171. return isset($preferredLanguages[0]) ? $preferredLanguages[0] : null;
  1172. }
  1173. if (!$preferredLanguages) {
  1174. return $locales[0];
  1175. }
  1176. $extendedPreferredLanguages = array();
  1177. foreach ($preferredLanguages as $language) {
  1178. $extendedPreferredLanguages[] = $language;
  1179. if (false !== $position = strpos($language, '_')) {
  1180. $superLanguage = substr($language, 0, $position);
  1181. if (!in_array($superLanguage, $preferredLanguages)) {
  1182. $extendedPreferredLanguages[] = $superLanguage;
  1183. }
  1184. }
  1185. }
  1186. $preferredLanguages = array_values(array_intersect($extendedPreferredLanguages, $locales));
  1187. return isset($preferredLanguages[0]) ? $preferredLanguages[0] : $locales[0];
  1188. }
  1189. /**
  1190. * Gets a list of languages acceptable by the client browser.
  1191. *
  1192. * @return array Languages ordered in the user browser preferences
  1193. *
  1194. * @api
  1195. */
  1196. public function getLanguages()
  1197. {
  1198. if (null !== $this->languages) {
  1199. return $this->languages;
  1200. }
  1201. $languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all();
  1202. $this->languages = array();
  1203. foreach (array_keys($languages) as $lang) {
  1204. if (strstr($lang, '-')) {
  1205. $codes = explode('-', $lang);
  1206. if ($codes[0] == 'i') {
  1207. // Language not listed in ISO 639 that are not variants
  1208. // of any listed language, which can be registered with the
  1209. // i-prefix, such as i-cherokee
  1210. if (count($codes) > 1) {
  1211. $lang = $codes[1];
  1212. }
  1213. } else {
  1214. for ($i = 0, $max = count($codes); $i < $max; $i++) {
  1215. if ($i == 0) {
  1216. $lang = strtolower($codes[0]);
  1217. } else {
  1218. $lang .= '_'.strtoupper($codes[$i]);
  1219. }
  1220. }
  1221. }
  1222. }
  1223. $this->languages[] = $lang;
  1224. }
  1225. return $this->languages;
  1226. }
  1227. /**
  1228. * Gets a list of charsets acceptable by the client browser.
  1229. *
  1230. * @return array List of charsets in preferable order
  1231. *
  1232. * @api
  1233. */
  1234. public function getCharsets()
  1235. {
  1236. if (null !== $this->charsets) {
  1237. return $this->charsets;
  1238. }
  1239. return $this->charsets = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all());
  1240. }
  1241. /**
  1242. * Gets a list of content types acceptable by the client browser
  1243. *
  1244. * @return array List of content types in preferable order
  1245. *
  1246. * @api
  1247. */
  1248. public function getAcceptableContentTypes()
  1249. {
  1250. if (null !== $this->acceptableContentTypes) {
  1251. return $this->acceptableContentTypes;
  1252. }
  1253. return $this->acceptableContentTypes = array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all());
  1254. }
  1255. /**
  1256. * Returns true if the request is a XMLHttpRequest.
  1257. *
  1258. * It works if your JavaScript library set an X-Requested-With HTTP header.
  1259. * It is known to work with common JavaScript frameworks:
  1260. * @link http://en.wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript
  1261. *
  1262. * @return Boolean true if the request is an XMLHttpRequest, false otherwise
  1263. *
  1264. * @api
  1265. */
  1266. public function isXmlHttpRequest()
  1267. {
  1268. return 'XMLHttpRequest' == $this->headers->get('X-Requested-With');
  1269. }
  1270. /*
  1271. * The following methods are derived from code of the Zend Framework (1.10dev - 2010-01-24)
  1272. *
  1273. * Code subject to the new BSD license (http://framework.zend.com/license/new-bsd).
  1274. *
  1275. * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  1276. */
  1277. protected function prepareRequestUri()
  1278. {
  1279. $requestUri = '';
  1280. if ($this->headers->has('X_ORIGINAL_URL') && false !== stripos(PHP_OS, 'WIN')) {
  1281. // IIS with Microsoft Rewrite Module
  1282. $requestUri = $this->headers->get('X_ORIGINAL_URL');
  1283. $this->headers->remove('X_ORIGINAL_URL');
  1284. } elseif ($this->headers->has('X_REWRITE_URL') && false !== stripos(PHP_OS, 'WIN')) {
  1285. // IIS with ISAPI_Rewrite
  1286. $requestUri = $this->headers->get('X_REWRITE_URL');
  1287. $this->headers->remove('X_REWRITE_URL');
  1288. } elseif ($this->server->get('IIS_WasUrlRewritten') == '1' && $this->server->get('UNENCODED_URL') != '') {
  1289. // IIS7 with URL Rewrite: make sure we get the unencoded url (double slash problem)
  1290. $requestUri = $this->server->get('UNENCODED_URL');
  1291. $this->server->remove('UNENCODED_URL');
  1292. $this->server->remove('IIS_WasUrlRewritten');
  1293. } elseif ($this->server->has('REQUEST_URI')) {
  1294. $requestUri = $this->server->get('REQUEST_URI');
  1295. // HTTP proxy reqs setup request uri with scheme and host [and port] + the url path, only use url path
  1296. $schemeAndHttpHost = $this->getSchemeAndHttpHost();
  1297. if (strpos($requestUri, $schemeAndHttpHost) === 0) {
  1298. $requestUri = substr($requestUri, strlen($schemeAndHttpHost));
  1299. }
  1300. } elseif ($this->server->has('ORIG_PATH_INFO')) {
  1301. // IIS 5.0, PHP as CGI
  1302. $requestUri = $this->server->get('ORIG_PATH_INFO');
  1303. if ('' != $this->server->get('QUERY_STRING')) {
  1304. $requestUri .= '?'.$this->server->get('QUERY_STRING');
  1305. }
  1306. $this->server->remove('ORIG_PATH_INFO');
  1307. }
  1308. // normalize the request URI to ease creating sub-requests from this request
  1309. $this->server->set('REQUEST_URI', $requestUri);
  1310. return $requestUri;
  1311. }
  1312. /**
  1313. * Prepares the base URL.
  1314. *
  1315. * @return string
  1316. */
  1317. protected function prepareBaseUrl()
  1318. {
  1319. $filename = basename($this->server->get('SCRIPT_FILENAME'));
  1320. if (basename($this->server->get('SCRIPT_NAME')) === $filename) {
  1321. $baseUrl = $this->server->get('SCRIPT_NAME');
  1322. } elseif (basename($this->server->get('PHP_SELF')) === $filename) {
  1323. $baseUrl = $this->server->get('PHP_SELF');
  1324. } elseif (basename($this->server->get('ORIG_SCRIPT_NAME')) === $filename) {
  1325. $baseUrl = $this->server->get('ORIG_SCRIPT_NAME'); // 1and1 shared hosting compatibility
  1326. } else {
  1327. // Backtrack up the script_filename to find the portion matching
  1328. // php_self
  1329. $path = $this->server->get('PHP_SELF', '');
  1330. $file = $this->server->get('SCRIPT_FILENAME', '');
  1331. $segs = explode('/', trim($file, '/'));
  1332. $segs = array_reverse($segs);
  1333. $index = 0;
  1334. $last = count($segs);
  1335. $baseUrl = '';
  1336. do {
  1337. $seg = $segs[$index];
  1338. $baseUrl = '/'.$seg.$baseUrl;
  1339. ++$index;
  1340. } while (($last > $index) && (false !== ($pos = strpos($path, $baseUrl))) && (0 != $pos));
  1341. }
  1342. // Does the baseUrl have anything in common with the request_uri?
  1343. $requestUri = $this->getRequestUri();
  1344. if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) {
  1345. // full $baseUrl matches
  1346. return $prefix;
  1347. }
  1348. if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, dirname($baseUrl))) {
  1349. // directory portion of $baseUrl matches
  1350. return rtrim($prefix, '/');
  1351. }
  1352. $truncatedRequestUri = $requestUri;
  1353. if (($pos = strpos($requestUri, '?')) !== false) {
  1354. $truncatedRequestUri = substr($requestUri, 0, $pos);
  1355. }
  1356. $basename = basename($baseUrl);
  1357. if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri), $basename)) {
  1358. // no match whatsoever; set it blank
  1359. return '';
  1360. }
  1361. // If using mod_rewrite or ISAPI_Rewrite strip the script filename
  1362. // out of baseUrl. $pos !== 0 makes sure it is not matching a value
  1363. // from PATH_INFO or QUERY_STRING
  1364. if ((strlen($requestUri) >= strlen($baseUrl)) && ((false !== ($pos = strpos($requestUri, $baseUrl))) && ($pos !== 0))) {
  1365. $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl));
  1366. }
  1367. return rtrim($baseUrl, '/');
  1368. }
  1369. /**
  1370. * Prepares the base path.
  1371. *
  1372. * @return string base path
  1373. */
  1374. protected function prepareBasePath()
  1375. {
  1376. $filename = basename($this->server->get('SCRIPT_FILENAME'));
  1377. $baseUrl = $this->getBaseUrl();
  1378. if (empty($baseUrl)) {
  1379. return '';
  1380. }
  1381. if (basename($baseUrl) === $filename) {
  1382. $basePath = dirname($baseUrl);
  1383. } else {
  1384. $basePath = $baseUrl;
  1385. }
  1386. if ('\\' === DIRECTORY_SEPARATOR) {
  1387. $basePath = str_replace('\\', '/', $basePath);
  1388. }
  1389. return rtrim($basePath, '/');
  1390. }
  1391. /**
  1392. * Prepares the path info.
  1393. *
  1394. * @return string path info
  1395. */
  1396. protected function preparePathInfo()
  1397. {
  1398. $baseUrl = $this->getBaseUrl();
  1399. if (null === ($requestUri = $this->getRequestUri())) {
  1400. return '/';
  1401. }
  1402. $pathInfo = '/';
  1403. // Remove the query string from REQUEST_URI
  1404. if ($pos = strpos($requestUri, '?')) {
  1405. $requestUri = substr($requestUri, 0, $pos);
  1406. }
  1407. if ((null !== $baseUrl) && (false === ($pathInfo = substr($requestUri, strlen($baseUrl))))) {
  1408. // If substr() returns false then PATH_INFO is set to an empty string
  1409. return '/';
  1410. } elseif (null === $baseUrl) {
  1411. return $requestUri;
  1412. }
  1413. return (string) $pathInfo;
  1414. }
  1415. /**
  1416. * Initializes HTTP request formats.
  1417. */
  1418. protected static function initializeFormats()
  1419. {
  1420. static::$formats = array(
  1421. 'html' => array('text/html', 'application/xhtml+xml'),
  1422. 'txt' => array('text/plain'),
  1423. 'js' => array('application/javascript', 'application/x-javascript', 'text/javascript'),
  1424. 'css' => array('text/css'),
  1425. 'json' => array('application/json', 'application/x-json'),
  1426. 'xml' => array('text/xml', 'application/xml', 'application/x-xml'),
  1427. 'rdf' => array('application/rdf+xml'),
  1428. 'atom' => array('application/atom+xml'),
  1429. 'rss' => array('application/rss+xml'),
  1430. );
  1431. }
  1432. /**
  1433. * Sets the default PHP locale.
  1434. *
  1435. * @param string $locale
  1436. */
  1437. private function setPhpDefaultLocale($locale)
  1438. {
  1439. // if either the class Locale doesn't exist, or an exception is thrown when
  1440. // setting the default locale, the intl module is not installed, and
  1441. // the call can be ignored:
  1442. try {
  1443. if (class_exists('Locale', false)) {
  1444. \Locale::setDefault($locale);
  1445. }
  1446. } catch (\Exception $e) {
  1447. }
  1448. }
  1449. /*
  1450. * Returns the prefix as encoded in the string when the string starts with
  1451. * the given prefix, false otherwise.
  1452. *
  1453. * @param string $string The urlencoded string
  1454. * @param string $prefix The prefix not encoded
  1455. *
  1456. * @return string|false The prefix as it is encoded in $string, or false
  1457. */
  1458. private function getUrlencodedPrefix($string, $prefix)
  1459. {
  1460. if (0 !== strpos(rawurldecode($string), $prefix)) {
  1461. return false;
  1462. }
  1463. $len = strlen($prefix);
  1464. if (preg_match("#^(%[[:xdigit:]]{2}|.){{$len}}#", $string, $match)) {
  1465. return $match[0];
  1466. }
  1467. return false;
  1468. }
  1469. }