ChainCache.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.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 Symfony\Component\Cache\Simple;
  11. use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
  12. use Symfony\Component\Cache\Adapter\ChainAdapter;
  13. use Symfony\Component\Cache\Exception\InvalidArgumentException;
  14. use Symfony\Component\Cache\PruneableInterface;
  15. use Symfony\Component\Cache\ResettableInterface;
  16. use Symfony\Contracts\Cache\CacheInterface;
  17. use Symfony\Contracts\Service\ResetInterface;
  18. @trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', ChainCache::class, ChainAdapter::class, CacheInterface::class), E_USER_DEPRECATED);
  19. /**
  20. * Chains several caches together.
  21. *
  22. * Cached items are fetched from the first cache having them in its data store.
  23. * They are saved and deleted in all caches at once.
  24. *
  25. * @deprecated since Symfony 4.3, use ChainAdapter and type-hint for CacheInterface instead.
  26. */
  27. class ChainCache implements Psr16CacheInterface, PruneableInterface, ResettableInterface
  28. {
  29. private $miss;
  30. private $caches = [];
  31. private $defaultLifetime;
  32. private $cacheCount;
  33. /**
  34. * @param Psr16CacheInterface[] $caches The ordered list of caches used to fetch cached items
  35. * @param int $defaultLifetime The lifetime of items propagated from lower caches to upper ones
  36. */
  37. public function __construct(array $caches, int $defaultLifetime = 0)
  38. {
  39. if (!$caches) {
  40. throw new InvalidArgumentException('At least one cache must be specified.');
  41. }
  42. foreach ($caches as $cache) {
  43. if (!$cache instanceof Psr16CacheInterface) {
  44. throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', \get_class($cache), Psr16CacheInterface::class));
  45. }
  46. }
  47. $this->miss = new \stdClass();
  48. $this->caches = array_values($caches);
  49. $this->cacheCount = \count($this->caches);
  50. $this->defaultLifetime = 0 < $defaultLifetime ? $defaultLifetime : null;
  51. }
  52. /**
  53. * {@inheritdoc}
  54. */
  55. public function get($key, $default = null)
  56. {
  57. $miss = null !== $default && \is_object($default) ? $default : $this->miss;
  58. foreach ($this->caches as $i => $cache) {
  59. $value = $cache->get($key, $miss);
  60. if ($miss !== $value) {
  61. while (0 <= --$i) {
  62. $this->caches[$i]->set($key, $value, $this->defaultLifetime);
  63. }
  64. return $value;
  65. }
  66. }
  67. return $default;
  68. }
  69. /**
  70. * {@inheritdoc}
  71. */
  72. public function getMultiple($keys, $default = null)
  73. {
  74. $miss = null !== $default && \is_object($default) ? $default : $this->miss;
  75. return $this->generateItems($this->caches[0]->getMultiple($keys, $miss), 0, $miss, $default);
  76. }
  77. private function generateItems($values, $cacheIndex, $miss, $default)
  78. {
  79. $missing = [];
  80. $nextCacheIndex = $cacheIndex + 1;
  81. $nextCache = isset($this->caches[$nextCacheIndex]) ? $this->caches[$nextCacheIndex] : null;
  82. foreach ($values as $k => $value) {
  83. if ($miss !== $value) {
  84. yield $k => $value;
  85. } elseif (!$nextCache) {
  86. yield $k => $default;
  87. } else {
  88. $missing[] = $k;
  89. }
  90. }
  91. if ($missing) {
  92. $cache = $this->caches[$cacheIndex];
  93. $values = $this->generateItems($nextCache->getMultiple($missing, $miss), $nextCacheIndex, $miss, $default);
  94. foreach ($values as $k => $value) {
  95. if ($miss !== $value) {
  96. $cache->set($k, $value, $this->defaultLifetime);
  97. yield $k => $value;
  98. } else {
  99. yield $k => $default;
  100. }
  101. }
  102. }
  103. }
  104. /**
  105. * {@inheritdoc}
  106. */
  107. public function has($key)
  108. {
  109. foreach ($this->caches as $cache) {
  110. if ($cache->has($key)) {
  111. return true;
  112. }
  113. }
  114. return false;
  115. }
  116. /**
  117. * {@inheritdoc}
  118. */
  119. public function clear()
  120. {
  121. $cleared = true;
  122. $i = $this->cacheCount;
  123. while ($i--) {
  124. $cleared = $this->caches[$i]->clear() && $cleared;
  125. }
  126. return $cleared;
  127. }
  128. /**
  129. * {@inheritdoc}
  130. */
  131. public function delete($key)
  132. {
  133. $deleted = true;
  134. $i = $this->cacheCount;
  135. while ($i--) {
  136. $deleted = $this->caches[$i]->delete($key) && $deleted;
  137. }
  138. return $deleted;
  139. }
  140. /**
  141. * {@inheritdoc}
  142. */
  143. public function deleteMultiple($keys)
  144. {
  145. if ($keys instanceof \Traversable) {
  146. $keys = iterator_to_array($keys, false);
  147. }
  148. $deleted = true;
  149. $i = $this->cacheCount;
  150. while ($i--) {
  151. $deleted = $this->caches[$i]->deleteMultiple($keys) && $deleted;
  152. }
  153. return $deleted;
  154. }
  155. /**
  156. * {@inheritdoc}
  157. */
  158. public function set($key, $value, $ttl = null)
  159. {
  160. $saved = true;
  161. $i = $this->cacheCount;
  162. while ($i--) {
  163. $saved = $this->caches[$i]->set($key, $value, $ttl) && $saved;
  164. }
  165. return $saved;
  166. }
  167. /**
  168. * {@inheritdoc}
  169. */
  170. public function setMultiple($values, $ttl = null)
  171. {
  172. if ($values instanceof \Traversable) {
  173. $valuesIterator = $values;
  174. $values = function () use ($valuesIterator, &$values) {
  175. $generatedValues = [];
  176. foreach ($valuesIterator as $key => $value) {
  177. yield $key => $value;
  178. $generatedValues[$key] = $value;
  179. }
  180. $values = $generatedValues;
  181. };
  182. $values = $values();
  183. }
  184. $saved = true;
  185. $i = $this->cacheCount;
  186. while ($i--) {
  187. $saved = $this->caches[$i]->setMultiple($values, $ttl) && $saved;
  188. }
  189. return $saved;
  190. }
  191. /**
  192. * {@inheritdoc}
  193. */
  194. public function prune()
  195. {
  196. $pruned = true;
  197. foreach ($this->caches as $cache) {
  198. if ($cache instanceof PruneableInterface) {
  199. $pruned = $cache->prune() && $pruned;
  200. }
  201. }
  202. return $pruned;
  203. }
  204. /**
  205. * {@inheritdoc}
  206. */
  207. public function reset()
  208. {
  209. foreach ($this->caches as $cache) {
  210. if ($cache instanceof ResetInterface) {
  211. $cache->reset();
  212. }
  213. }
  214. }
  215. }