PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/tedivm/stash/src/Stash/Driver/Sub/SqlitePdo.php

https://bitbucket.org/PHP__Pro/mediacraft
PHP | 290 lines | 147 code | 32 blank | 111 comment | 9 complexity | 0d3bb41c63cec1c18c2827c63d7fb077 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, AGPL-3.0, MPL-2.0-no-copyleft-exception
  1. <?php
  2. /*
  3. * This file is part of the Stash package.
  4. *
  5. * (c) Robert Hafner <tedivm@tedivm.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Stash\Driver\Sub;
  11. /**
  12. * Class SqlitePDO
  13. *
  14. * This SQLite subdriver uses PDO and the latest version of SQLite.
  15. *
  16. * @internal
  17. * @package Stash
  18. * @author Robert Hafner <tedivm@tedivm.com>
  19. */
  20. class SqlitePdo
  21. {
  22. /**
  23. * Directory where the SQLite databases are stored.
  24. *
  25. * @var string
  26. */
  27. protected $path;
  28. /**
  29. * Output of buildDriver, used to interact with the relevant SQLite extension.
  30. *
  31. * @var \PDO
  32. */
  33. protected $driver;
  34. /**
  35. * PDO driver string, used to distinguish between SQLite versions.
  36. *
  37. * @var string
  38. */
  39. protected static $pdoDriver = 'sqlite';
  40. /**
  41. * The SQLite query used to generate the database.
  42. *
  43. * @var string
  44. */
  45. protected $creationSql = 'CREATE TABLE cacheStore (
  46. key TEXT UNIQUE ON CONFLICT REPLACE,
  47. expiration INTEGER,
  48. encoding TEXT,
  49. data BLOB
  50. );
  51. CREATE INDEX keyIndex ON cacheStore (key);';
  52. /**
  53. * File permissions of new SQLite databases.
  54. *
  55. * @var string
  56. */
  57. protected $filePermissions;
  58. /**
  59. * File permissions of new directories leading to SQLite databases.
  60. *
  61. * @var string
  62. */
  63. protected $dirPermissions;
  64. /**
  65. * Amounts of time to wait for the SQLite engine before timing out.
  66. *
  67. * @var int milliseconds
  68. */
  69. protected $busyTimeout;
  70. /**
  71. * The appropriate response code to use when retrieving data.
  72. *
  73. * @var int
  74. */
  75. protected $responseCode;
  76. /**
  77. * @param string $path
  78. * @param string $directoryPermission
  79. * @param string $filePermission
  80. * @param int $busyTimeout
  81. */
  82. public function __construct($path, $directoryPermission, $filePermission, $busyTimeout)
  83. {
  84. $this->path = $path;
  85. $this->filePermissions = $filePermission;
  86. $this->dirPermissions = $directoryPermission;
  87. $this->busyTimeout = $busyTimeout;
  88. $this->responseCode = \PDO::FETCH_ASSOC;
  89. }
  90. /**
  91. * Clear out driver, closing file sockets.
  92. */
  93. public function __destruct()
  94. {
  95. $this->driver = null;
  96. }
  97. /**
  98. * Retrieves data from cache store.
  99. *
  100. * @param string $key
  101. * @return bool|mixed
  102. */
  103. public function get($key)
  104. {
  105. $driver = $this->getDriver();
  106. $query = $driver->query("SELECT * FROM cacheStore WHERE key LIKE '{$key}'");
  107. if ($query === false) {
  108. return false;
  109. }
  110. if ($resultArray = $query->fetch($this->responseCode)) {
  111. return unserialize(base64_decode($resultArray['data']));
  112. }
  113. return false;
  114. }
  115. /**
  116. * Stores data in sqlite database.
  117. *
  118. * @param string $key
  119. * @param mixed $value
  120. * @param int $expiration
  121. * @return bool
  122. */
  123. public function set($key, $value, $expiration)
  124. {
  125. $driver = $this->getDriver();
  126. $data = base64_encode(serialize($value));
  127. $contentLength = strlen($data);
  128. if ($contentLength > 100000) {
  129. $this->setTimeout($this->busyTimeout * (ceil($contentLength / 100000))); // .5s per 100k
  130. }
  131. $driver->query("INSERT INTO cacheStore (key, expiration, data)
  132. VALUES ('{$key}', '{$expiration}', '{$data}')");
  133. return true;
  134. }
  135. /**
  136. * Clears data from database. If a key is defined only it and it's children are removed. If everything is set to be
  137. * cleared then the database itself is deleted off disk.
  138. *
  139. * @param null|string $key
  140. * @return bool
  141. */
  142. public function clear($key = null)
  143. {
  144. // return true if the cache is already empty
  145. try {
  146. $driver = $this->getDriver();
  147. } catch (RuntimeException $e) {
  148. return true;
  149. }
  150. if (!isset($key)) {
  151. unset($driver);
  152. $this->driver = null;
  153. $this->driver = false;
  154. \Stash\Utilities::deleteRecursive($this->path);
  155. } else {
  156. $driver->query("DELETE FROM cacheStore WHERE key LIKE '{$key}%'");
  157. }
  158. return true;
  159. }
  160. /**
  161. * Old data is removed and the "vacuum" operation is run.
  162. *
  163. * @return bool
  164. */
  165. public function purge()
  166. {
  167. $driver = $this->getDriver();
  168. $driver->query('DELETE FROM cacheStore WHERE expiration < ' . time());
  169. $driver->query('VACUUM');
  170. return true;
  171. }
  172. /**
  173. * Checks that PDO extension is present and has the appropriate SQLite driver.
  174. *
  175. */
  176. public static function isAvailable()
  177. {
  178. if (!class_exists('\PDO', false)) {
  179. return false;
  180. }
  181. $drivers = \PDO::getAvailableDrivers();
  182. return in_array(static::$pdoDriver, $drivers);
  183. }
  184. /**
  185. * Tells the SQLite driver how long to wait for data to be written.
  186. *
  187. * @param int $milliseconds
  188. * @return bool
  189. */
  190. protected function setTimeout($milliseconds)
  191. {
  192. $driver = $this->getDriver();
  193. $timeout = ceil($milliseconds / 1000);
  194. $driver->setAttribute(\PDO::ATTR_TIMEOUT, $timeout);
  195. }
  196. /**
  197. * Retrieves the relevant SQLite driver, creating the database file if necessary.
  198. *
  199. * @return \SQLiteDatabase
  200. * @throws \Stash\Exception\RuntimeException
  201. */
  202. protected function getDriver()
  203. {
  204. if (isset($this->driver) && $this->driver !== false) {
  205. return $this->driver;
  206. }
  207. if (!file_exists($this->path)) {
  208. $dir = $this->path;
  209. // Since PHP will understand paths with mixed slashes- both the windows \ and unix / variants- we have
  210. // to test for both and see which one is the last in the string.
  211. $pos1 = strrpos($this->path, '/');
  212. $pos2 = strrpos($this->path, '\\');
  213. if ($pos1 || $pos2) {
  214. $pos = $pos1 >= $pos2 ? $pos1 : $pos2;
  215. $dir = substr($this->path, 0, $pos);
  216. }
  217. if (!is_dir($dir)) {
  218. mkdir($dir, $this->dirPermissions, true);
  219. }
  220. $runInstall = true;
  221. } else {
  222. $runInstall = false;
  223. }
  224. $db = $this->buildDriver();
  225. if ($runInstall && !$db->query($this->creationSql)) {
  226. unlink($this->path);
  227. throw new RuntimeException('Unable to set SQLite: structure');
  228. }
  229. if (!$db) {
  230. throw new RuntimeException('SQLite driver failed to load');
  231. }
  232. $this->driver = $db;
  233. // prevent the cache from getting hungup waiting on a return
  234. $this->setTimeout($this->busyTimeout);
  235. return $db;
  236. }
  237. /**
  238. * Creates the actual database driver itself.
  239. *
  240. * @return \SQLiteDatabase
  241. * @throws \Stash\Exception\RuntimeException
  242. */
  243. protected function buildDriver()
  244. {
  245. $db = new \PDO(static::$pdoDriver . ':' . $this->path);
  246. return $db;
  247. }
  248. }