PageRenderTime 53ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/classes/kohana/request/client.php

https://github.com/jshaw86/core
PHP | 328 lines | 150 code | 42 blank | 136 comment | 29 complexity | 21f5b107767d34a6ad9cba8ec3d5e4e2 MD5 | raw file
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * Request Client
  4. *
  5. * @package Kohana
  6. * @category Base
  7. * @author Kohana Team
  8. * @copyright (c) 2008-2011 Kohana Team
  9. * @license http://kohanaframework.org/license
  10. * @since 3.1.0
  11. */
  12. abstract class Kohana_Request_Client {
  13. /**
  14. * @var Cache Caching library for request caching
  15. */
  16. protected $_cache;
  17. /**
  18. * @var boolean Defines whether this client should cache `private` cache directives
  19. * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
  20. */
  21. protected $_allow_private_cache = FALSE;
  22. /**
  23. * @var int The timestamp of the request
  24. */
  25. protected $_request_time;
  26. /**
  27. * @var int The timestamp of the response
  28. */
  29. protected $_response_time;
  30. /**
  31. * Creates a new `Request_Client` object,
  32. * allows for dependency injection.
  33. *
  34. * @param array $params Params
  35. */
  36. public function __construct(array $params = array())
  37. {
  38. if ($params)
  39. {
  40. foreach ($params as $key => $value)
  41. {
  42. if (method_exists($this, $key))
  43. {
  44. if (property_exists($this, $key) OR property_exists($this, '_'.$key))
  45. {
  46. $method = trim($key, '_');
  47. $this->$method($value);
  48. }
  49. }
  50. }
  51. }
  52. }
  53. /**
  54. * Processes the request, executing the controller action that handles this
  55. * request, determined by the [Route].
  56. *
  57. * 1. Before the controller action is called, the [Controller::before] method
  58. * will be called.
  59. * 2. Next the controller action will be called.
  60. * 3. After the controller action is called, the [Controller::after] method
  61. * will be called.
  62. *
  63. * By default, the output from the controller is captured and returned, and
  64. * no headers are sent.
  65. *
  66. * $request->execute();
  67. *
  68. * @param Request $request
  69. * @return Response
  70. * @throws Kohana_Exception
  71. * @uses [Kohana::$profiling]
  72. * @uses [Profiler]
  73. */
  74. abstract public function execute(Request $request);
  75. /**
  76. * Invalidate a cached response for the [Request] supplied.
  77. * This has the effect of deleting the response from the
  78. * [Cache] entry.
  79. *
  80. * @param Request $request Response to remove from cache
  81. * @return void
  82. */
  83. public function invalidate_cache(Request $request)
  84. {
  85. if ( ! $this->_cache instanceof Cache)
  86. return;
  87. $this->_cache->delete($this->_create_cache_key($request));
  88. return;
  89. }
  90. /**
  91. * Getter and setter for the internal caching engine,
  92. * used to cache responses if available and valid.
  93. *
  94. * @param Kohana_Cache cache engine to use for caching
  95. * @return Kohana_Cache
  96. * @return Kohana_Request_Client
  97. */
  98. public function cache(Cache $cache = NULL)
  99. {
  100. if ($cache === NULL)
  101. return $this->_cache;
  102. $this->_cache = $cache;
  103. return $this;
  104. }
  105. /**
  106. * Gets or sets the [Request_Client::allow_private_cache] setting.
  107. * If set to `TRUE`, the client will also cache cache-control directives
  108. * that have the `private` setting.
  109. *
  110. * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
  111. * @param boolean allow caching of privately marked responses
  112. * @return boolean
  113. * @return [Request_Client]
  114. */
  115. public function allow_private_cache($setting = NULL)
  116. {
  117. if ($setting === NULL)
  118. return $this->_allow_private_cache;
  119. $this->_allow_private_cache = (bool) $setting;
  120. return $this;
  121. }
  122. /**
  123. * Creates a cache key for the request to use for caching
  124. * [Kohana_Response] returned by [Request::execute].
  125. *
  126. * @param Request request
  127. * @return string
  128. * @return boolean
  129. */
  130. public function create_cache_key(Request $request)
  131. {
  132. return sha1($request->url());
  133. }
  134. /**
  135. * Controls whether the response can be cached. Uses HTTP
  136. * protocol to determine whether the response can be cached.
  137. *
  138. * @see RFC 2616 http://www.w3.org/Protocols/rfc2616/
  139. * @param Response $response The Response
  140. * @return boolean
  141. */
  142. public function set_cache(Response $response)
  143. {
  144. $headers = (array) $response->headers();
  145. if ($cache_control = arr::get($headers, 'cache-control'))
  146. {
  147. // Parse the cache control
  148. $cache_control = Response::parse_cache_control( (string) $cache_control);
  149. // If the no-cache or no-store directive is set, return
  150. if (array_intersect_key($cache_control, array('no-cache' => NULL, 'no-store' => NULL)))
  151. return FALSE;
  152. // Get the directives
  153. $directives = array_keys($cache_control);
  154. // Check for private cache and get out of here if invalid
  155. if ( ! $this->_allow_private_cache and in_array('private', $directives))
  156. {
  157. if ( ! isset($cache_control['s-maxage']))
  158. return FALSE;
  159. // If there is a s-maxage directive we can use that
  160. $cache_control['max-age'] = $cache_control['s-maxage'];
  161. }
  162. // Check that max-age has been set and if it is valid for caching
  163. if (isset($cache_control['max-age']) and (int) $cache_control['max-age'] < 1)
  164. return FALSE;
  165. }
  166. if ($expires = arr::get($headers, 'expires') and ! isset($cache_control['max-age']))
  167. {
  168. // Can't cache things that have expired already
  169. if (strtotime( (string) $expires) <= time())
  170. return FALSE;
  171. }
  172. return TRUE;
  173. }
  174. /**
  175. * Caches a [Response] using the supplied [Cache]
  176. * and the key generated by [Request_Client::_create_cache_key].
  177. *
  178. * If not response is supplied, the cache will be checked for an existing
  179. * one that is available.
  180. *
  181. * @param Request $request The request
  182. * @param Response $response Response
  183. * @return mixed
  184. */
  185. public function cache_response(Request $request, Response $response = NULL)
  186. {
  187. if ( ! $this->_cache instanceof Cache)
  188. return FALSE;
  189. // Check for Pragma: no-cache
  190. if ($pragma = $request->headers('pragma'))
  191. {
  192. if ($pragma instanceof HTTP_Header_Value and $pragma->key == 'no-cache')
  193. return FALSE;
  194. elseif (is_array($pragma) and isset($pragma['no-cache']))
  195. return FALSE;
  196. }
  197. if ( ! $response)
  198. {
  199. $response = $this->_cache->get($this->create_cache_key($request));
  200. return ($response !== NULL) ? $response : FALSE;
  201. }
  202. else
  203. {
  204. if (($ttl = $this->cache_lifetime($response)) === FALSE)
  205. return FALSE;
  206. return $this->_cache->set($this->create_cache_key($request), $response, $ttl);
  207. }
  208. }
  209. /**
  210. * Calculates the total Time To Live based on the specification
  211. * RFC 2616 cache lifetime rules.
  212. *
  213. * @param Response $response Response to evaluate
  214. * @return mixed TTL value or false if the response should not be cached
  215. */
  216. public function cache_lifetime(Response $response)
  217. {
  218. // Get out of here if this cannot be cached
  219. if ( ! $this->set_cache($response))
  220. return FALSE;
  221. // Calculate apparent age
  222. if ($date = $response->headers('date'))
  223. {
  224. $apparent_age = max(0, $this->_response_time - strtotime( (string) $date));
  225. }
  226. else
  227. {
  228. $apparent_age = max(0, $this->_response_time);
  229. }
  230. // Calculate corrected received age
  231. if ($age = $response->headers('age'))
  232. {
  233. $corrected_received_age = max($apparent_age, intval( (string) $age));
  234. }
  235. else
  236. {
  237. $corrected_received_age = $apparent_age;
  238. }
  239. // Corrected initial age
  240. $corrected_initial_age = $corrected_received_age + $this->request_execution_time();
  241. // Resident time
  242. $resident_time = time() - $this->_response_time;
  243. // Current age
  244. $current_age = $corrected_initial_age + $resident_time;
  245. // Prepare the cache freshness lifetime
  246. $ttl = NULL;
  247. // Cache control overrides
  248. if ($cache_control = $response->headers('cache-control'))
  249. {
  250. // Parse the cache control header
  251. $cache_control = Response::parse_cache_control( (string) $cache_control);
  252. if (isset($cache_control['max-age']))
  253. {
  254. $ttl = (int) $cache_control['max-age'];
  255. }
  256. if (isset($cache_control['s-maxage']) AND isset($cache_control['private']) AND $this->_allow_private_cache)
  257. {
  258. $ttl = (int) $cache_control['s-maxage'];
  259. }
  260. if (isset($cache_control['max-stale']) AND ! isset($cache_control['must-revalidate']))
  261. {
  262. $ttl = $current_age + (int) $cache_control['max-stale'];
  263. }
  264. }
  265. // If we have a TTL at this point, return
  266. if ($ttl !== NULL)
  267. return $ttl;
  268. if ($expires = $response->headers('expires'))
  269. return strtotime( (string) $expires) - $current_age;
  270. return FALSE;
  271. }
  272. /**
  273. * Returns the duration of the last request execution.
  274. * Either returns the time of completed requests or
  275. * `FALSE` if the request hasn't finished executing, or
  276. * is yet to be run.
  277. *
  278. * @return mixed
  279. */
  280. public function request_execution_time()
  281. {
  282. if ($this->_request_time === NULL OR $this->_response_time === NULL)
  283. return FALSE;
  284. return $this->_response_time - $this->_request_time;
  285. }
  286. }