RedisStore.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. <?php
  2. namespace Illuminate\Cache;
  3. use Illuminate\Contracts\Cache\LockProvider;
  4. use Illuminate\Contracts\Redis\Factory as Redis;
  5. use Illuminate\Redis\Connections\PhpRedisConnection;
  6. class RedisStore extends TaggableStore implements LockProvider
  7. {
  8. /**
  9. * The Redis factory implementation.
  10. *
  11. * @var \Illuminate\Contracts\Redis\Factory
  12. */
  13. protected $redis;
  14. /**
  15. * A string that should be prepended to keys.
  16. *
  17. * @var string
  18. */
  19. protected $prefix;
  20. /**
  21. * The Redis connection instance that should be used to manage locks.
  22. *
  23. * @var string
  24. */
  25. protected $connection;
  26. /**
  27. * The name of the connection that should be used for locks.
  28. *
  29. * @var string
  30. */
  31. protected $lockConnection;
  32. /**
  33. * Create a new Redis store.
  34. *
  35. * @param \Illuminate\Contracts\Redis\Factory $redis
  36. * @param string $prefix
  37. * @param string $connection
  38. * @return void
  39. */
  40. public function __construct(Redis $redis, $prefix = '', $connection = 'default')
  41. {
  42. $this->redis = $redis;
  43. $this->setPrefix($prefix);
  44. $this->setConnection($connection);
  45. }
  46. /**
  47. * Retrieve an item from the cache by key.
  48. *
  49. * @param string|array $key
  50. * @return mixed
  51. */
  52. public function get($key)
  53. {
  54. $value = $this->connection()->get($this->prefix.$key);
  55. return ! is_null($value) ? $this->unserialize($value) : null;
  56. }
  57. /**
  58. * Retrieve multiple items from the cache by key.
  59. *
  60. * Items not found in the cache will have a null value.
  61. *
  62. * @param array $keys
  63. * @return array
  64. */
  65. public function many(array $keys)
  66. {
  67. if (count($keys) === 0) {
  68. return [];
  69. }
  70. $results = [];
  71. $values = $this->connection()->mget(array_map(function ($key) {
  72. return $this->prefix.$key;
  73. }, $keys));
  74. foreach ($values as $index => $value) {
  75. $results[$keys[$index]] = ! is_null($value) ? $this->unserialize($value) : null;
  76. }
  77. return $results;
  78. }
  79. /**
  80. * Store an item in the cache for a given number of seconds.
  81. *
  82. * @param string $key
  83. * @param mixed $value
  84. * @param int $seconds
  85. * @return bool
  86. */
  87. public function put($key, $value, $seconds)
  88. {
  89. return (bool) $this->connection()->setex(
  90. $this->prefix.$key, (int) max(1, $seconds), $this->serialize($value)
  91. );
  92. }
  93. /**
  94. * Store multiple items in the cache for a given number of seconds.
  95. *
  96. * @param array $values
  97. * @param int $seconds
  98. * @return bool
  99. */
  100. public function putMany(array $values, $seconds)
  101. {
  102. $this->connection()->multi();
  103. $manyResult = null;
  104. foreach ($values as $key => $value) {
  105. $result = $this->put($key, $value, $seconds);
  106. $manyResult = is_null($manyResult) ? $result : $result && $manyResult;
  107. }
  108. $this->connection()->exec();
  109. return $manyResult ?: false;
  110. }
  111. /**
  112. * Store an item in the cache if the key doesn't exist.
  113. *
  114. * @param string $key
  115. * @param mixed $value
  116. * @param int $seconds
  117. * @return bool
  118. */
  119. public function add($key, $value, $seconds)
  120. {
  121. $lua = "return redis.call('exists',KEYS[1])<1 and redis.call('setex',KEYS[1],ARGV[2],ARGV[1])";
  122. return (bool) $this->connection()->eval(
  123. $lua, 1, $this->prefix.$key, $this->serialize($value), (int) max(1, $seconds)
  124. );
  125. }
  126. /**
  127. * Increment the value of an item in the cache.
  128. *
  129. * @param string $key
  130. * @param mixed $value
  131. * @return int
  132. */
  133. public function increment($key, $value = 1)
  134. {
  135. return $this->connection()->incrby($this->prefix.$key, $value);
  136. }
  137. /**
  138. * Decrement the value of an item in the cache.
  139. *
  140. * @param string $key
  141. * @param mixed $value
  142. * @return int
  143. */
  144. public function decrement($key, $value = 1)
  145. {
  146. return $this->connection()->decrby($this->prefix.$key, $value);
  147. }
  148. /**
  149. * Store an item in the cache indefinitely.
  150. *
  151. * @param string $key
  152. * @param mixed $value
  153. * @return bool
  154. */
  155. public function forever($key, $value)
  156. {
  157. return (bool) $this->connection()->set($this->prefix.$key, $this->serialize($value));
  158. }
  159. /**
  160. * Get a lock instance.
  161. *
  162. * @param string $name
  163. * @param int $seconds
  164. * @param string|null $owner
  165. * @return \Illuminate\Contracts\Cache\Lock
  166. */
  167. public function lock($name, $seconds = 0, $owner = null)
  168. {
  169. $lockName = $this->prefix.$name;
  170. $lockConnection = $this->lockConnection();
  171. if ($lockConnection instanceof PhpRedisConnection) {
  172. return new PhpRedisLock($lockConnection, $lockName, $seconds, $owner);
  173. }
  174. return new RedisLock($lockConnection, $lockName, $seconds, $owner);
  175. }
  176. /**
  177. * Restore a lock instance using the owner identifier.
  178. *
  179. * @param string $name
  180. * @param string $owner
  181. * @return \Illuminate\Contracts\Cache\Lock
  182. */
  183. public function restoreLock($name, $owner)
  184. {
  185. return $this->lock($name, 0, $owner);
  186. }
  187. /**
  188. * Remove an item from the cache.
  189. *
  190. * @param string $key
  191. * @return bool
  192. */
  193. public function forget($key)
  194. {
  195. return (bool) $this->connection()->del($this->prefix.$key);
  196. }
  197. /**
  198. * Remove all items from the cache.
  199. *
  200. * @return bool
  201. */
  202. public function flush()
  203. {
  204. $this->connection()->flushdb();
  205. return true;
  206. }
  207. /**
  208. * Begin executing a new tags operation.
  209. *
  210. * @param array|mixed $names
  211. * @return \Illuminate\Cache\RedisTaggedCache
  212. */
  213. public function tags($names)
  214. {
  215. return new RedisTaggedCache(
  216. $this, new TagSet($this, is_array($names) ? $names : func_get_args())
  217. );
  218. }
  219. /**
  220. * Get the Redis connection instance.
  221. *
  222. * @return \Illuminate\Redis\Connections\Connection
  223. */
  224. public function connection()
  225. {
  226. return $this->redis->connection($this->connection);
  227. }
  228. /**
  229. * Get the Redis connection instance that should be used to manage locks.
  230. *
  231. * @return \Illuminate\Redis\Connections\Connection
  232. */
  233. public function lockConnection()
  234. {
  235. return $this->redis->connection($this->lockConnection ?? $this->connection);
  236. }
  237. /**
  238. * Specify the name of the connection that should be used to store data.
  239. *
  240. * @param string $connection
  241. * @return void
  242. */
  243. public function setConnection($connection)
  244. {
  245. $this->connection = $connection;
  246. }
  247. /**
  248. * Specify the name of the connection that should be used to manage locks.
  249. *
  250. * @param string $connection
  251. * @return $this
  252. */
  253. public function setLockConnection($connection)
  254. {
  255. $this->lockConnection = $connection;
  256. return $this;
  257. }
  258. /**
  259. * Get the Redis database instance.
  260. *
  261. * @return \Illuminate\Contracts\Redis\Factory
  262. */
  263. public function getRedis()
  264. {
  265. return $this->redis;
  266. }
  267. /**
  268. * Get the cache key prefix.
  269. *
  270. * @return string
  271. */
  272. public function getPrefix()
  273. {
  274. return $this->prefix;
  275. }
  276. /**
  277. * Set the cache key prefix.
  278. *
  279. * @param string $prefix
  280. * @return void
  281. */
  282. public function setPrefix($prefix)
  283. {
  284. $this->prefix = ! empty($prefix) ? $prefix.':' : '';
  285. }
  286. /**
  287. * Serialize the value.
  288. *
  289. * @param mixed $value
  290. * @return mixed
  291. */
  292. protected function serialize($value)
  293. {
  294. return is_numeric($value) && ! in_array($value, [INF, -INF]) && ! is_nan($value) ? $value : serialize($value);
  295. }
  296. /**
  297. * Unserialize the value.
  298. *
  299. * @param mixed $value
  300. * @return mixed
  301. */
  302. protected function unserialize($value)
  303. {
  304. return is_numeric($value) ? $value : unserialize($value);
  305. }
  306. }