Sqlite.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. <?php
  2. /**
  3. * Kohana Cache Sqlite Driver
  4. *
  5. * Requires SQLite3 and PDO
  6. *
  7. * @package Kohana/Cache
  8. * @category Base
  9. * @author Kohana Team
  10. * @copyright (c) Kohana Team
  11. * @license https://koseven.ga/LICENSE.md
  12. */
  13. class Kohana_Cache_Sqlite extends Cache implements Cache_Tagging, Cache_GarbageCollect {
  14. /**
  15. * Database resource
  16. *
  17. * @var PDO
  18. */
  19. protected $_db;
  20. /**
  21. * Sets up the PDO SQLite table and
  22. * initialises the PDO connection
  23. *
  24. * @param array $config configuration
  25. * @throws Cache_Exception
  26. */
  27. protected function __construct(array $config)
  28. {
  29. parent::__construct($config);
  30. $database = Arr::get($this->_config, 'database', NULL);
  31. if ($database === NULL)
  32. {
  33. throw new Cache_Exception('Database path not available in Kohana Cache configuration');
  34. }
  35. // Load new Sqlite DB
  36. $this->_db = new PDO('sqlite:'.$database);
  37. // Test for existing DB
  38. $result = $this->_db->query("SELECT * FROM sqlite_master WHERE name = 'caches' AND type = 'table'")->fetchAll();
  39. // If there is no table, create a new one
  40. if (0 == count($result))
  41. {
  42. $database_schema = Arr::get($this->_config, 'schema', NULL);
  43. if ($database_schema === NULL)
  44. {
  45. throw new Cache_Exception('Database schema not found in Kohana Cache configuration');
  46. }
  47. try
  48. {
  49. // Create the caches table
  50. $this->_db->query(Arr::get($this->_config, 'schema', NULL));
  51. }
  52. catch (PDOException $e)
  53. {
  54. throw new Cache_Exception('Failed to create new SQLite caches table with the following error : :error', [':error' => $e->getMessage()]);
  55. }
  56. }
  57. }
  58. /**
  59. * Retrieve a value based on an id
  60. *
  61. * @param string $id id
  62. * @param string $default default [Optional] Default value to return if id not found
  63. * @return mixed
  64. * @throws Cache_Exception
  65. */
  66. public function get($id, $default = NULL)
  67. {
  68. // Prepare statement
  69. $statement = $this->_db->prepare('SELECT id, expiration, cache FROM caches WHERE id = :id LIMIT 0, 1');
  70. // Try and load the cache based on id
  71. try
  72. {
  73. $statement->execute([':id' => $this->_sanitize_id($id)]);
  74. }
  75. catch (PDOException $e)
  76. {
  77. throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', [':error' => $e->getMessage()]);
  78. }
  79. if ( ! $result = $statement->fetch(PDO::FETCH_OBJ))
  80. {
  81. return $default;
  82. }
  83. // If the cache has expired
  84. if ($result->expiration != 0 and $result->expiration <= time())
  85. {
  86. // Delete it and return default value
  87. $this->delete($id);
  88. return $default;
  89. }
  90. // Otherwise return cached object
  91. else
  92. {
  93. // Return the valid cache data
  94. $data = @unserialize($result->cache);
  95. // Return the resulting data
  96. return $data;
  97. }
  98. }
  99. /**
  100. * Set a value based on an id. Optionally add tags.
  101. *
  102. * @param string $id id
  103. * @param mixed $data data
  104. * @param integer $lifetime lifetime [Optional]
  105. * @return boolean
  106. */
  107. public function set($id, $data, $lifetime = NULL)
  108. {
  109. return (bool) $this->set_with_tags($id, $data, $lifetime);
  110. }
  111. /**
  112. * Delete a cache entry based on id
  113. *
  114. * @param string $id id
  115. * @return boolean
  116. * @throws Cache_Exception
  117. */
  118. public function delete($id)
  119. {
  120. // Prepare statement
  121. $statement = $this->_db->prepare('DELETE FROM caches WHERE id = :id');
  122. // Remove the entry
  123. try
  124. {
  125. $statement->execute([':id' => $this->_sanitize_id($id)]);
  126. }
  127. catch (PDOException $e)
  128. {
  129. throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', [':error' => $e->getMessage()]);
  130. }
  131. return (bool) $statement->rowCount();
  132. }
  133. /**
  134. * Delete all cache entries
  135. *
  136. * @return boolean
  137. */
  138. public function delete_all()
  139. {
  140. // Prepare statement
  141. $statement = $this->_db->prepare('DELETE FROM caches');
  142. // Remove the entry
  143. try
  144. {
  145. $statement->execute();
  146. }
  147. catch (PDOException $e)
  148. {
  149. throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', [':error' => $e->getMessage()]);
  150. }
  151. return (bool) $statement->rowCount();
  152. }
  153. /**
  154. * Set a value based on an id. Optionally add tags.
  155. *
  156. * @param string $id id
  157. * @param mixed $data data
  158. * @param integer $lifetime lifetime [Optional]
  159. * @param array $tags tags [Optional]
  160. * @return boolean
  161. * @throws Cache_Exception
  162. */
  163. public function set_with_tags($id, $data, $lifetime = NULL, array $tags = NULL)
  164. {
  165. // Serialize the data
  166. $data = serialize($data);
  167. // Normalise tags
  168. $tags = (NULL === $tags) ? NULL : ('<'.implode('>,<', $tags).'>');
  169. // Setup lifetime
  170. if ($lifetime === NULL)
  171. {
  172. $lifetime = (0 === Arr::get($this->_config, 'default_expire', NULL)) ? 0 : (Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE) + time());
  173. }
  174. else
  175. {
  176. $lifetime = (0 === $lifetime) ? 0 : ($lifetime + time());
  177. }
  178. // Prepare statement
  179. // $this->exists() may throw Cache_Exception, no need to catch/rethrow
  180. $statement = $this->exists($id) ? $this->_db->prepare('UPDATE caches SET expiration = :expiration, cache = :cache, tags = :tags WHERE id = :id') : $this->_db->prepare('INSERT INTO caches (id, cache, expiration, tags) VALUES (:id, :cache, :expiration, :tags)');
  181. // Try to insert
  182. try
  183. {
  184. $statement->execute([':id' => $this->_sanitize_id($id), ':cache' => $data, ':expiration' => $lifetime, ':tags' => $tags]);
  185. }
  186. catch (PDOException $e)
  187. {
  188. throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', [':error' => $e->getMessage()]);
  189. }
  190. return (bool) $statement->rowCount();
  191. }
  192. /**
  193. * Delete cache entries based on a tag
  194. *
  195. * @param string $tag tag
  196. * @return boolean
  197. * @throws Cache_Exception
  198. */
  199. public function delete_tag($tag)
  200. {
  201. // Prepare the statement
  202. $statement = $this->_db->prepare('DELETE FROM caches WHERE tags LIKE :tag');
  203. // Try to delete
  204. try
  205. {
  206. $statement->execute([':tag' => "%<{$tag}>%"]);
  207. }
  208. catch (PDOException $e)
  209. {
  210. throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', [':error' => $e->getMessage()]);
  211. }
  212. return (bool) $statement->rowCount();
  213. }
  214. /**
  215. * Find cache entries based on a tag
  216. *
  217. * @param string $tag tag
  218. * @return array
  219. * @throws Cache_Exception
  220. */
  221. public function find($tag)
  222. {
  223. // Prepare the statement
  224. $statement = $this->_db->prepare('SELECT id, cache FROM caches WHERE tags LIKE :tag');
  225. // Try to find
  226. try
  227. {
  228. if ( ! $statement->execute([':tag' => "%<{$tag}>%"]))
  229. {
  230. return [];
  231. }
  232. }
  233. catch (PDOException $e)
  234. {
  235. throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', [':error' => $e->getMessage()]);
  236. }
  237. $result = [];
  238. while ($row = $statement->fetchObject())
  239. {
  240. $result[$row->id] = @unserialize($row->cache);
  241. }
  242. return $result;
  243. }
  244. /**
  245. * Garbage collection method that cleans any expired
  246. * cache entries from the cache.
  247. *
  248. * @return void
  249. */
  250. public function garbage_collect()
  251. {
  252. // Create the sequel statement
  253. $statement = $this->_db->prepare('DELETE FROM caches WHERE expiration < :expiration');
  254. try
  255. {
  256. $statement->execute([':expiration' => time()]);
  257. }
  258. catch (PDOException $e)
  259. {
  260. throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', [':error' => $e->getMessage()]);
  261. }
  262. }
  263. /**
  264. * Tests whether an id exists or not
  265. *
  266. * @param string $id id
  267. * @return boolean
  268. * @throws Cache_Exception
  269. */
  270. protected function exists($id)
  271. {
  272. $statement = $this->_db->prepare('SELECT id FROM caches WHERE id = :id');
  273. try
  274. {
  275. $statement->execute([':id' => $this->_sanitize_id($id)]);
  276. }
  277. catch (PDOExeption $e)
  278. {
  279. throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', [':error' => $e->getMessage()]);
  280. }
  281. return (bool) $statement->fetchAll();
  282. }
  283. }