PageRenderTime 57ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/Nette/Web/HttpRequest.php

https://github.com/Vrtak-CZ/ORM-benchmark
PHP | 712 lines | 375 code | 156 blank | 181 comment | 80 complexity | b3bfcf66b2ab7cedf03070b31286a5f8 MD5 | raw file
  1. <?php
  2. /**
  3. * Nette Framework
  4. *
  5. * @copyright Copyright (c) 2004, 2010 David Grudl
  6. * @license http://nette.org/license Nette license
  7. * @link http://nette.org
  8. * @category Nette
  9. * @package Nette\Web
  10. */
  11. namespace Nette\Web;
  12. use Nette,
  13. Nette\String;
  14. /**
  15. * HttpRequest provides access scheme for request sent via HTTP.
  16. *
  17. * @copyright Copyright (c) 2004, 2010 David Grudl
  18. * @package Nette\Web
  19. *
  20. * @property UriScript $uri
  21. * @property-read Uri $originalUri
  22. * @property-read array $query
  23. * @property-read array $post
  24. * @property-read string $postRaw
  25. * @property-read array $files
  26. * @property-read array $cookies
  27. * @property-read string $method
  28. * @property-read array $headers
  29. * @property-read Uri $referer
  30. * @property-read string $remoteAddress
  31. * @property-read string $remoteHost
  32. * @property-read bool $secured
  33. */
  34. class HttpRequest extends Nette\Object implements IHttpRequest
  35. {
  36. /** @var array */
  37. protected $query;
  38. /** @var array */
  39. protected $post;
  40. /** @var array */
  41. protected $files;
  42. /** @var array */
  43. protected $cookies;
  44. /** @var UriScript {@link HttpRequest::getUri()} */
  45. protected $uri;
  46. /** @var Uri {@link HttpRequest::getOriginalUri()} */
  47. protected $originalUri;
  48. /** @var array {@link HttpRequest::getHeaders()} */
  49. protected $headers;
  50. /** @var array */
  51. protected $uriFilter = array(
  52. PHP_URL_PATH => array('#/{2,}#' => '/'), // '%20' => ''
  53. 0 => array(), // '#[.,)]$#' => ''
  54. );
  55. /** @var string */
  56. protected $encoding;
  57. /********************* URI ****************d*g**/
  58. /**
  59. * Returns URL object.
  60. * @return UriScript
  61. */
  62. final public function getUri()
  63. {
  64. if ($this->uri === NULL) {
  65. $this->detectUri();
  66. }
  67. return $this->uri;
  68. }
  69. /**
  70. * Sets URL object.
  71. * @param UriScript
  72. * @return HttpRequest provides a fluent interface
  73. */
  74. public function setUri(UriScript $uri)
  75. {
  76. $this->uri = clone $uri;
  77. $this->query = NULL;
  78. $this->uri->canonicalize();
  79. $this->uri->freeze();
  80. return $this;
  81. }
  82. /**
  83. * Returns URL object.
  84. * @return Uri
  85. */
  86. final public function getOriginalUri()
  87. {
  88. if ($this->originalUri === NULL) {
  89. $this->detectUri();
  90. }
  91. return $this->originalUri;
  92. }
  93. /**
  94. * Sets request URI filter.
  95. * @param string pattern to search for
  96. * @param string string to replace
  97. * @param int PHP_URL_PATH or NULL
  98. * @return void
  99. */
  100. public function addUriFilter($pattern, $replacement = '', $component = NULL)
  101. {
  102. $pattern = '#' . $pattern . '#';
  103. $component = $component === PHP_URL_PATH ? PHP_URL_PATH : 0;
  104. if ($replacement === NULL) {
  105. unset($this->uriFilter[$component][$pattern]);
  106. } else {
  107. $this->uriFilter[$component][$pattern] = $replacement;
  108. }
  109. $this->uri = NULL;
  110. }
  111. /**
  112. * Returns request URI filter.
  113. * @return array
  114. */
  115. final public function getUriFilters()
  116. {
  117. return $this->uriFilter;
  118. }
  119. /**
  120. * Detects uri, base path and script path of the request.
  121. * @return void
  122. */
  123. protected function detectUri()
  124. {
  125. $uri = $this->uri = new UriScript;
  126. $uri->scheme = $this->isSecured() ? 'https' : 'http';
  127. $uri->user = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : '';
  128. $uri->password = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '';
  129. // host & port
  130. if (isset($_SERVER['HTTP_HOST'])) {
  131. $pair = explode(':', $_SERVER['HTTP_HOST']);
  132. } elseif (isset($_SERVER['SERVER_NAME'])) {
  133. $pair = explode(':', $_SERVER['SERVER_NAME']);
  134. } else {
  135. $pair = array('');
  136. }
  137. $uri->host = $pair[0];
  138. if (isset($pair[1])) {
  139. $uri->port = (int) $pair[1];
  140. } elseif (isset($_SERVER['SERVER_PORT'])) {
  141. $uri->port = (int) $_SERVER['SERVER_PORT'];
  142. }
  143. // path & query
  144. if (isset($_SERVER['REQUEST_URI'])) { // Apache, IIS 6.0
  145. $requestUri = $_SERVER['REQUEST_URI'];
  146. } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { // IIS 5.0 (PHP as CGI ?)
  147. $requestUri = $_SERVER['ORIG_PATH_INFO'];
  148. if (isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] != '') {
  149. $requestUri .= '?' . $_SERVER['QUERY_STRING'];
  150. }
  151. } else {
  152. $requestUri = '';
  153. }
  154. $tmp = explode('?', $requestUri, 2);
  155. $this->originalUri = new Uri($uri);
  156. $this->originalUri->path = $tmp[0];
  157. $this->originalUri->query = isset($tmp[1]) ? $tmp[1] : '';
  158. $this->originalUri->freeze();
  159. $requestUri = String::replace($requestUri, $this->uriFilter[0]);
  160. $tmp = explode('?', $requestUri, 2);
  161. $uri->path = String::replace($tmp[0], $this->uriFilter[PHP_URL_PATH]);
  162. $uri->query = isset($tmp[1]) ? $tmp[1] : '';
  163. // normalized uri
  164. $uri->canonicalize();
  165. $uri->path = String::fixEncoding($uri->path);
  166. // detect base URI-path - inspired by Zend Framework (c) Zend Technologies USA Inc. (http://www.zend.com), new BSD license
  167. $filename = isset($_SERVER['SCRIPT_FILENAME']) ? basename($_SERVER['SCRIPT_FILENAME']) : NULL;
  168. $scriptPath = '';
  169. if (isset($_SERVER['SCRIPT_NAME']) && basename($_SERVER['SCRIPT_NAME']) === $filename) {
  170. $scriptPath = rtrim($_SERVER['SCRIPT_NAME'], '/');
  171. } elseif (isset($_SERVER['PHP_SELF']) && basename($_SERVER['PHP_SELF']) === $filename) {
  172. $scriptPath = $_SERVER['PHP_SELF'];
  173. } elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $filename) {
  174. $scriptPath = $_SERVER['ORIG_SCRIPT_NAME']; // 1and1 shared hosting compatibility
  175. } elseif (isset($_SERVER['PHP_SELF'], $_SERVER['SCRIPT_FILENAME'])) {
  176. // Backtrack up the script_filename to find the portion matching php_self
  177. $path = $_SERVER['PHP_SELF'];
  178. $segs = explode('/', trim($_SERVER['SCRIPT_FILENAME'], '/'));
  179. $segs = array_reverse($segs);
  180. $index = 0;
  181. $last = count($segs);
  182. do {
  183. $seg = $segs[$index];
  184. $scriptPath = '/' . $seg . $scriptPath;
  185. $index++;
  186. } while (($last > $index) && (FALSE !== ($pos = strpos($path, $scriptPath))) && (0 != $pos));
  187. }
  188. // Does the scriptPath have anything in common with the request_uri?
  189. if (strncmp($uri->path, $scriptPath, strlen($scriptPath)) === 0) {
  190. // whole $scriptPath in URL
  191. $uri->scriptPath = $scriptPath;
  192. } elseif (strncmp($uri->path, $scriptPath, strrpos($scriptPath, '/') + 1) === 0) {
  193. // directory portion of $scriptPath in URL
  194. $uri->scriptPath = substr($scriptPath, 0, strrpos($scriptPath, '/') + 1);
  195. } elseif (strpos($uri->path, basename($scriptPath)) === FALSE) {
  196. // no match whatsoever; set it blank
  197. $uri->scriptPath = '/';
  198. } elseif ((strlen($uri->path) >= strlen($scriptPath))
  199. && ((FALSE !== ($pos = strpos($uri->path, $scriptPath))) && ($pos !== 0))) {
  200. // If using mod_rewrite or ISAPI_Rewrite strip the script filename
  201. // out of scriptPath. $pos !== 0 makes sure it is not matching a value
  202. // from PATH_INFO or QUERY_STRING
  203. $uri->scriptPath = substr($uri->path, 0, $pos + strlen($scriptPath));
  204. } else {
  205. $uri->scriptPath = $scriptPath;
  206. }
  207. $uri->freeze();
  208. }
  209. /********************* query, post, files & cookies ****************d*g**/
  210. /**
  211. * Returns variable provided to the script via URL query ($_GET).
  212. * If no key is passed, returns the entire array.
  213. * @param string key
  214. * @param mixed default value
  215. * @return mixed
  216. */
  217. final public function getQuery($key = NULL, $default = NULL)
  218. {
  219. if ($this->query === NULL) {
  220. $this->initialize();
  221. }
  222. if (func_num_args() === 0) {
  223. return $this->query;
  224. } elseif (isset($this->query[$key])) {
  225. return $this->query[$key];
  226. } else {
  227. return $default;
  228. }
  229. }
  230. /**
  231. * Returns variable provided to the script via POST method ($_POST).
  232. * If no key is passed, returns the entire array.
  233. * @param string key
  234. * @param mixed default value
  235. * @return mixed
  236. */
  237. final public function getPost($key = NULL, $default = NULL)
  238. {
  239. if ($this->post === NULL) {
  240. $this->initialize();
  241. }
  242. if (func_num_args() === 0) {
  243. return $this->post;
  244. } elseif (isset($this->post[$key])) {
  245. return $this->post[$key];
  246. } else {
  247. return $default;
  248. }
  249. }
  250. /**
  251. * Returns HTTP POST data in raw format (only for "application/x-www-form-urlencoded").
  252. * @return string
  253. */
  254. public function getPostRaw()
  255. {
  256. return file_get_contents('php://input');
  257. }
  258. /**
  259. * Returns uploaded file.
  260. * @param string key (or more keys)
  261. * @return HttpUploadedFile
  262. */
  263. final public function getFile($key)
  264. {
  265. if ($this->files === NULL) {
  266. $this->initialize();
  267. }
  268. $args = func_get_args();
  269. return Nette\ArrayTools::get($this->files, $args);
  270. }
  271. /**
  272. * Returns uploaded files.
  273. * @return array
  274. */
  275. final public function getFiles()
  276. {
  277. if ($this->files === NULL) {
  278. $this->initialize();
  279. }
  280. return $this->files;
  281. }
  282. /**
  283. * Returns variable provided to the script via HTTP cookies.
  284. * @param string key
  285. * @param mixed default value
  286. * @return mixed
  287. */
  288. final public function getCookie($key, $default = NULL)
  289. {
  290. if ($this->cookies === NULL) {
  291. $this->initialize();
  292. }
  293. if (func_num_args() === 0) {
  294. return $this->cookies;
  295. } elseif (isset($this->cookies[$key])) {
  296. return $this->cookies[$key];
  297. } else {
  298. return $default;
  299. }
  300. }
  301. /**
  302. * Returns variables provided to the script via HTTP cookies.
  303. * @return array
  304. */
  305. final public function getCookies()
  306. {
  307. if ($this->cookies === NULL) {
  308. $this->initialize();
  309. }
  310. return $this->cookies;
  311. }
  312. /**
  313. * Recursively converts and checks encoding.
  314. * @param array
  315. * @param string
  316. * @return HttpRequest provides a fluent interface
  317. */
  318. public function setEncoding($encoding)
  319. {
  320. if (strcasecmp($encoding, $this->encoding)) {
  321. $this->encoding = $encoding;
  322. $this->query = $this->post = $this->cookies = $this->files = NULL; // reinitialization required
  323. }
  324. return $this;
  325. }
  326. /**
  327. * Initializes $this->query, $this->files, $this->cookies and $this->files arrays
  328. * @return void
  329. */
  330. public function initialize()
  331. {
  332. $filter = (!in_array(ini_get("filter.default"), array("", "unsafe_raw")) || ini_get("filter.default_flags"));
  333. parse_str($this->getUri()->query, $this->query);
  334. if (!$this->query) {
  335. $this->query = $filter ? filter_input_array(INPUT_GET, FILTER_UNSAFE_RAW) : (empty($_GET) ? array() : $_GET);
  336. }
  337. $this->post = $filter ? filter_input_array(INPUT_POST, FILTER_UNSAFE_RAW) : (empty($_POST) ? array() : $_POST);
  338. $this->cookies = $filter ? filter_input_array(INPUT_COOKIE, FILTER_UNSAFE_RAW) : (empty($_COOKIE) ? array() : $_COOKIE);
  339. $gpc = (bool) get_magic_quotes_gpc();
  340. $enc = (bool) $this->encoding;
  341. $old = error_reporting(error_reporting() ^ E_NOTICE);
  342. $nonChars = '#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u';
  343. // remove fucking quotes and check (and optionally convert) encoding
  344. if ($gpc || $enc) {
  345. $utf = strcasecmp($this->encoding, 'UTF-8') === 0;
  346. $list = array(& $this->query, & $this->post, & $this->cookies);
  347. while (list($key, $val) = each($list)) {
  348. foreach ($val as $k => $v) {
  349. unset($list[$key][$k]);
  350. if ($gpc) {
  351. $k = stripslashes($k);
  352. }
  353. if ($enc && is_string($k) && (preg_match($nonChars, $k) || preg_last_error())) {
  354. // invalid key -> ignore
  355. } elseif (is_array($v)) {
  356. $list[$key][$k] = $v;
  357. $list[] = & $list[$key][$k];
  358. } else {
  359. if ($gpc && !$filter) {
  360. $v = stripSlashes($v);
  361. }
  362. if ($enc) {
  363. if ($utf) {
  364. $v = String::fixEncoding($v);
  365. } else {
  366. if (!String::checkEncoding($v)) {
  367. $v = iconv($this->encoding, 'UTF-8//IGNORE', $v);
  368. }
  369. $v = html_entity_decode($v, ENT_QUOTES, 'UTF-8');
  370. }
  371. $v = preg_replace($nonChars, '', $v);
  372. }
  373. $list[$key][$k] = $v;
  374. }
  375. }
  376. }
  377. unset($list, $key, $val, $k, $v);
  378. }
  379. // structure $files and create HttpUploadedFile objects
  380. $this->files = array();
  381. $list = array();
  382. if (!empty($_FILES)) {
  383. foreach ($_FILES as $k => $v) {
  384. if ($enc && is_string($k) && (preg_match($nonChars, $k) || preg_last_error())) continue;
  385. $v['@'] = & $this->files[$k];
  386. $list[] = $v;
  387. }
  388. }
  389. while (list(, $v) = each($list)) {
  390. if (!isset($v['name'])) {
  391. continue;
  392. } elseif (!is_array($v['name'])) {
  393. if ($gpc) {
  394. $v['name'] = stripSlashes($v['name']);
  395. }
  396. if ($enc) {
  397. $v['name'] = preg_replace($nonChars, '', String::fixEncoding($v['name']));
  398. }
  399. $v['@'] = new HttpUploadedFile($v);
  400. continue;
  401. }
  402. foreach ($v['name'] as $k => $foo) {
  403. if ($enc && is_string($k) && (preg_match($nonChars, $k) || preg_last_error())) continue;
  404. $list[] = array(
  405. 'name' => $v['name'][$k],
  406. 'type' => $v['type'][$k],
  407. 'size' => $v['size'][$k],
  408. 'tmp_name' => $v['tmp_name'][$k],
  409. 'error' => $v['error'][$k],
  410. '@' => & $v['@'][$k],
  411. );
  412. }
  413. }
  414. error_reporting($old);
  415. }
  416. /********************* method & headers ****************d*g**/
  417. /**
  418. * Returns HTTP request method (GET, POST, HEAD, PUT, ...). The method is case-sensitive.
  419. * @return string
  420. */
  421. public function getMethod()
  422. {
  423. return isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : NULL;
  424. }
  425. /**
  426. * Checks if the request method is the given one.
  427. * @param string
  428. * @return bool
  429. */
  430. public function isMethod($method)
  431. {
  432. return isset($_SERVER['REQUEST_METHOD']) ? strcasecmp($_SERVER['REQUEST_METHOD'], $method) === 0 : FALSE;
  433. }
  434. /**
  435. * Checks if the request method is POST.
  436. * @return bool
  437. */
  438. public function isPost()
  439. {
  440. return $this->isMethod('POST');
  441. }
  442. /**
  443. * Return the value of the HTTP header. Pass the header name as the
  444. * plain, HTTP-specified header name (e.g. 'Accept-Encoding').
  445. * @param string
  446. * @param mixed
  447. * @return mixed
  448. */
  449. final public function getHeader($header, $default = NULL)
  450. {
  451. $header = strtolower($header);
  452. $headers = $this->getHeaders();
  453. if (isset($headers[$header])) {
  454. return $headers[$header];
  455. } else {
  456. return $default;
  457. }
  458. }
  459. /**
  460. * Returns all HTTP headers.
  461. * @return array
  462. */
  463. public function getHeaders()
  464. {
  465. if ($this->headers === NULL) {
  466. // lazy initialization
  467. if (function_exists('apache_request_headers')) {
  468. $this->headers = array_change_key_case(apache_request_headers(), CASE_LOWER);
  469. } else {
  470. $this->headers = array();
  471. foreach ($_SERVER as $k => $v) {
  472. if (strncmp($k, 'HTTP_', 5) == 0) {
  473. $k = substr($k, 5);
  474. } elseif (strncmp($k, 'CONTENT_', 8)) {
  475. continue;
  476. }
  477. $this->headers[ strtr(strtolower($k), '_', '-') ] = $v;
  478. }
  479. }
  480. }
  481. return $this->headers;
  482. }
  483. /**
  484. * Returns referrer.
  485. * @return Uri|NULL
  486. */
  487. final public function getReferer()
  488. {
  489. $uri = self::getHeader('referer');
  490. return $uri ? new Uri($uri) : NULL;
  491. }
  492. /**
  493. * Is the request is sent via secure channel (https).
  494. * @return bool
  495. */
  496. public function isSecured()
  497. {
  498. return isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off');
  499. }
  500. /**
  501. * Is AJAX request?
  502. * @return bool
  503. */
  504. public function isAjax()
  505. {
  506. return $this->getHeader('X-Requested-With') === 'XMLHttpRequest';
  507. }
  508. /**
  509. * Returns the IP address of the remote client.
  510. * @return string
  511. */
  512. public function getRemoteAddress()
  513. {
  514. return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : NULL;
  515. }
  516. /**
  517. * Returns the host of the remote client.
  518. * @return string
  519. */
  520. public function getRemoteHost()
  521. {
  522. if (!isset($_SERVER['REMOTE_HOST'])) {
  523. if (!isset($_SERVER['REMOTE_ADDR'])) {
  524. return NULL;
  525. }
  526. $_SERVER['REMOTE_HOST'] = getHostByAddr($_SERVER['REMOTE_ADDR']);
  527. }
  528. return $_SERVER['REMOTE_HOST'];
  529. }
  530. /**
  531. * Parse Accept-Language header and returns prefered language.
  532. * @param array Supported languages
  533. * @return string
  534. */
  535. public function detectLanguage(array $langs)
  536. {
  537. $header = $this->getHeader('accept-language');
  538. if (!$header) return NULL;
  539. $s = strtolower($header); // case insensitive
  540. $s = strtr($s, '_', '-'); // cs_CZ means cs-CZ
  541. rsort($langs); // first more specific
  542. preg_match_all('#(' . implode('|', $langs) . ')(?:-[^\s,;=]+)?\s*(?:;\s*q=([0-9.]+))?#', $s, $matches);
  543. if (!$matches[0]) {
  544. return NULL;
  545. }
  546. $max = 0;
  547. $lang = NULL;
  548. foreach ($matches[1] as $key => $value) {
  549. $q = $matches[2][$key] === '' ? 1.0 : (float) $matches[2][$key];
  550. if ($q > $max) {
  551. $max = $q; $lang = $value;
  552. }
  553. }
  554. return $lang;
  555. }
  556. }