HTTP.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. <?php
  2. /**
  3. * Contains the most low-level helpers methods in Kohana:
  4. *
  5. * - Environment initialization
  6. * - Locating files within the cascading filesystem
  7. * - Auto-loading and transparent extension of classes
  8. * - Variable and path debugging
  9. *
  10. * @package Kohana
  11. * @category HTTP
  12. * @author Kohana Team
  13. * @since 3.1.0
  14. * @copyright (c) Kohana Team
  15. * @license https://koseven.ga/LICENSE.md
  16. */
  17. abstract class Kohana_HTTP {
  18. /**
  19. * @var The default protocol to use if it cannot be detected
  20. */
  21. public static $protocol = 'HTTP/1.1';
  22. /**
  23. * Issues a HTTP redirect.
  24. *
  25. * @param string $uri URI to redirect to
  26. * @param int $code HTTP Status code to use for the redirect
  27. * @throws HTTP_Exception
  28. */
  29. public static function redirect($uri = '', $code = 302)
  30. {
  31. $e = HTTP_Exception::factory($code);
  32. if ( ! $e instanceof HTTP_Exception_Redirect)
  33. throw new Kohana_Exception('Invalid redirect code \':code\'', [
  34. ':code' => $code
  35. ]);
  36. throw $e->location($uri);
  37. }
  38. /**
  39. * Checks the browser cache to see the response needs to be returned,
  40. * execution will halt and a 304 Not Modified will be sent if the
  41. * browser cache is up to date.
  42. *
  43. * @param Request $request Request
  44. * @param Response $response Response
  45. * @param string $etag Resource ETag
  46. * @throws HTTP_Exception_304
  47. * @return Response
  48. */
  49. public static function check_cache(Request $request, Response $response, $etag = NULL)
  50. {
  51. // Generate an etag if necessary
  52. if ($etag == NULL)
  53. {
  54. $etag = $response->generate_etag();
  55. }
  56. // Set the ETag header
  57. $response->headers('etag', $etag);
  58. // Add the Cache-Control header if it is not already set
  59. // This allows etags to be used with max-age, etc
  60. if ($response->headers('cache-control'))
  61. {
  62. $response->headers('cache-control', $response->headers('cache-control').', must-revalidate');
  63. }
  64. else
  65. {
  66. $response->headers('cache-control', 'must-revalidate');
  67. }
  68. // Check if we have a matching etag
  69. if ($request->headers('if-none-match') AND (string) $request->headers('if-none-match') === $etag)
  70. {
  71. // No need to send data again
  72. throw HTTP_Exception::factory(304)->headers('etag', $etag);
  73. }
  74. return $response;
  75. }
  76. /**
  77. * Parses a HTTP header string into an associative array
  78. *
  79. * @param string $header_string Header string to parse
  80. * @return HTTP_Header
  81. */
  82. public static function parse_header_string($header_string)
  83. {
  84. // If the PECL HTTP extension is loaded
  85. if (extension_loaded('http'))
  86. {
  87. // Use the fast method to parse header string
  88. $headers = version_compare(phpversion('http'), '2.0.0', '>=') ?
  89. \http\Header::parse($header_string) :
  90. http_parse_headers($header_string);
  91. return new HTTP_Header($headers);
  92. }
  93. // Otherwise we use the slower PHP parsing
  94. $headers = [];
  95. // Match all HTTP headers
  96. if (preg_match_all('/(\w[^\s:]*):[ ]*([^\r\n]*(?:\r\n[ \t][^\r\n]*)*)/', $header_string, $matches))
  97. {
  98. // Parse each matched header
  99. foreach ($matches[0] as $key => $value)
  100. {
  101. // If the header has not already been set
  102. if ( ! isset($headers[$matches[1][$key]]))
  103. {
  104. // Apply the header directly
  105. $headers[$matches[1][$key]] = $matches[2][$key];
  106. }
  107. // Otherwise there is an existing entry
  108. else
  109. {
  110. // If the entry is an array
  111. if (is_array($headers[$matches[1][$key]]))
  112. {
  113. // Apply the new entry to the array
  114. $headers[$matches[1][$key]][] = $matches[2][$key];
  115. }
  116. // Otherwise create a new array with the entries
  117. else
  118. {
  119. $headers[$matches[1][$key]] = [
  120. $headers[$matches[1][$key]],
  121. $matches[2][$key],
  122. ];
  123. }
  124. }
  125. }
  126. }
  127. // Return the headers
  128. return new HTTP_Header($headers);
  129. }
  130. /**
  131. * Parses the the HTTP request headers and returns an array containing
  132. * key value pairs. This method is slow, but provides an accurate
  133. * representation of the HTTP request.
  134. *
  135. * // Get http headers into the request
  136. * $request->headers = HTTP::request_headers();
  137. *
  138. * @return HTTP_Header
  139. */
  140. public static function request_headers()
  141. {
  142. // If running on apache server
  143. if (function_exists('apache_request_headers'))
  144. {
  145. // Return the much faster method
  146. return new HTTP_Header(apache_request_headers());
  147. }
  148. // If the PECL HTTP tools are installed
  149. elseif (extension_loaded('http'))
  150. {
  151. // Return the much faster method
  152. $headers = version_compare(phpversion('http'), '2.0.0', '>=') ?
  153. \http\Env::getRequestHeader() :
  154. http_get_request_headers();
  155. return new HTTP_Header($headers);
  156. }
  157. // Setup the output
  158. $headers = [];
  159. // Parse the content type
  160. if ( ! empty($_SERVER['CONTENT_TYPE']))
  161. {
  162. $headers['content-type'] = $_SERVER['CONTENT_TYPE'];
  163. }
  164. // Parse the content length
  165. if ( ! empty($_SERVER['CONTENT_LENGTH']))
  166. {
  167. $headers['content-length'] = $_SERVER['CONTENT_LENGTH'];
  168. }
  169. foreach ($_SERVER as $key => $value)
  170. {
  171. // If there is no HTTP header here, skip
  172. if (strpos($key, 'HTTP_') !== 0)
  173. {
  174. continue;
  175. }
  176. // This is a dirty hack to ensure HTTP_X_FOO_BAR becomes X-FOO-BAR
  177. $headers[str_replace('_', '-', substr($key, 5))] = $value;
  178. }
  179. return new HTTP_Header($headers);
  180. }
  181. /**
  182. * Processes an array of key value pairs and encodes
  183. * the values to meet RFC 3986
  184. *
  185. * @param array $params Params
  186. * @return string
  187. */
  188. public static function www_form_urlencode(array $params = [])
  189. {
  190. if ( ! $params)
  191. return;
  192. $encoded = [];
  193. foreach ($params as $key => $value)
  194. {
  195. $encoded[] = $key.'='.rawurlencode($value);
  196. }
  197. return implode('&', $encoded);
  198. }
  199. }