123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- <?php
- namespace Drupal\Core\Cache;
- /**
- * A trait for cache tag checksum implementations.
- *
- * Handles delayed cache tag invalidations.
- */
- trait CacheTagsChecksumTrait {
- /**
- * A list of tags that have already been invalidated in this request.
- *
- * Used to prevent the invalidation of the same cache tag multiple times.
- *
- * @var bool[]
- */
- protected $invalidatedTags = [];
- /**
- * The set of cache tags whose invalidation is delayed.
- *
- * @var string[]
- */
- protected $delayedTags = [];
- /**
- * Contains already loaded tag invalidation counts from the storage.
- *
- * @var int[]
- */
- protected $tagCache = [];
- /**
- * Callback to be invoked just after a database transaction gets committed.
- *
- * Executes all delayed tag invalidations.
- *
- * @param bool $success
- * Whether or not the transaction was successful.
- */
- public function rootTransactionEndCallback($success) {
- if ($success) {
- $this->doInvalidateTags($this->delayedTags);
- }
- $this->delayedTags = [];
- }
- /**
- * Implements \Drupal\Core\Cache\CacheTagsChecksumInterface::invalidateTags()
- */
- public function invalidateTags(array $tags) {
- // Only invalidate tags once per request unless they are written again.
- foreach ($tags as $key => $tag) {
- if (isset($this->invalidatedTags[$tag])) {
- unset($tags[$key]);
- }
- else {
- $this->invalidatedTags[$tag] = TRUE;
- unset($this->tagCache[$tag]);
- }
- }
- if (!$tags) {
- return;
- }
- $in_transaction = $this->getDatabaseConnection()->inTransaction();
- if ($in_transaction) {
- if (empty($this->delayedTags)) {
- $this->getDatabaseConnection()->addRootTransactionEndCallback([$this, 'rootTransactionEndCallback']);
- }
- $this->delayedTags = Cache::mergeTags($this->delayedTags, $tags);
- }
- else {
- $this->doInvalidateTags($tags);
- }
- }
- /**
- * Implements \Drupal\Core\Cache\CacheTagsChecksumInterface::getCurrentChecksum()
- */
- public function getCurrentChecksum(array $tags) {
- // Any cache writes in this request containing cache tags whose invalidation
- // has been delayed due to an in-progress transaction must not be read by
- // any other request, so use a nonsensical checksum which will cause any
- // written cache items to be ignored.
- if (!empty(array_intersect($tags, $this->delayedTags))) {
- return CacheTagsChecksumInterface::INVALID_CHECKSUM_WHILE_IN_TRANSACTION;
- }
- // Remove tags that were already invalidated during this request from the
- // static caches so that another invalidation can occur later in the same
- // request. Without that, written cache items would not be invalidated
- // correctly.
- foreach ($tags as $tag) {
- unset($this->invalidatedTags[$tag]);
- }
- return $this->calculateChecksum($tags);
- }
- /**
- * Implements \Drupal\Core\Cache\CacheTagsChecksumInterface::isValid()
- */
- public function isValid($checksum, array $tags) {
- // Any cache reads in this request involving cache tags whose invalidation
- // has been delayed due to an in-progress transaction are not allowed to use
- // data stored in cache; it must be assumed to be stale. This forces those
- // results to be computed instead. Together with the logic in
- // ::getCurrentChecksum(), it also prevents that computed data from being
- // written to the cache.
- if (!empty(array_intersect($tags, $this->delayedTags))) {
- return FALSE;
- }
- return $checksum == $this->calculateChecksum($tags);
- }
- /**
- * Calculates the current checksum for a given set of tags.
- *
- * @param string[] $tags
- * The array of tags to calculate the checksum for.
- *
- * @return int
- * The calculated checksum.
- */
- protected function calculateChecksum(array $tags) {
- $checksum = 0;
- $query_tags = array_diff($tags, array_keys($this->tagCache));
- if ($query_tags) {
- $tag_invalidations = $this->getTagInvalidationCounts($query_tags);
- $this->tagCache += $tag_invalidations;
- // Fill static cache with empty objects for tags not found in the storage.
- $this->tagCache += array_fill_keys(array_diff($query_tags, array_keys($tag_invalidations)), 0);
- }
- foreach ($tags as $tag) {
- $checksum += $this->tagCache[$tag];
- }
- return $checksum;
- }
- /**
- * Implements \Drupal\Core\Cache\CacheTagsChecksumInterface::reset()
- */
- public function reset() {
- $this->tagCache = [];
- $this->invalidatedTags = [];
- }
- /**
- * Fetches invalidation counts for cache tags.
- *
- * @param string[] $tags
- * The list of tags to fetch invalidations for.
- *
- * @return int[]
- * List of invalidation counts keyed by the respective cache tag.
- */
- abstract protected function getTagInvalidationCounts(array $tags);
- /**
- * Returns the database connection.
- *
- * @return \Drupal\Core\Database\Connection
- * The database connection.
- */
- abstract protected function getDatabaseConnection();
- /**
- * Marks cache items with any of the specified tags as invalid.
- *
- * @param string[] $tags
- * The set of tags for which to invalidate cache items.
- */
- abstract protected function doInvalidateTags(array $tags);
- }
|