DatabaseStore.php 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. <?php
  2. namespace Illuminate\Cache;
  3. use Closure;
  4. use Exception;
  5. use Illuminate\Contracts\Cache\Store;
  6. use Illuminate\Support\InteractsWithTime;
  7. use Illuminate\Database\ConnectionInterface;
  8. class DatabaseStore implements Store
  9. {
  10. use InteractsWithTime, RetrievesMultipleKeys;
  11. /**
  12. * The database connection instance.
  13. *
  14. * @var \Illuminate\Database\ConnectionInterface
  15. */
  16. protected $connection;
  17. /**
  18. * The name of the cache table.
  19. *
  20. * @var string
  21. */
  22. protected $table;
  23. /**
  24. * A string that should be prepended to keys.
  25. *
  26. * @var string
  27. */
  28. protected $prefix;
  29. /**
  30. * Create a new database store.
  31. *
  32. * @param \Illuminate\Database\ConnectionInterface $connection
  33. * @param string $table
  34. * @param string $prefix
  35. * @return void
  36. */
  37. public function __construct(ConnectionInterface $connection, $table, $prefix = '')
  38. {
  39. $this->table = $table;
  40. $this->prefix = $prefix;
  41. $this->connection = $connection;
  42. }
  43. /**
  44. * Retrieve an item from the cache by key.
  45. *
  46. * @param string|array $key
  47. * @return mixed
  48. */
  49. public function get($key)
  50. {
  51. $prefixed = $this->prefix.$key;
  52. $cache = $this->table()->where('key', '=', $prefixed)->first();
  53. // If we have a cache record we will check the expiration time against current
  54. // time on the system and see if the record has expired. If it has, we will
  55. // remove the records from the database table so it isn't returned again.
  56. if (is_null($cache)) {
  57. return;
  58. }
  59. $cache = is_array($cache) ? (object) $cache : $cache;
  60. // If this cache expiration date is past the current time, we will remove this
  61. // item from the cache. Then we will return a null value since the cache is
  62. // expired. We will use "Carbon" to make this comparison with the column.
  63. if ($this->currentTime() >= $cache->expiration) {
  64. $this->forget($key);
  65. return;
  66. }
  67. return unserialize($cache->value);
  68. }
  69. /**
  70. * Store an item in the cache for a given number of minutes.
  71. *
  72. * @param string $key
  73. * @param mixed $value
  74. * @param float|int $minutes
  75. * @return void
  76. */
  77. public function put($key, $value, $minutes)
  78. {
  79. $key = $this->prefix.$key;
  80. $value = serialize($value);
  81. $expiration = $this->getTime() + (int) ($minutes * 60);
  82. try {
  83. $this->table()->insert(compact('key', 'value', 'expiration'));
  84. } catch (Exception $e) {
  85. $this->table()->where('key', $key)->update(compact('value', 'expiration'));
  86. }
  87. }
  88. /**
  89. * Increment the value of an item in the cache.
  90. *
  91. * @param string $key
  92. * @param mixed $value
  93. * @return int|bool
  94. */
  95. public function increment($key, $value = 1)
  96. {
  97. return $this->incrementOrDecrement($key, $value, function ($current, $value) {
  98. return $current + $value;
  99. });
  100. }
  101. /**
  102. * Decrement the value of an item in the cache.
  103. *
  104. * @param string $key
  105. * @param mixed $value
  106. * @return int|bool
  107. */
  108. public function decrement($key, $value = 1)
  109. {
  110. return $this->incrementOrDecrement($key, $value, function ($current, $value) {
  111. return $current - $value;
  112. });
  113. }
  114. /**
  115. * Increment or decrement an item in the cache.
  116. *
  117. * @param string $key
  118. * @param mixed $value
  119. * @param \Closure $callback
  120. * @return int|bool
  121. */
  122. protected function incrementOrDecrement($key, $value, Closure $callback)
  123. {
  124. return $this->connection->transaction(function () use ($key, $value, $callback) {
  125. $prefixed = $this->prefix.$key;
  126. $cache = $this->table()->where('key', $prefixed)
  127. ->lockForUpdate()->first();
  128. // If there is no value in the cache, we will return false here. Otherwise the
  129. // value will be decrypted and we will proceed with this function to either
  130. // increment or decrement this value based on the given action callbacks.
  131. if (is_null($cache)) {
  132. return false;
  133. }
  134. $cache = is_array($cache) ? (object) $cache : $cache;
  135. $current = unserialize($cache->value);
  136. // Here we'll call this callback function that was given to the function which
  137. // is used to either increment or decrement the function. We use a callback
  138. // so we do not have to recreate all this logic in each of the functions.
  139. $new = $callback((int) $current, $value);
  140. if (! is_numeric($current)) {
  141. return false;
  142. }
  143. // Here we will update the values in the table. We will also encrypt the value
  144. // since database cache values are encrypted by default with secure storage
  145. // that can't be easily read. We will return the new value after storing.
  146. $this->table()->where('key', $prefixed)->update([
  147. 'value' => serialize($new),
  148. ]);
  149. return $new;
  150. });
  151. }
  152. /**
  153. * Get the current system time.
  154. *
  155. * @return int
  156. */
  157. protected function getTime()
  158. {
  159. return $this->currentTime();
  160. }
  161. /**
  162. * Store an item in the cache indefinitely.
  163. *
  164. * @param string $key
  165. * @param mixed $value
  166. * @return void
  167. */
  168. public function forever($key, $value)
  169. {
  170. $this->put($key, $value, 5256000);
  171. }
  172. /**
  173. * Remove an item from the cache.
  174. *
  175. * @param string $key
  176. * @return bool
  177. */
  178. public function forget($key)
  179. {
  180. $this->table()->where('key', '=', $this->prefix.$key)->delete();
  181. return true;
  182. }
  183. /**
  184. * Remove all items from the cache.
  185. *
  186. * @return bool
  187. */
  188. public function flush()
  189. {
  190. return (bool) $this->table()->delete();
  191. }
  192. /**
  193. * Get a query builder for the cache table.
  194. *
  195. * @return \Illuminate\Database\Query\Builder
  196. */
  197. protected function table()
  198. {
  199. return $this->connection->table($this->table);
  200. }
  201. /**
  202. * Get the underlying database connection.
  203. *
  204. * @return \Illuminate\Database\ConnectionInterface
  205. */
  206. public function getConnection()
  207. {
  208. return $this->connection;
  209. }
  210. /**
  211. * Get the cache key prefix.
  212. *
  213. * @return string
  214. */
  215. public function getPrefix()
  216. {
  217. return $this->prefix;
  218. }
  219. }