Repository.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. <?php
  2. namespace Illuminate\Cache;
  3. use ArrayAccess;
  4. use BadMethodCallException;
  5. use Closure;
  6. use DateTimeInterface;
  7. use Illuminate\Cache\Events\CacheHit;
  8. use Illuminate\Cache\Events\CacheMissed;
  9. use Illuminate\Cache\Events\KeyForgotten;
  10. use Illuminate\Cache\Events\KeyWritten;
  11. use Illuminate\Contracts\Cache\Repository as CacheContract;
  12. use Illuminate\Contracts\Cache\Store;
  13. use Illuminate\Contracts\Events\Dispatcher;
  14. use Illuminate\Support\Carbon;
  15. use Illuminate\Support\InteractsWithTime;
  16. use Illuminate\Support\Traits\Macroable;
  17. /**
  18. * @mixin \Illuminate\Contracts\Cache\Store
  19. */
  20. class Repository implements ArrayAccess, CacheContract
  21. {
  22. use InteractsWithTime, Macroable {
  23. __call as macroCall;
  24. }
  25. /**
  26. * The cache store implementation.
  27. *
  28. * @var \Illuminate\Contracts\Cache\Store
  29. */
  30. protected $store;
  31. /**
  32. * The event dispatcher implementation.
  33. *
  34. * @var \Illuminate\Contracts\Events\Dispatcher
  35. */
  36. protected $events;
  37. /**
  38. * The default number of seconds to store items.
  39. *
  40. * @var int|null
  41. */
  42. protected $default = 3600;
  43. /**
  44. * Create a new cache repository instance.
  45. *
  46. * @param \Illuminate\Contracts\Cache\Store $store
  47. * @return void
  48. */
  49. public function __construct(Store $store)
  50. {
  51. $this->store = $store;
  52. }
  53. /**
  54. * Determine if an item exists in the cache.
  55. *
  56. * @param array|string $key
  57. * @return bool
  58. */
  59. public function has($key): bool
  60. {
  61. return ! is_null($this->get($key));
  62. }
  63. /**
  64. * Determine if an item doesn't exist in the cache.
  65. *
  66. * @param string $key
  67. * @return bool
  68. */
  69. public function missing($key)
  70. {
  71. return ! $this->has($key);
  72. }
  73. /**
  74. * Retrieve an item from the cache by key.
  75. *
  76. * @template TCacheValue
  77. *
  78. * @param array|string $key
  79. * @param TCacheValue|(\Closure(): TCacheValue) $default
  80. * @return (TCacheValue is null ? mixed : TCacheValue)
  81. */
  82. public function get($key, $default = null): mixed
  83. {
  84. if (is_array($key)) {
  85. return $this->many($key);
  86. }
  87. $value = $this->store->get($this->itemKey($key));
  88. // If we could not find the cache value, we will fire the missed event and get
  89. // the default value for this cache value. This default could be a callback
  90. // so we will execute the value function which will resolve it if needed.
  91. if (is_null($value)) {
  92. $this->event(new CacheMissed($key));
  93. $value = value($default);
  94. } else {
  95. $this->event(new CacheHit($key, $value));
  96. }
  97. return $value;
  98. }
  99. /**
  100. * Retrieve multiple items from the cache by key.
  101. *
  102. * Items not found in the cache will have a null value.
  103. *
  104. * @param array $keys
  105. * @return array
  106. */
  107. public function many(array $keys)
  108. {
  109. $values = $this->store->many(collect($keys)->map(function ($value, $key) {
  110. return is_string($key) ? $key : $value;
  111. })->values()->all());
  112. return collect($values)->map(function ($value, $key) use ($keys) {
  113. return $this->handleManyResult($keys, $key, $value);
  114. })->all();
  115. }
  116. /**
  117. * {@inheritdoc}
  118. *
  119. * @return iterable
  120. */
  121. public function getMultiple($keys, $default = null): iterable
  122. {
  123. $defaults = [];
  124. foreach ($keys as $key) {
  125. $defaults[$key] = $default;
  126. }
  127. return $this->many($defaults);
  128. }
  129. /**
  130. * Handle a result for the "many" method.
  131. *
  132. * @param array $keys
  133. * @param string $key
  134. * @param mixed $value
  135. * @return mixed
  136. */
  137. protected function handleManyResult($keys, $key, $value)
  138. {
  139. // If we could not find the cache value, we will fire the missed event and get
  140. // the default value for this cache value. This default could be a callback
  141. // so we will execute the value function which will resolve it if needed.
  142. if (is_null($value)) {
  143. $this->event(new CacheMissed($key));
  144. return (isset($keys[$key]) && ! array_is_list($keys)) ? value($keys[$key]) : null;
  145. }
  146. // If we found a valid value we will fire the "hit" event and return the value
  147. // back from this function. The "hit" event gives developers an opportunity
  148. // to listen for every possible cache "hit" throughout this applications.
  149. $this->event(new CacheHit($key, $value));
  150. return $value;
  151. }
  152. /**
  153. * Retrieve an item from the cache and delete it.
  154. *
  155. * @template TCacheValue
  156. *
  157. * @param array|string $key
  158. * @param TCacheValue|(\Closure(): TCacheValue) $default
  159. * @return (TCacheValue is null ? mixed : TCacheValue)
  160. */
  161. public function pull($key, $default = null)
  162. {
  163. return tap($this->get($key, $default), function () use ($key) {
  164. $this->forget($key);
  165. });
  166. }
  167. /**
  168. * Store an item in the cache.
  169. *
  170. * @param array|string $key
  171. * @param mixed $value
  172. * @param \DateTimeInterface|\DateInterval|int|null $ttl
  173. * @return bool
  174. */
  175. public function put($key, $value, $ttl = null)
  176. {
  177. if (is_array($key)) {
  178. return $this->putMany($key, $value);
  179. }
  180. if ($ttl === null) {
  181. return $this->forever($key, $value);
  182. }
  183. $seconds = $this->getSeconds($ttl);
  184. if ($seconds <= 0) {
  185. return $this->forget($key);
  186. }
  187. $result = $this->store->put($this->itemKey($key), $value, $seconds);
  188. if ($result) {
  189. $this->event(new KeyWritten($key, $value, $seconds));
  190. }
  191. return $result;
  192. }
  193. /**
  194. * {@inheritdoc}
  195. *
  196. * @return bool
  197. */
  198. public function set($key, $value, $ttl = null): bool
  199. {
  200. return $this->put($key, $value, $ttl);
  201. }
  202. /**
  203. * Store multiple items in the cache for a given number of seconds.
  204. *
  205. * @param array $values
  206. * @param \DateTimeInterface|\DateInterval|int|null $ttl
  207. * @return bool
  208. */
  209. public function putMany(array $values, $ttl = null)
  210. {
  211. if ($ttl === null) {
  212. return $this->putManyForever($values);
  213. }
  214. $seconds = $this->getSeconds($ttl);
  215. if ($seconds <= 0) {
  216. return $this->deleteMultiple(array_keys($values));
  217. }
  218. $result = $this->store->putMany($values, $seconds);
  219. if ($result) {
  220. foreach ($values as $key => $value) {
  221. $this->event(new KeyWritten($key, $value, $seconds));
  222. }
  223. }
  224. return $result;
  225. }
  226. /**
  227. * Store multiple items in the cache indefinitely.
  228. *
  229. * @param array $values
  230. * @return bool
  231. */
  232. protected function putManyForever(array $values)
  233. {
  234. $result = true;
  235. foreach ($values as $key => $value) {
  236. if (! $this->forever($key, $value)) {
  237. $result = false;
  238. }
  239. }
  240. return $result;
  241. }
  242. /**
  243. * {@inheritdoc}
  244. *
  245. * @return bool
  246. */
  247. public function setMultiple($values, $ttl = null): bool
  248. {
  249. return $this->putMany(is_array($values) ? $values : iterator_to_array($values), $ttl);
  250. }
  251. /**
  252. * Store an item in the cache if the key does not exist.
  253. *
  254. * @param string $key
  255. * @param mixed $value
  256. * @param \DateTimeInterface|\DateInterval|int|null $ttl
  257. * @return bool
  258. */
  259. public function add($key, $value, $ttl = null)
  260. {
  261. $seconds = null;
  262. if ($ttl !== null) {
  263. $seconds = $this->getSeconds($ttl);
  264. if ($seconds <= 0) {
  265. return false;
  266. }
  267. // If the store has an "add" method we will call the method on the store so it
  268. // has a chance to override this logic. Some drivers better support the way
  269. // this operation should work with a total "atomic" implementation of it.
  270. if (method_exists($this->store, 'add')) {
  271. return $this->store->add(
  272. $this->itemKey($key), $value, $seconds
  273. );
  274. }
  275. }
  276. // If the value did not exist in the cache, we will put the value in the cache
  277. // so it exists for subsequent requests. Then, we will return true so it is
  278. // easy to know if the value gets added. Otherwise, we will return false.
  279. if (is_null($this->get($key))) {
  280. return $this->put($key, $value, $seconds);
  281. }
  282. return false;
  283. }
  284. /**
  285. * Increment the value of an item in the cache.
  286. *
  287. * @param string $key
  288. * @param mixed $value
  289. * @return int|bool
  290. */
  291. public function increment($key, $value = 1)
  292. {
  293. return $this->store->increment($key, $value);
  294. }
  295. /**
  296. * Decrement the value of an item in the cache.
  297. *
  298. * @param string $key
  299. * @param mixed $value
  300. * @return int|bool
  301. */
  302. public function decrement($key, $value = 1)
  303. {
  304. return $this->store->decrement($key, $value);
  305. }
  306. /**
  307. * Store an item in the cache indefinitely.
  308. *
  309. * @param string $key
  310. * @param mixed $value
  311. * @return bool
  312. */
  313. public function forever($key, $value)
  314. {
  315. $result = $this->store->forever($this->itemKey($key), $value);
  316. if ($result) {
  317. $this->event(new KeyWritten($key, $value));
  318. }
  319. return $result;
  320. }
  321. /**
  322. * Get an item from the cache, or execute the given Closure and store the result.
  323. *
  324. * @template TCacheValue
  325. *
  326. * @param string $key
  327. * @param \Closure|\DateTimeInterface|\DateInterval|int|null $ttl
  328. * @param \Closure(): TCacheValue $callback
  329. * @return TCacheValue
  330. */
  331. public function remember($key, $ttl, Closure $callback)
  332. {
  333. $value = $this->get($key);
  334. // If the item exists in the cache we will just return this immediately and if
  335. // not we will execute the given Closure and cache the result of that for a
  336. // given number of seconds so it's available for all subsequent requests.
  337. if (! is_null($value)) {
  338. return $value;
  339. }
  340. $value = $callback();
  341. $this->put($key, $value, value($ttl, $value));
  342. return $value;
  343. }
  344. /**
  345. * Get an item from the cache, or execute the given Closure and store the result forever.
  346. *
  347. * @template TCacheValue
  348. *
  349. * @param string $key
  350. * @param \Closure(): TCacheValue $callback
  351. * @return TCacheValue
  352. */
  353. public function sear($key, Closure $callback)
  354. {
  355. return $this->rememberForever($key, $callback);
  356. }
  357. /**
  358. * Get an item from the cache, or execute the given Closure and store the result forever.
  359. *
  360. * @template TCacheValue
  361. *
  362. * @param string $key
  363. * @param \Closure(): TCacheValue $callback
  364. * @return TCacheValue
  365. */
  366. public function rememberForever($key, Closure $callback)
  367. {
  368. $value = $this->get($key);
  369. // If the item exists in the cache we will just return this immediately
  370. // and if not we will execute the given Closure and cache the result
  371. // of that forever so it is available for all subsequent requests.
  372. if (! is_null($value)) {
  373. return $value;
  374. }
  375. $this->forever($key, $value = $callback());
  376. return $value;
  377. }
  378. /**
  379. * Remove an item from the cache.
  380. *
  381. * @param string $key
  382. * @return bool
  383. */
  384. public function forget($key)
  385. {
  386. return tap($this->store->forget($this->itemKey($key)), function ($result) use ($key) {
  387. if ($result) {
  388. $this->event(new KeyForgotten($key));
  389. }
  390. });
  391. }
  392. /**
  393. * {@inheritdoc}
  394. *
  395. * @return bool
  396. */
  397. public function delete($key): bool
  398. {
  399. return $this->forget($key);
  400. }
  401. /**
  402. * {@inheritdoc}
  403. *
  404. * @return bool
  405. */
  406. public function deleteMultiple($keys): bool
  407. {
  408. $result = true;
  409. foreach ($keys as $key) {
  410. if (! $this->forget($key)) {
  411. $result = false;
  412. }
  413. }
  414. return $result;
  415. }
  416. /**
  417. * {@inheritdoc}
  418. *
  419. * @return bool
  420. */
  421. public function clear(): bool
  422. {
  423. return $this->store->flush();
  424. }
  425. /**
  426. * Begin executing a new tags operation if the store supports it.
  427. *
  428. * @param array|mixed $names
  429. * @return \Illuminate\Cache\TaggedCache
  430. *
  431. * @throws \BadMethodCallException
  432. */
  433. public function tags($names)
  434. {
  435. if (! $this->supportsTags()) {
  436. throw new BadMethodCallException('This cache store does not support tagging.');
  437. }
  438. $cache = $this->store->tags(is_array($names) ? $names : func_get_args());
  439. if (! is_null($this->events)) {
  440. $cache->setEventDispatcher($this->events);
  441. }
  442. return $cache->setDefaultCacheTime($this->default);
  443. }
  444. /**
  445. * Format the key for a cache item.
  446. *
  447. * @param string $key
  448. * @return string
  449. */
  450. protected function itemKey($key)
  451. {
  452. return $key;
  453. }
  454. /**
  455. * Calculate the number of seconds for the given TTL.
  456. *
  457. * @param \DateTimeInterface|\DateInterval|int $ttl
  458. * @return int
  459. */
  460. protected function getSeconds($ttl)
  461. {
  462. $duration = $this->parseDateInterval($ttl);
  463. if ($duration instanceof DateTimeInterface) {
  464. $duration = Carbon::now()->diffInRealSeconds($duration, false);
  465. }
  466. return (int) ($duration > 0 ? $duration : 0);
  467. }
  468. /**
  469. * Determine if the current store supports tags.
  470. *
  471. * @return bool
  472. */
  473. public function supportsTags()
  474. {
  475. return method_exists($this->store, 'tags');
  476. }
  477. /**
  478. * Get the default cache time.
  479. *
  480. * @return int|null
  481. */
  482. public function getDefaultCacheTime()
  483. {
  484. return $this->default;
  485. }
  486. /**
  487. * Set the default cache time in seconds.
  488. *
  489. * @param int|null $seconds
  490. * @return $this
  491. */
  492. public function setDefaultCacheTime($seconds)
  493. {
  494. $this->default = $seconds;
  495. return $this;
  496. }
  497. /**
  498. * Get the cache store implementation.
  499. *
  500. * @return \Illuminate\Contracts\Cache\Store
  501. */
  502. public function getStore()
  503. {
  504. return $this->store;
  505. }
  506. /**
  507. * Set the cache store implementation.
  508. *
  509. * @param \Illuminate\Contracts\Cache\Store $store
  510. * @return static
  511. */
  512. public function setStore($store)
  513. {
  514. $this->store = $store;
  515. return $this;
  516. }
  517. /**
  518. * Fire an event for this cache instance.
  519. *
  520. * @param object|string $event
  521. * @return void
  522. */
  523. protected function event($event)
  524. {
  525. $this->events?->dispatch($event);
  526. }
  527. /**
  528. * Get the event dispatcher instance.
  529. *
  530. * @return \Illuminate\Contracts\Events\Dispatcher
  531. */
  532. public function getEventDispatcher()
  533. {
  534. return $this->events;
  535. }
  536. /**
  537. * Set the event dispatcher instance.
  538. *
  539. * @param \Illuminate\Contracts\Events\Dispatcher $events
  540. * @return void
  541. */
  542. public function setEventDispatcher(Dispatcher $events)
  543. {
  544. $this->events = $events;
  545. }
  546. /**
  547. * Determine if a cached value exists.
  548. *
  549. * @param string $key
  550. * @return bool
  551. */
  552. public function offsetExists($key): bool
  553. {
  554. return $this->has($key);
  555. }
  556. /**
  557. * Retrieve an item from the cache by key.
  558. *
  559. * @param string $key
  560. * @return mixed
  561. */
  562. public function offsetGet($key): mixed
  563. {
  564. return $this->get($key);
  565. }
  566. /**
  567. * Store an item in the cache for the default time.
  568. *
  569. * @param string $key
  570. * @param mixed $value
  571. * @return void
  572. */
  573. public function offsetSet($key, $value): void
  574. {
  575. $this->put($key, $value, $this->default);
  576. }
  577. /**
  578. * Remove an item from the cache.
  579. *
  580. * @param string $key
  581. * @return void
  582. */
  583. public function offsetUnset($key): void
  584. {
  585. $this->forget($key);
  586. }
  587. /**
  588. * Handle dynamic calls into macros or pass missing methods to the store.
  589. *
  590. * @param string $method
  591. * @param array $parameters
  592. * @return mixed
  593. */
  594. public function __call($method, $parameters)
  595. {
  596. if (static::hasMacro($method)) {
  597. return $this->macroCall($method, $parameters);
  598. }
  599. return $this->store->$method(...$parameters);
  600. }
  601. /**
  602. * Clone cache repository instance.
  603. *
  604. * @return void
  605. */
  606. public function __clone()
  607. {
  608. $this->store = clone $this->store;
  609. }
  610. }