CacheTagsChecksumTrait.php
Same filename in other branches
Namespace
Drupal\Core\CacheFile
-
core/
lib/ Drupal/ Core/ Cache/ CacheTagsChecksumTrait.php
View source
<?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\CacheTagsInvalidatorInterface::invalidateTags()
*/
public function invalidateTags(array $tags) {
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()
->transactionManager()
->addPostTransactionCallback([
$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) {
// If there are no cache tags, then there is no cache tag to validate,
// hence it's always valid.
if (empty($tags)) {
return TRUE;
}
// 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;
// If there are no cache tags, then there is no cache tag to checksum,
// so return early..
if (empty($tags)) {
return $checksum;
}
$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.
*
* @throws \Exception
* Thrown if the table could not be created or the database connection
* failed.
*/
protected abstract function getTagInvalidationCounts(array $tags);
/**
* Returns the database connection.
*
* @return \Drupal\Core\Database\Connection
* The database connection.
*/
protected abstract 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.
*
* @throws \Exception
* Thrown if the table could not be created or the database connection
* failed.
*/
protected abstract function doInvalidateTags(array $tags);
}
Traits
Title | Deprecated | Summary |
---|---|---|
CacheTagsChecksumTrait | A trait for cache tag checksum implementations. |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.