Sqlite.php 8.0 KB

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