ChainAdapter.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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\Adapter;
  11. use Psr\Cache\CacheItemInterface;
  12. use Psr\Cache\CacheItemPoolInterface;
  13. use Symfony\Component\Cache\CacheItem;
  14. use Symfony\Component\Cache\Exception\InvalidArgumentException;
  15. use Symfony\Component\Cache\PruneableInterface;
  16. use Symfony\Component\Cache\ResettableInterface;
  17. /**
  18. * Chains several adapters together.
  19. *
  20. * Cached items are fetched from the first adapter having them in its data store.
  21. * They are saved and deleted in all adapters at once.
  22. *
  23. * @author Kévin Dunglas <dunglas@gmail.com>
  24. */
  25. class ChainAdapter implements AdapterInterface, PruneableInterface, ResettableInterface
  26. {
  27. private $adapters = [];
  28. private $adapterCount;
  29. private $syncItem;
  30. /**
  31. * @param CacheItemPoolInterface[] $adapters The ordered list of adapters used to fetch cached items
  32. * @param int $defaultLifetime The default lifetime of items propagated from lower adapters to upper ones
  33. */
  34. public function __construct(array $adapters, int $defaultLifetime = 0)
  35. {
  36. if (!$adapters) {
  37. throw new InvalidArgumentException('At least one adapter must be specified.');
  38. }
  39. foreach ($adapters as $adapter) {
  40. if (!$adapter instanceof CacheItemPoolInterface) {
  41. throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', \get_class($adapter), CacheItemPoolInterface::class));
  42. }
  43. if ($adapter instanceof AdapterInterface) {
  44. $this->adapters[] = $adapter;
  45. } else {
  46. $this->adapters[] = new ProxyAdapter($adapter);
  47. }
  48. }
  49. $this->adapterCount = \count($this->adapters);
  50. $this->syncItem = \Closure::bind(
  51. function ($sourceItem, $item) use ($defaultLifetime) {
  52. $item->value = $sourceItem->value;
  53. $item->expiry = $sourceItem->expiry;
  54. $item->isHit = $sourceItem->isHit;
  55. if (0 < $sourceItem->defaultLifetime && $sourceItem->defaultLifetime < $defaultLifetime) {
  56. $defaultLifetime = $sourceItem->defaultLifetime;
  57. }
  58. if (0 < $defaultLifetime && ($item->defaultLifetime <= 0 || $defaultLifetime < $item->defaultLifetime)) {
  59. $item->defaultLifetime = $defaultLifetime;
  60. }
  61. return $item;
  62. },
  63. null,
  64. CacheItem::class
  65. );
  66. }
  67. /**
  68. * {@inheritdoc}
  69. */
  70. public function getItem($key)
  71. {
  72. $syncItem = $this->syncItem;
  73. $misses = [];
  74. foreach ($this->adapters as $i => $adapter) {
  75. $item = $adapter->getItem($key);
  76. if ($item->isHit()) {
  77. while (0 <= --$i) {
  78. $this->adapters[$i]->save($syncItem($item, $misses[$i]));
  79. }
  80. return $item;
  81. }
  82. $misses[$i] = $item;
  83. }
  84. return $item;
  85. }
  86. /**
  87. * {@inheritdoc}
  88. */
  89. public function getItems(array $keys = [])
  90. {
  91. return $this->generateItems($this->adapters[0]->getItems($keys), 0);
  92. }
  93. private function generateItems($items, $adapterIndex)
  94. {
  95. $missing = [];
  96. $misses = [];
  97. $nextAdapterIndex = $adapterIndex + 1;
  98. $nextAdapter = isset($this->adapters[$nextAdapterIndex]) ? $this->adapters[$nextAdapterIndex] : null;
  99. foreach ($items as $k => $item) {
  100. if (!$nextAdapter || $item->isHit()) {
  101. yield $k => $item;
  102. } else {
  103. $missing[] = $k;
  104. $misses[$k] = $item;
  105. }
  106. }
  107. if ($missing) {
  108. $syncItem = $this->syncItem;
  109. $adapter = $this->adapters[$adapterIndex];
  110. $items = $this->generateItems($nextAdapter->getItems($missing), $nextAdapterIndex);
  111. foreach ($items as $k => $item) {
  112. if ($item->isHit()) {
  113. $adapter->save($syncItem($item, $misses[$k]));
  114. }
  115. yield $k => $item;
  116. }
  117. }
  118. }
  119. /**
  120. * {@inheritdoc}
  121. */
  122. public function hasItem($key)
  123. {
  124. foreach ($this->adapters as $adapter) {
  125. if ($adapter->hasItem($key)) {
  126. return true;
  127. }
  128. }
  129. return false;
  130. }
  131. /**
  132. * {@inheritdoc}
  133. */
  134. public function clear()
  135. {
  136. $cleared = true;
  137. $i = $this->adapterCount;
  138. while ($i--) {
  139. $cleared = $this->adapters[$i]->clear() && $cleared;
  140. }
  141. return $cleared;
  142. }
  143. /**
  144. * {@inheritdoc}
  145. */
  146. public function deleteItem($key)
  147. {
  148. $deleted = true;
  149. $i = $this->adapterCount;
  150. while ($i--) {
  151. $deleted = $this->adapters[$i]->deleteItem($key) && $deleted;
  152. }
  153. return $deleted;
  154. }
  155. /**
  156. * {@inheritdoc}
  157. */
  158. public function deleteItems(array $keys)
  159. {
  160. $deleted = true;
  161. $i = $this->adapterCount;
  162. while ($i--) {
  163. $deleted = $this->adapters[$i]->deleteItems($keys) && $deleted;
  164. }
  165. return $deleted;
  166. }
  167. /**
  168. * {@inheritdoc}
  169. */
  170. public function save(CacheItemInterface $item)
  171. {
  172. $saved = true;
  173. $i = $this->adapterCount;
  174. while ($i--) {
  175. $saved = $this->adapters[$i]->save($item) && $saved;
  176. }
  177. return $saved;
  178. }
  179. /**
  180. * {@inheritdoc}
  181. */
  182. public function saveDeferred(CacheItemInterface $item)
  183. {
  184. $saved = true;
  185. $i = $this->adapterCount;
  186. while ($i--) {
  187. $saved = $this->adapters[$i]->saveDeferred($item) && $saved;
  188. }
  189. return $saved;
  190. }
  191. /**
  192. * {@inheritdoc}
  193. */
  194. public function commit()
  195. {
  196. $committed = true;
  197. $i = $this->adapterCount;
  198. while ($i--) {
  199. $committed = $this->adapters[$i]->commit() && $committed;
  200. }
  201. return $committed;
  202. }
  203. /**
  204. * {@inheritdoc}
  205. */
  206. public function prune()
  207. {
  208. $pruned = true;
  209. foreach ($this->adapters as $adapter) {
  210. if ($adapter instanceof PruneableInterface) {
  211. $pruned = $adapter->prune() && $pruned;
  212. }
  213. }
  214. return $pruned;
  215. }
  216. /**
  217. * {@inheritdoc}
  218. */
  219. public function reset()
  220. {
  221. foreach ($this->adapters as $adapter) {
  222. if ($adapter instanceof ResettableInterface) {
  223. $adapter->reset();
  224. }
  225. }
  226. }
  227. }