CacheClient.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. <?php
  2. namespace Moinax\TvDb\Http;
  3. use Moinax\TvDb\CurlException;
  4. use Moinax\TvDb\Http\Cache\Cache;
  5. class CacheClient implements HttpClient
  6. {
  7. /**
  8. * Cache to store resources.
  9. *
  10. * @var Cache
  11. */
  12. private $cache;
  13. /**
  14. * Minimum resource age before doing any request.
  15. *
  16. * @var integer
  17. */
  18. private $ttl;
  19. /**
  20. * Create a new HttpClient with caching features.
  21. *
  22. * @param Cache $cache
  23. * @param integer $ttl
  24. */
  25. public function __construct(Cache $cache, $ttl)
  26. {
  27. $this->cache = $cache;
  28. $this->ttl = $ttl;
  29. }
  30. /**
  31. *
  32. * @see \Moinax\TvDb\Http\HttpClient::fetch()
  33. */
  34. public function fetch($url, array $params = array(), $method = HttpClient::GET)
  35. {
  36. $ch = $this->curlInit($url, $params, $method);
  37. if($method == HttpClient::POST) {
  38. return $this->doPost($ch, $url);
  39. }
  40. return $this->doGet($ch, $url);
  41. }
  42. /**
  43. * Get the cache time to live.
  44. *
  45. * @return number
  46. */
  47. public function getTtl()
  48. {
  49. return $this->ttl;
  50. }
  51. /**
  52. * Set the cache time to live.
  53. *
  54. * @param integer $ttl
  55. */
  56. public function setTtl($ttl)
  57. {
  58. $this->ttl = $ttl;
  59. }
  60. /**
  61. * Do a GET request.
  62. *
  63. * @param resource $ch
  64. * @param string $url
  65. * @throws CurlException
  66. * @return string
  67. */
  68. private function doGet($ch, $url)
  69. {
  70. $now = time();
  71. $resource = $this->getResourceName($url);
  72. $date = $this->cache->getDate($resource);
  73. $limit = $now - $this->ttl;
  74. //Return content if ttl has not yet expired.
  75. if($date > 0 && $date > $limit) {
  76. return $this->cache->getContent($resource);
  77. }
  78. //Add the If-Modified-Since header
  79. if($date > 0) {
  80. curl_setopt($ch, CURLOPT_HTTPHEADER, array(
  81. sprintf("If-Modified-Since: %s GMT", gmdate("D, d M Y H:i:s", $date)),
  82. ));
  83. }
  84. curl_setopt($ch, CURLOPT_FILETIME, true);
  85. $data = $this->curlExec($ch);
  86. $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  87. $date = curl_getinfo($ch, CURLINFO_FILETIME);
  88. curl_close($ch);
  89. //Return content from cache.
  90. if($httpCode == 304) {
  91. $this->cache->setDate($resource, $date);
  92. return $this->cache->getContent($resource);
  93. }
  94. //Cache content
  95. if ($httpCode == 200) {
  96. $date = ($date >= 0) ? $date : $now;
  97. $this->cache->cache($resource, $date, $data);
  98. return $data;
  99. }
  100. throw new CurlException(sprintf('Cannot fetch %s', $url), $httpCode);
  101. }
  102. /**
  103. * Do a POST request.
  104. *
  105. * @param resource $ch
  106. * @param string $url
  107. * @throws CurlException
  108. * @return string
  109. */
  110. private function doPost($ch, $url)
  111. {
  112. $data = $this->curlExec($ch);
  113. $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  114. curl_close($ch);
  115. if ($httpCode != 200) {
  116. throw new CurlException(sprintf('Cannot fetch %s', $url), $httpCode);
  117. }
  118. return $data;
  119. }
  120. /**
  121. * Exec the given cURL connection and return the content body.
  122. * @param resource $ch
  123. * @return string
  124. */
  125. private function curlExec($ch)
  126. {
  127. $response = curl_exec($ch);
  128. $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
  129. return substr($response, $headerSize);
  130. }
  131. /**
  132. * Initialize a new cURL client.
  133. *
  134. * @param string $url
  135. * @param array $params
  136. * @param string $method
  137. *
  138. * @return resource
  139. */
  140. private function curlInit($url, array $params, $method)
  141. {
  142. $ch = curl_init($url);
  143. curl_setopt($ch, CURLOPT_HEADER, 1);
  144. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  145. if ($method == HttpClient::POST) {
  146. curl_setopt($ch, CURLOPT_POST, 1);
  147. curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
  148. }
  149. return $ch;
  150. }
  151. /**
  152. * Get the resource name from url removing the host part and the API key if provided.
  153. *
  154. * @param string $url
  155. * @return string
  156. */
  157. private function getResourceName($url)
  158. {
  159. $url = parse_url($url);
  160. $extension = pathinfo($url['path'], PATHINFO_EXTENSION);
  161. $resource = $url['path'];
  162. if(in_array($extension, array('xml', 'zip'))) {
  163. //Remove the api key from the resource name.
  164. $path = explode('/', $url['path']);
  165. unset($path[2]);
  166. $resource = implode('/', $path);
  167. }
  168. if (!empty($url['query'])) {
  169. $resource .= '?' . $url['query'];
  170. }
  171. return $resource;
  172. }
  173. }