PageRenderTime 41ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/system/classes/kohana/http/header.php

https://bitbucket.org/waqar4at/scrumie
PHP | 310 lines | 149 code | 39 blank | 122 comment | 14 complexity | 7a3eb52e7b10489de0397fe4ed46376d MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * The Kohana_HTTP_Header class provides an Object-Orientated interface
  4. * to HTTP headers. This can parse header arrays returned from the
  5. * PHP functions `apache_request_headers()` or the `http_parse_headers()`
  6. * function available within the PECL HTTP library.
  7. *
  8. * @package Kohana
  9. * @category HTTP
  10. * @author Kohana Team
  11. * @since 3.1.0
  12. * @copyright (c) 2008-2011 Kohana Team
  13. * @license http://kohanaphp.com/license
  14. */
  15. class Kohana_HTTP_Header extends ArrayObject {
  16. /**
  17. * @var boolean Controls whether to automatically sort headers by quality value
  18. */
  19. public static $sort_by_quality = FALSE;
  20. /**
  21. * @var array Default positive filter for sorting header values
  22. */
  23. public static $default_sort_filter = array('accept','accept-charset','accept-encoding','accept-language');
  24. /**
  25. * Parses HTTP Header values and creating an appropriate object
  26. * depending on type; i.e. accept-type, accept-char, cache-control etc.
  27. *
  28. * $header_values_array = HTTP_Header::parse_header_values(array('cache-control' => 'max-age=200; public'));
  29. *
  30. * @param array $header_values Values to parse
  31. * @param array $header_commas_allowed Header values where commas are not delimiters (usually date)
  32. * @return array
  33. */
  34. public static function parse_header_values(array $header_values, array $header_commas_allowed = array('user-agent', 'date', 'expires', 'last-modified'))
  35. {
  36. /**
  37. * @see http://www.w3.org/Protocols/rfc2616/rfc2616.html
  38. *
  39. * HTTP header declarations should be treated as case-insensitive
  40. */
  41. $header_values = array_change_key_case($header_values, CASE_LOWER);
  42. // Foreach of the header values applied
  43. foreach ($header_values as $key => $value)
  44. {
  45. if (is_array($value))
  46. {
  47. $values = array();
  48. if (Arr::is_assoc($value))
  49. {
  50. foreach ($value as $k => $v)
  51. {
  52. $values[] = HTTP_Header::parse_header_values($v);
  53. }
  54. }
  55. else
  56. {
  57. // RFC 2616 allows multiple headers with same name if they can be
  58. // concatinated using commas without altering the original message.
  59. // This usually occurs with multiple Set-Cookie: headers
  60. $array = array();
  61. foreach ($value as $k => $v)
  62. {
  63. // Break value into component parts
  64. $v = explode(';', $v);
  65. // Do some nasty parsing to flattern the array into components,
  66. // parsing key values
  67. $array = Arr::flatten(array_map('HTTP_Header_Value::parse_key_value', $v));
  68. // Get the K/V component and extract the first element
  69. $key_value_component = array_slice($array, 0, 1, TRUE);
  70. array_shift($array);
  71. // Create the HTTP_Header_Value component array
  72. $http_header['key'] = key($key_value_component);
  73. $http_header['value'] = current($key_value_component);
  74. $http_header['properties'] = $array;
  75. // Create the HTTP_Header_Value
  76. $values[] = new HTTP_Header_Value($http_header);
  77. }
  78. }
  79. // Assign HTTP_Header_Value array to the header
  80. $header_values[$key] = $values;
  81. continue;
  82. }
  83. // If the key allows commas or no commas are found
  84. if (in_array($key, $header_commas_allowed) or (strpos($value, ',') === FALSE))
  85. {
  86. // If the key is user-agent, we don't want to parse the string
  87. if ($key === 'user-agent')
  88. {
  89. $header_values[$key] = new HTTP_Header_Value($value, TRUE);
  90. }
  91. // Else, behave normally
  92. else
  93. {
  94. $header_values[$key] = new HTTP_Header_Value($value);
  95. }
  96. // Move to next header
  97. continue;
  98. }
  99. // Create an array of the values and clear any whitespace
  100. $value = array_map('trim', explode(',', $value));
  101. $parsed_values = array();
  102. // Foreach value
  103. foreach ($value as $v)
  104. {
  105. $v = new HTTP_Header_Value($v);
  106. // Convert the value string into an object
  107. if ($v->key === NULL)
  108. {
  109. $parsed_values[] = $v;
  110. }
  111. else
  112. {
  113. $parsed_values[$v->key] = $v;
  114. }
  115. }
  116. // Apply parsed value to the header
  117. $header_values[$key] = $parsed_values;
  118. }
  119. // Return the parsed header values
  120. return $header_values;
  121. }
  122. /**
  123. * Constructor method for [Kohana_HTTP_Header]. Uses the standard constructor
  124. * of the parent `ArrayObject` class.
  125. *
  126. * $header_object = new HTTP_Header(array('x-powered-by' => 'Kohana 3.1.x', 'expires' => '...'));
  127. *
  128. * @param mixed Input array
  129. * @param int Flags
  130. * @param string The iterator class to use
  131. */
  132. public function __construct($input, $flags = NULL, $iterator_class = 'ArrayIterator')
  133. {
  134. // Parse the values into [HTTP_Header_Values]
  135. parent::__construct(HTTP_Header::parse_header_values($input), $flags, $iterator_class);
  136. // If sort by quality is set, sort the fields by q=0.0 value
  137. if (HTTP_Header::$sort_by_quality)
  138. {
  139. $this->sort_values_by_quality();
  140. }
  141. }
  142. /**
  143. * Returns the header object as a string, including
  144. * the terminating new line
  145. *
  146. * // Return the header as a string
  147. * echo (string) $request->headers();
  148. *
  149. * @return string
  150. */
  151. public function __toString()
  152. {
  153. $header = '';
  154. foreach ($this as $key => $value)
  155. {
  156. if (is_array($value))
  157. {
  158. $header .= $key.': '.(implode(', ', $value))."\r\n";
  159. }
  160. else
  161. {
  162. $header .= $key.': '.$value."\r\n";
  163. }
  164. }
  165. return $header."\n";
  166. }
  167. /**
  168. * Overloads the `ArrayObject::exchangeArray()` method to ensure all
  169. * values passed are parsed correctly into a [Kohana_HTTP_Header_Value].
  170. *
  171. * // Input new headers
  172. * $headers->exchangeArray(array(
  173. * 'date' => 'Wed, 24 Nov 2010 21:09:23 GMT',
  174. * 'cache-control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
  175. * ));
  176. *
  177. * @param array $array Array to exchange
  178. * @return array
  179. */
  180. public function exchangeArray($array)
  181. {
  182. return parent::exchangeArray(HTTP_Header::parse_header_values($array));
  183. }
  184. /**
  185. * Overloads the `ArrayObject::offsetSet` method to ensure any
  186. * access is correctly converted to the correct object type.
  187. *
  188. * // Add a new header from encoded string
  189. * $headers['cache-control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
  190. *
  191. * @param mixed $index Key
  192. * @param mixed $newval Value
  193. * @return void
  194. */
  195. public function offsetSet($index, $newval)
  196. {
  197. if (is_array($newval) AND (current($newval) instanceof HTTP_Header_Value))
  198. return parent::offsetSet(strtolower($index), $newval);
  199. elseif ( ! $newval instanceof HTTP_Header_Value)
  200. {
  201. $newval = new HTTP_Header_Value($newval);
  202. }
  203. parent::offsetSet(strtolower($index), $newval);
  204. }
  205. /**
  206. * Sort the headers by quality property if the header matches the
  207. * [Kohana_HTTP_Header::$default_sort_filter] definition.
  208. *
  209. * #### Default sort values
  210. *
  211. * - Accept
  212. * - Accept-Chars
  213. * - Accept-Encoding
  214. * - Accept-Lang
  215. *
  216. * @param array $filter Header fields to parse
  217. * @return self
  218. */
  219. public function sort_values_by_quality(array $filter = array())
  220. {
  221. // If a filter argument is supplied
  222. if ($filter)
  223. {
  224. // Apply filter and store previous
  225. $previous_filter = HTTP_Header::$default_sort_filter;
  226. HTTP_Header::$default_sort_filter = $filter;
  227. }
  228. // Get a copy of this ArrayObject
  229. $values = $this->getArrayCopy();
  230. foreach ($values as $key => $value)
  231. {
  232. if ( ! is_array($value) or ! in_array($key, HTTP_Header::$default_sort_filter))
  233. {
  234. unset($values[$key]);
  235. continue;
  236. }
  237. // Sort them by comparison
  238. uasort($value, array($this, '_sort_by_comparison'));
  239. $values[$key] = $value;
  240. }
  241. // Return filter to previous state if required
  242. if ($filter)
  243. {
  244. HTTP_Header::$default_sort_filter = $previous_filter;
  245. }
  246. foreach ($values as $key => $value)
  247. {
  248. $this[$key] = $value;
  249. }
  250. // Return this
  251. return $this;
  252. }
  253. protected function _sort_by_comparison($value_a, $value_b)
  254. {
  255. // Test for correct instance type
  256. if ( ! $value_a instanceof HTTP_Header_Value OR ! $value_b instanceof HTTP_Header_Value)
  257. {
  258. // Return neutral if cannot test value
  259. return 0;
  260. }
  261. // Extract the qualities
  262. $a = (float) Arr::get($value_a->properties, 'q', HTTP_Header_Value::$default_quality);
  263. $b = (float) Arr::get($value_b->properties, 'q', HTTP_Header_Value::$default_quality);
  264. if ($a == $b)
  265. return 0;
  266. elseif ($a < $b)
  267. return 1;
  268. elseif ($a > $b)
  269. return -1;
  270. }
  271. } // End Kohana_HTTP_Header