SQLite3Cache.php 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. <?php
  2. /*
  3. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  4. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  5. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  6. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  7. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  8. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  9. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14. *
  15. * This software consists of voluntary contributions made by many individuals
  16. * and is licensed under the MIT license. For more information, see
  17. * <http://www.doctrine-project.org>.
  18. */
  19. namespace Doctrine\Common\Cache;
  20. use SQLite3;
  21. use SQLite3Result;
  22. /**
  23. * SQLite3 cache provider.
  24. *
  25. * @since 1.4
  26. * @author Jake Bell <jake@theunraveler.com>
  27. */
  28. class SQLite3Cache extends CacheProvider
  29. {
  30. /**
  31. * The ID field will store the cache key.
  32. */
  33. const ID_FIELD = 'k';
  34. /**
  35. * The data field will store the serialized PHP value.
  36. */
  37. const DATA_FIELD = 'd';
  38. /**
  39. * The expiration field will store a date value indicating when the
  40. * cache entry should expire.
  41. */
  42. const EXPIRATION_FIELD = 'e';
  43. /**
  44. * @var SQLite3
  45. */
  46. private $sqlite;
  47. /**
  48. * @var string
  49. */
  50. private $table;
  51. /**
  52. * Constructor.
  53. *
  54. * Calling the constructor will ensure that the database file and table
  55. * exist and will create both if they don't.
  56. *
  57. * @param SQLite3 $sqlite
  58. * @param string $table
  59. */
  60. public function __construct(SQLite3 $sqlite, $table)
  61. {
  62. $this->sqlite = $sqlite;
  63. $this->table = (string) $table;
  64. list($id, $data, $exp) = $this->getFields();
  65. return $this->sqlite->exec(sprintf(
  66. 'CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY NOT NULL, %s BLOB, %s INTEGER)',
  67. $table,
  68. $id,
  69. $data,
  70. $exp
  71. ));
  72. }
  73. /**
  74. * {@inheritdoc}
  75. */
  76. protected function doFetch($id)
  77. {
  78. if ($item = $this->findById($id)) {
  79. return unserialize($item[self::DATA_FIELD]);
  80. }
  81. return false;
  82. }
  83. /**
  84. * {@inheritdoc}
  85. */
  86. protected function doContains($id)
  87. {
  88. return null !== $this->findById($id, false);
  89. }
  90. /**
  91. * {@inheritdoc}
  92. */
  93. protected function doSave($id, $data, $lifeTime = 0)
  94. {
  95. $statement = $this->sqlite->prepare(sprintf(
  96. 'INSERT OR REPLACE INTO %s (%s) VALUES (:id, :data, :expire)',
  97. $this->table,
  98. implode(',', $this->getFields())
  99. ));
  100. $statement->bindValue(':id', $id);
  101. $statement->bindValue(':data', serialize($data), SQLITE3_BLOB);
  102. $statement->bindValue(':expire', $lifeTime > 0 ? time() + $lifeTime : null);
  103. return $statement->execute() instanceof SQLite3Result;
  104. }
  105. /**
  106. * {@inheritdoc}
  107. */
  108. protected function doDelete($id)
  109. {
  110. list($idField) = $this->getFields();
  111. $statement = $this->sqlite->prepare(sprintf(
  112. 'DELETE FROM %s WHERE %s = :id',
  113. $this->table,
  114. $idField
  115. ));
  116. $statement->bindValue(':id', $id);
  117. return $statement->execute() instanceof SQLite3Result;
  118. }
  119. /**
  120. * {@inheritdoc}
  121. */
  122. protected function doFlush()
  123. {
  124. return $this->sqlite->exec(sprintf('DELETE FROM %s', $this->table));
  125. }
  126. /**
  127. * {@inheritdoc}
  128. */
  129. protected function doGetStats()
  130. {
  131. // no-op.
  132. }
  133. /**
  134. * Find a single row by ID.
  135. *
  136. * @param mixed $id
  137. * @param bool $includeData
  138. *
  139. * @return array|null
  140. */
  141. private function findById($id, $includeData = true)
  142. {
  143. list($idField) = $fields = $this->getFields();
  144. if (!$includeData) {
  145. $key = array_search(static::DATA_FIELD, $fields);
  146. unset($fields[$key]);
  147. }
  148. $statement = $this->sqlite->prepare(sprintf(
  149. 'SELECT %s FROM %s WHERE %s = :id LIMIT 1',
  150. implode(',', $fields),
  151. $this->table,
  152. $idField
  153. ));
  154. $statement->bindValue(':id', $id, SQLITE3_TEXT);
  155. $item = $statement->execute()->fetchArray(SQLITE3_ASSOC);
  156. if ($item === false) {
  157. return null;
  158. }
  159. if ($this->isExpired($item)) {
  160. $this->doDelete($id);
  161. return null;
  162. }
  163. return $item;
  164. }
  165. /**
  166. * Gets an array of the fields in our table.
  167. *
  168. * @return array
  169. */
  170. private function getFields()
  171. {
  172. return array(static::ID_FIELD, static::DATA_FIELD, static::EXPIRATION_FIELD);
  173. }
  174. /**
  175. * Check if the item is expired.
  176. *
  177. * @param array $item
  178. *
  179. * @return bool
  180. */
  181. private function isExpired(array $item)
  182. {
  183. return isset($item[static::EXPIRATION_FIELD]) &&
  184. $item[self::EXPIRATION_FIELD] !== null &&
  185. $item[self::EXPIRATION_FIELD] < time();
  186. }
  187. }