PageRenderTime 42ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/core/lib/Drupal/Core/Cache/CacheCollector.php

https://gitlab.com/geeta7/drupal
PHP | 348 lines | 132 code | 34 blank | 182 comment | 21 complexity | c5ef7d1c2dfe0aaa7f3d60c5fac2dd92 MD5 | raw file
  1. <?php
  2. /**
  3. * @file
  4. * Contains \Drupal\Core\Cache\CacheCollector.
  5. */
  6. namespace Drupal\Core\Cache;
  7. use Drupal\Component\Utility\Crypt;
  8. use Drupal\Core\DestructableInterface;
  9. use Drupal\Core\Lock\LockBackendInterface;
  10. /**
  11. * Default implementation for CacheCollectorInterface.
  12. *
  13. * By default, the class accounts for caches where calling functions might
  14. * request keys that won't exist even after a cache rebuild. This prevents
  15. * situations where a cache rebuild would be triggered over and over due to a
  16. * 'missing' item. These cases are stored internally as a value of NULL. This
  17. * means that the CacheCollector::get() method must be overridden if caching
  18. * data where the values can legitimately be NULL, and where
  19. * CacheCollector->has() needs to correctly return (equivalent to
  20. * array_key_exists() vs. isset()). This should not be necessary in the majority
  21. * of cases.
  22. *
  23. * @ingroup cache
  24. */
  25. abstract class CacheCollector implements CacheCollectorInterface, DestructableInterface {
  26. /**
  27. * The cache id that is used for the cache entry.
  28. *
  29. * @var string
  30. */
  31. protected $cid;
  32. /**
  33. * A list of tags that are used for the cache entry.
  34. *
  35. * @var array
  36. */
  37. protected $tags;
  38. /**
  39. * The cache backend that should be used.
  40. *
  41. * @var \Drupal\Core\Cache\CacheBackendInterface
  42. */
  43. protected $cache;
  44. /**
  45. * The lock backend that should be used.
  46. *
  47. * @var \Drupal\Core\Lock\LockBackendInterface
  48. */
  49. protected $lock;
  50. /**
  51. * An array of keys to add to the cache on service termination.
  52. *
  53. * @var array
  54. */
  55. protected $keysToPersist = array();
  56. /**
  57. * An array of keys to remove from the cache on service termination.
  58. *
  59. * @var array
  60. */
  61. protected $keysToRemove = array();
  62. /**
  63. * Storage for the data itself.
  64. *
  65. * @var array
  66. */
  67. protected $storage = array();
  68. /**
  69. * Stores the cache creation time.
  70. *
  71. * This is used to check if an invalidated cache item has been overwritten in
  72. * the meantime.
  73. *
  74. * @var int
  75. */
  76. protected $cacheCreated;
  77. /**
  78. * Flag that indicates of the cache has been invalidated.
  79. *
  80. * @var bool
  81. */
  82. protected $cacheInvalidated = FALSE;
  83. /**
  84. * Indicates if the collected cache was already loaded.
  85. *
  86. * The collected cache is lazy loaded when an entry is set, get or deleted.
  87. *
  88. * @var bool
  89. */
  90. protected $cacheLoaded = FALSE;
  91. /**
  92. * Constructs a CacheCollector object.
  93. *
  94. * @param string $cid
  95. * The cid for the array being cached.
  96. * @param \Drupal\Core\Cache\CacheBackendInterface $cache
  97. * The cache backend.
  98. * @param \Drupal\Core\Lock\LockBackendInterface $lock
  99. * The lock backend.
  100. * @param array $tags
  101. * (optional) The tags to specify for the cache item.
  102. */
  103. public function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, array $tags = array()) {
  104. assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($tags)', 'Cache tags must be strings.');
  105. $this->cid = $cid;
  106. $this->cache = $cache;
  107. $this->tags = $tags;
  108. $this->lock = $lock;
  109. }
  110. /**
  111. * Gets the cache ID.
  112. *
  113. * @return string
  114. */
  115. protected function getCid() {
  116. return $this->cid;
  117. }
  118. /**
  119. * {@inheritdoc}
  120. */
  121. public function has($key) {
  122. // Make sure the value is loaded.
  123. $this->get($key);
  124. return isset($this->storage[$key]) || array_key_exists($key, $this->storage);
  125. }
  126. /**
  127. * {@inheritdoc}
  128. */
  129. public function get($key) {
  130. $this->lazyLoadCache();
  131. if (isset($this->storage[$key]) || array_key_exists($key, $this->storage)) {
  132. return $this->storage[$key];
  133. }
  134. else {
  135. return $this->resolveCacheMiss($key);
  136. }
  137. }
  138. /**
  139. * Implements \Drupal\Core\Cache\CacheCollectorInterface::set().
  140. *
  141. * This is not persisted by default. In practice this means that setting a
  142. * value will only apply while the object is in scope and will not be written
  143. * back to the persistent cache. This follows a similar pattern to static vs.
  144. * persistent caching in procedural code. Extending classes may wish to alter
  145. * this behavior, for example by adding a call to persist().
  146. */
  147. public function set($key, $value) {
  148. $this->lazyLoadCache();
  149. $this->storage[$key] = $value;
  150. // The key might have been marked for deletion.
  151. unset($this->keysToRemove[$key]);
  152. $this->invalidateCache();
  153. }
  154. /**
  155. * {@inheritdoc}
  156. */
  157. public function delete($key) {
  158. $this->lazyLoadCache();
  159. unset($this->storage[$key]);
  160. $this->keysToRemove[$key] = $key;
  161. // The key might have been marked for persisting.
  162. unset($this->keysToPersist[$key]);
  163. $this->invalidateCache();
  164. }
  165. /**
  166. * Flags an offset value to be written to the persistent cache.
  167. *
  168. * @param string $key
  169. * The key that was requested.
  170. * @param bool $persist
  171. * (optional) Whether the offset should be persisted or not, defaults to
  172. * TRUE. When called with $persist = FALSE the offset will be unflagged so
  173. * that it will not be written at the end of the request.
  174. */
  175. protected function persist($key, $persist = TRUE) {
  176. $this->keysToPersist[$key] = $persist;
  177. }
  178. /**
  179. * Resolves a cache miss.
  180. *
  181. * When an offset is not found in the object, this is treated as a cache
  182. * miss. This method allows classes using this implementation to look up the
  183. * actual value and allow it to be cached.
  184. *
  185. * @param string $key
  186. * The offset that was requested.
  187. *
  188. * @return mixed
  189. * The value of the offset, or NULL if no value was found.
  190. */
  191. abstract protected function resolveCacheMiss($key);
  192. /**
  193. * Writes a value to the persistent cache immediately.
  194. *
  195. * @param bool $lock
  196. * (optional) Whether to acquire a lock before writing to cache. Defaults to
  197. * TRUE.
  198. */
  199. protected function updateCache($lock = TRUE) {
  200. $data = array();
  201. foreach ($this->keysToPersist as $offset => $persist) {
  202. if ($persist) {
  203. $data[$offset] = $this->storage[$offset];
  204. }
  205. }
  206. if (empty($data) && empty($this->keysToRemove)) {
  207. return;
  208. }
  209. // Lock cache writes to help avoid stampedes.
  210. $cid = $this->getCid();
  211. $lock_name = $this->normalizeLockName($cid . ':' . __CLASS__);
  212. if (!$lock || $this->lock->acquire($lock_name)) {
  213. // Set and delete operations invalidate the cache item. Try to also load
  214. // an eventually invalidated cache entry, only update an invalidated cache
  215. // entry if the creation date did not change as this could result in an
  216. // inconsistent cache.
  217. if ($cache = $this->cache->get($cid, $this->cacheInvalidated)) {
  218. if ($this->cacheInvalidated && $cache->created != $this->cacheCreated) {
  219. // We have invalidated the cache in this request and got a different
  220. // cache entry. Do not attempt to overwrite data that might have been
  221. // changed in a different request. We'll let the cache rebuild in
  222. // later requests.
  223. $this->cache->delete($cid);
  224. $this->lock->release($lock_name);
  225. return;
  226. }
  227. $data = array_merge($cache->data, $data);
  228. }
  229. // Remove keys marked for deletion.
  230. foreach ($this->keysToRemove as $delete_key) {
  231. unset($data[$delete_key]);
  232. }
  233. $this->cache->set($cid, $data, Cache::PERMANENT, $this->tags);
  234. if ($lock) {
  235. $this->lock->release($lock_name);
  236. }
  237. }
  238. $this->keysToPersist = array();
  239. $this->keysToRemove = array();
  240. }
  241. /**
  242. * Normalizes a cache ID in order to comply with database limitations.
  243. *
  244. * @param string $cid
  245. * The passed in cache ID.
  246. *
  247. * @return string
  248. * An ASCII-encoded cache ID that is at most 255 characters long.
  249. */
  250. protected function normalizeLockName($cid) {
  251. // Nothing to do if the ID is a US ASCII string of 255 characters or less.
  252. $cid_is_ascii = mb_check_encoding($cid, 'ASCII');
  253. if (strlen($cid) <= 255 && $cid_is_ascii) {
  254. return $cid;
  255. }
  256. // Return a string that uses as much as possible of the original cache ID
  257. // with the hash appended.
  258. $hash = Crypt::hashBase64($cid);
  259. if (!$cid_is_ascii) {
  260. return $hash;
  261. }
  262. return substr($cid, 0, 255 - strlen($hash)) . $hash;
  263. }
  264. /**
  265. * {@inheritdoc}
  266. */
  267. public function reset() {
  268. $this->storage = array();
  269. $this->keysToPersist = array();
  270. $this->keysToRemove = array();
  271. $this->cacheLoaded = FALSE;
  272. }
  273. /**
  274. * {@inheritdoc}
  275. */
  276. public function clear() {
  277. $this->reset();
  278. if ($this->tags) {
  279. Cache::invalidateTags($this->tags);
  280. }
  281. else {
  282. $this->cache->delete($this->getCid());
  283. }
  284. }
  285. /**
  286. * {@inheritdoc}
  287. */
  288. public function destruct() {
  289. $this->updateCache();
  290. }
  291. /**
  292. * Loads the cache if not already done.
  293. */
  294. protected function lazyLoadCache() {
  295. if ($this->cacheLoaded) {
  296. return;
  297. }
  298. // The cache was not yet loaded, set flag to TRUE.
  299. $this->cacheLoaded = TRUE;
  300. if ($cache = $this->cache->get($this->getCid())) {
  301. $this->cacheCreated = $cache->created;
  302. $this->storage = $cache->data;
  303. }
  304. }
  305. /**
  306. * Invalidate the cache.
  307. */
  308. protected function invalidateCache() {
  309. // Invalidate the cache to make sure that other requests immediately see the
  310. // deletion before this request is terminated.
  311. $this->cache->invalidate($this->getCid());
  312. $this->cacheInvalidated = TRUE;
  313. }
  314. }