PageRenderTime 83ms CodeModel.GetById 6ms RepoModel.GetById 0ms app.codeStats 0ms

/Nette/Web/HttpResponse.php

https://github.com/DocX/nette
PHP | 379 lines | 179 code | 75 blank | 125 comment | 31 complexity | daf5449bea77eea0512479d0e13b596c MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * Nette Framework
  4. *
  5. * Copyright (c) 2004, 2009 David Grudl (http://davidgrudl.com)
  6. *
  7. * This source file is subject to the "Nette license" that is bundled
  8. * with this package in the file license.txt.
  9. *
  10. * For more information please see http://nettephp.com
  11. *
  12. * @copyright Copyright (c) 2004, 2009 David Grudl
  13. * @license http://nettephp.com/license Nette license
  14. * @link http://nettephp.com
  15. * @category Nette
  16. * @package Nette\Web
  17. */
  18. /*namespace Nette\Web;*/
  19. require_once dirname(__FILE__) . '/../Object.php';
  20. require_once dirname(__FILE__) . '/../Web/IHttpResponse.php';
  21. /**
  22. * HttpResponse class.
  23. *
  24. * @author David Grudl
  25. * @copyright Copyright (c) 2004, 2009 David Grudl
  26. * @package Nette\Web
  27. *
  28. * @property int $code
  29. * @property-read array $headers
  30. * @property-read mixed $sent
  31. */
  32. final class HttpResponse extends /*Nette\*/Object implements IHttpResponse
  33. {
  34. /** @var bool Send invisible garbage for IE 6? */
  35. private static $fixIE = TRUE;
  36. /** @var string The domain in which the cookie will be available */
  37. public $cookieDomain = '';
  38. /** @var string The path in which the cookie will be available */
  39. public $cookiePath = '';
  40. /** @var string The path in which the cookie will be available */
  41. public $cookieSecure = FALSE;
  42. /** @var int HTTP response code */
  43. private $code = self::S200_OK;
  44. /**
  45. * Sets HTTP response code.
  46. * @param int
  47. * @return HttpResponse provides a fluent interface
  48. * @throws \InvalidArgumentException if code is invalid
  49. * @throws \InvalidStateException if HTTP headers have been sent
  50. */
  51. public function setCode($code)
  52. {
  53. $code = (int) $code;
  54. static $allowed = array(
  55. 200=>1, 201=>1, 202=>1, 203=>1, 204=>1, 205=>1, 206=>1,
  56. 300=>1, 301=>1, 302=>1, 303=>1, 304=>1, 307=>1,
  57. 400=>1, 401=>1, 403=>1, 404=>1, 406=>1, 408=>1, 410=>1, 412=>1, 415=>1, 416=>1,
  58. 500=>1, 501=>1, 503=>1, 505=>1
  59. );
  60. if (!isset($allowed[$code])) {
  61. throw new /*\*/InvalidArgumentException("Bad HTTP response '$code'.");
  62. } elseif (headers_sent($file, $line)) {
  63. throw new /*\*/InvalidStateException("Cannot set HTTP code after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : "."));
  64. } else {
  65. $this->code = $code;
  66. $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1';
  67. header($protocol . ' ' . $code, TRUE, $code);
  68. }
  69. return $this;
  70. }
  71. /**
  72. * Returns HTTP response code.
  73. * @return int
  74. */
  75. public function getCode()
  76. {
  77. return $this->code;
  78. }
  79. /**
  80. * Sends a HTTP header and replaces a previous one.
  81. * @param string header name
  82. * @param string header value
  83. * @return HttpResponse provides a fluent interface
  84. * @throws \InvalidStateException if HTTP headers have been sent
  85. */
  86. public function setHeader($name, $value)
  87. {
  88. if (headers_sent($file, $line)) {
  89. throw new /*\*/InvalidStateException("Cannot send header after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : "."));
  90. }
  91. if ($value === NULL && function_exists('header_remove')) {
  92. header_remove($name);
  93. } else {
  94. header($name . ': ' . $value, TRUE, $this->code);
  95. }
  96. return $this;
  97. }
  98. /**
  99. * Adds HTTP header.
  100. * @param string header name
  101. * @param string header value
  102. * @return void
  103. * @throws \InvalidStateException if HTTP headers have been sent
  104. */
  105. public function addHeader($name, $value)
  106. {
  107. if (headers_sent($file, $line)) {
  108. throw new /*\*/InvalidStateException("Cannot send header after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : "."));
  109. }
  110. header($name . ': ' . $value, FALSE, $this->code);
  111. }
  112. /**
  113. * Sends a Content-type HTTP header.
  114. * @param string mime-type
  115. * @param string charset
  116. * @return HttpResponse provides a fluent interface
  117. * @throws \InvalidStateException if HTTP headers have been sent
  118. */
  119. public function setContentType($type, $charset = NULL)
  120. {
  121. $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : ''));
  122. return $this;
  123. }
  124. /**
  125. * Redirects to a new URL. Note: call exit() after it.
  126. * @param string URL
  127. * @param int HTTP code
  128. * @return void
  129. * @throws \InvalidStateException if HTTP headers have been sent
  130. */
  131. public function redirect($url, $code = self::S302_FOUND)
  132. {
  133. if (isset($_SERVER['SERVER_SOFTWARE']) && preg_match('#^Microsoft-IIS/[1-5]#', $_SERVER['SERVER_SOFTWARE']) && $this->getHeader('Set-Cookie') !== NULL) {
  134. $this->setHeader('Refresh', "0;url=$url");
  135. return;
  136. }
  137. $this->setCode($code);
  138. $this->setHeader('Location', $url);
  139. echo "<h1>Redirect</h1>\n\n<p><a href=\"" . htmlSpecialChars($url) . "\">Please click here to continue</a>.</p>";
  140. }
  141. /**
  142. * Sets the number of seconds before a page cached on a browser expires.
  143. * @param mixed timestamp or number of seconds
  144. * @return void
  145. * @throws \InvalidStateException if HTTP headers have been sent
  146. */
  147. public function expire($seconds)
  148. {
  149. if (is_string($seconds) && !is_numeric($seconds)) {
  150. $seconds = strtotime($seconds);
  151. }
  152. if ($seconds > 0) {
  153. if ($seconds <= /*Nette\*/Tools::YEAR) {
  154. $seconds += time();
  155. }
  156. $this->setHeader('Cache-Control', 'max-age=' . ($seconds - time()));
  157. $this->setHeader('Expires', self::date($seconds));
  158. } else { // no cache
  159. $this->setHeader('Expires', 'Mon, 23 Jan 1978 10:00:00 GMT');
  160. $this->setHeader('Cache-Control', 's-maxage=0, max-age=0, must-revalidate');
  161. }
  162. }
  163. /**
  164. * Checks if headers have been sent.
  165. * @return bool
  166. */
  167. public function isSent()
  168. {
  169. return headers_sent();
  170. }
  171. /**
  172. * Return the value of the HTTP header.
  173. * @param string
  174. * @param mixed
  175. * @return mixed
  176. */
  177. public function getHeader($header, $default = NULL)
  178. {
  179. $header .= ':';
  180. $len = strlen($header);
  181. foreach (headers_list() as $item) {
  182. if (strncasecmp($item, $header, $len) === 0) {
  183. return ltrim(substr($item, $len));
  184. }
  185. }
  186. return $default;
  187. }
  188. /**
  189. * Returns a list of headers to sent.
  190. * @return array
  191. */
  192. public function getHeaders()
  193. {
  194. $headers = array();
  195. foreach (headers_list() as $header) {
  196. $a = strpos($header, ':');
  197. $headers[substr($header, 0, $a)] = (string) substr($header, $a + 2);
  198. }
  199. return $headers;
  200. }
  201. /**
  202. * Returns HTTP valid date format.
  203. * @param int timestamp
  204. * @return string
  205. */
  206. public static function date($time = NULL)
  207. {
  208. return gmdate('D, d M Y H:i:s \G\M\T', $time === NULL ? time() : $time);
  209. }
  210. /**
  211. * Enables compression. (warning: may not work)
  212. * @return bool
  213. */
  214. public function enableCompression()
  215. {
  216. if (headers_sent()) {
  217. return FALSE;
  218. }
  219. if ($this->getHeader('Content-Encoding') !== NULL) {
  220. return FALSE; // called twice
  221. }
  222. $ok = ob_gzhandler('', PHP_OUTPUT_HANDLER_START);
  223. if ($ok === FALSE) {
  224. return FALSE; // not allowed
  225. }
  226. if (function_exists('ini_set')) {
  227. ini_set('zlib.output_compression', 'Off');
  228. ini_set('zlib.output_compression_level', '6');
  229. }
  230. ob_start('ob_gzhandler', 1);
  231. return TRUE;
  232. }
  233. /**
  234. * @return void
  235. */
  236. public function __destruct()
  237. {
  238. if (self::$fixIE) {
  239. // Sends invisible garbage for IE.
  240. if (!isset($_SERVER['HTTP_USER_AGENT']) || strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE ') === FALSE) return;
  241. if (!in_array($this->code, array(400, 403, 404, 405, 406, 408, 409, 410, 500, 501, 505), TRUE)) return;
  242. if ($this->getHeader('Content-Type', 'text/html') !== 'text/html') return;
  243. $s = " \t\r\n";
  244. for ($i = 2e3; $i; $i--) echo $s{rand(0, 3)};
  245. self::$fixIE = FALSE;
  246. }
  247. }
  248. /**
  249. * Sends a cookie.
  250. * @param string name of the cookie
  251. * @param string value
  252. * @param mixed expiration as unix timestamp or number of seconds; Value 0 means "until the browser is closed"
  253. * @param string
  254. * @param string
  255. * @param bool
  256. * @return HttpResponse provides a fluent interface
  257. * @throws \InvalidStateException if HTTP headers have been sent
  258. */
  259. public function setCookie($name, $value, $expire, $path = NULL, $domain = NULL, $secure = NULL)
  260. {
  261. if (headers_sent($file, $line)) {
  262. throw new /*\*/InvalidStateException("Cannot set cookie after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : "."));
  263. }
  264. if (is_string($expire) && !is_numeric($expire)) {
  265. $expire = strtotime($expire);
  266. } elseif ($expire > 0 && $expire <= /*Nette\*/Tools::YEAR) {
  267. $expire += time();
  268. }
  269. setcookie(
  270. $name,
  271. $value,
  272. $expire,
  273. $path === NULL ? $this->cookiePath : (string) $path,
  274. $domain === NULL ? $this->cookieDomain : (string) $domain, // . '; httponly'
  275. $secure === NULL ? $this->cookieSecure : (bool) $secure,
  276. TRUE // added in PHP 5.2.0.
  277. );
  278. return $this;
  279. }
  280. /**
  281. * Deletes a cookie.
  282. * @param string name of the cookie.
  283. * @param string
  284. * @param string
  285. * @param bool
  286. * @return void
  287. * @throws \InvalidStateException if HTTP headers have been sent
  288. */
  289. public function deleteCookie($name, $path = NULL, $domain = NULL, $secure = NULL)
  290. {
  291. if (headers_sent($file, $line)) {
  292. throw new /*\*/InvalidStateException("Cannot delete cookie after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : "."));
  293. }
  294. setcookie(
  295. $name,
  296. FALSE,
  297. 254400000,
  298. $path === NULL ? $this->cookiePath : (string) $path,
  299. $domain === NULL ? $this->cookieDomain : (string) $domain, // . '; httponly'
  300. $secure === NULL ? $this->cookieSecure : (bool) $secure,
  301. TRUE // added in PHP 5.2.0.
  302. );
  303. }
  304. }