Sqlite.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. <?php
  2. /**
  3. * KO7 Cache Sqlite Driver
  4. *
  5. * Requires SQLite3 and PDO
  6. *
  7. * @package KO7/Cache
  8. * @category Base
  9. * @author Kohana Team
  10. * @copyright (c) Kohana Team
  11. * @license https://koseven.ga/LICENSE.md
  12. */
  13. class KO7_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 KO7 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 KO7 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. // Disable notices for unserializing
  94. $ER = error_reporting(~E_NOTICE);
  95. // Return the valid cache data
  96. $data = unserialize($result->cache);
  97. // Turn notices back on
  98. error_reporting($ER);
  99. // Return the resulting data
  100. return $data;
  101. }
  102. }
  103. /**
  104. * Set a value based on an id. Optionally add tags.
  105. *
  106. * @param string $id id
  107. * @param mixed $data data
  108. * @param integer $lifetime lifetime [Optional]
  109. * @return boolean
  110. */
  111. public function set($id, $data, $lifetime = NULL)
  112. {
  113. return (bool) $this->set_with_tags($id, $data, $lifetime);
  114. }
  115. /**
  116. * Delete a cache entry based on id
  117. *
  118. * @param string $id id
  119. * @return boolean
  120. * @throws Cache_Exception
  121. */
  122. public function delete($id)
  123. {
  124. // Prepare statement
  125. $statement = $this->_db->prepare('DELETE FROM caches WHERE id = :id');
  126. // Remove the entry
  127. try
  128. {
  129. $statement->execute([':id' => $this->_sanitize_id($id)]);
  130. }
  131. catch (PDOException $e)
  132. {
  133. throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', [':error' => $e->getMessage()]);
  134. }
  135. return (bool) $statement->rowCount();
  136. }
  137. /**
  138. * Delete all cache entries
  139. *
  140. * @return boolean
  141. */
  142. public function delete_all()
  143. {
  144. // Prepare statement
  145. $statement = $this->_db->prepare('DELETE FROM caches');
  146. // Remove the entry
  147. try
  148. {
  149. $statement->execute();
  150. }
  151. catch (PDOException $e)
  152. {
  153. throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', [':error' => $e->getMessage()]);
  154. }
  155. return (bool) $statement->rowCount();
  156. }
  157. /**
  158. * Set a value based on an id. Optionally add tags.
  159. *
  160. * @param string $id id
  161. * @param mixed $data data
  162. * @param integer $lifetime lifetime [Optional]
  163. * @param array $tags tags [Optional]
  164. * @return boolean
  165. * @throws Cache_Exception
  166. */
  167. public function set_with_tags($id, $data, $lifetime = NULL, array $tags = NULL)
  168. {
  169. // Serialize the data
  170. $data = serialize($data);
  171. // Normalise tags
  172. $tags = (NULL === $tags) ? NULL : ('<'.implode('>,<', $tags).'>');
  173. // Setup lifetime
  174. if ($lifetime === NULL)
  175. {
  176. $lifetime = (0 === Arr::get($this->_config, 'default_expire', NULL)) ? 0 : (Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE) + time());
  177. }
  178. else
  179. {
  180. $lifetime = (0 === $lifetime) ? 0 : ($lifetime + time());
  181. }
  182. // Prepare statement
  183. // $this->exists() may throw Cache_Exception, no need to catch/rethrow
  184. $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)');
  185. // Try to insert
  186. try
  187. {
  188. $statement->execute([':id' => $this->_sanitize_id($id), ':cache' => $data, ':expiration' => $lifetime, ':tags' => $tags]);
  189. }
  190. catch (PDOException $e)
  191. {
  192. throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', [':error' => $e->getMessage()]);
  193. }
  194. return (bool) $statement->rowCount();
  195. }
  196. /**
  197. * Delete cache entries based on a tag
  198. *
  199. * @param string $tag tag
  200. * @return boolean
  201. * @throws Cache_Exception
  202. */
  203. public function delete_tag($tag)
  204. {
  205. // Prepare the statement
  206. $statement = $this->_db->prepare('DELETE FROM caches WHERE tags LIKE :tag');
  207. // Try to delete
  208. try
  209. {
  210. $statement->execute([':tag' => "%<{$tag}>%"]);
  211. }
  212. catch (PDOException $e)
  213. {
  214. throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', [':error' => $e->getMessage()]);
  215. }
  216. return (bool) $statement->rowCount();
  217. }
  218. /**
  219. * Find cache entries based on a tag
  220. *
  221. * @param string $tag tag
  222. * @return array
  223. * @throws Cache_Exception
  224. */
  225. public function find($tag)
  226. {
  227. // Prepare the statement
  228. $statement = $this->_db->prepare('SELECT id, cache FROM caches WHERE tags LIKE :tag');
  229. // Try to find
  230. try
  231. {
  232. if ( ! $statement->execute([':tag' => "%<{$tag}>%"]))
  233. {
  234. return [];
  235. }
  236. }
  237. catch (PDOException $e)
  238. {
  239. throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', [':error' => $e->getMessage()]);
  240. }
  241. $result = [];
  242. while ($row = $statement->fetchObject())
  243. {
  244. // Disable notices for unserializing
  245. $ER = error_reporting(~E_NOTICE);
  246. $result[$row->id] = unserialize($row->cache);
  247. // Turn notices back on
  248. error_reporting($ER);
  249. }
  250. return $result;
  251. }
  252. /**
  253. * Garbage collection method that cleans any expired
  254. * cache entries from the cache.
  255. *
  256. * @return void
  257. */
  258. public function garbage_collect()
  259. {
  260. // Create the sequel statement
  261. $statement = $this->_db->prepare('DELETE FROM caches WHERE expiration < :expiration');
  262. try
  263. {
  264. $statement->execute([':expiration' => time()]);
  265. }
  266. catch (PDOException $e)
  267. {
  268. throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', [':error' => $e->getMessage()]);
  269. }
  270. }
  271. /**
  272. * Tests whether an id exists or not
  273. *
  274. * @param string $id id
  275. * @return boolean
  276. * @throws Cache_Exception
  277. */
  278. protected function exists($id)
  279. {
  280. $statement = $this->_db->prepare('SELECT id FROM caches WHERE id = :id');
  281. try
  282. {
  283. $statement->execute([':id' => $this->_sanitize_id($id)]);
  284. }
  285. catch (PDOExeption $e)
  286. {
  287. throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', [':error' => $e->getMessage()]);
  288. }
  289. return (bool) $statement->fetchAll();
  290. }
  291. }