diff --git a/core/core.services.yml b/core/core.services.yml index b850c9e..2769dc0 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -6,9 +6,10 @@ services: - [setContainer, ['@service_container']] cache.backend.database: class: Drupal\Core\Cache\DatabaseBackendFactory - arguments: ['@database'] + arguments: ['@database', '@cache_tag_factory'] cache.backend.memory: class: Drupal\Core\Cache\MemoryBackendFactory + arguments: ['@cache_tag_factory'] cache.bootstrap: class: Drupal\Core\Cache\CacheBackendInterface tags: @@ -66,6 +67,22 @@ services: factory_method: get factory_service: cache_factory arguments: [path] + cache_tag_factory: + class: Drupal\Core\Cache\CacheTagFactory + arguments: ['@settings'] + calls: + - [setContainer, ['@service_container']] + cache.tag.database: + class: Drupal\Core\Cache\DatabaseTag + arguments: ['@database'] + tags: + - { name: cache.tag } + - { name: persist } + cache.tag.memory: + class: Drupal\Core\Cache\MemoryTag + tags: + - { name: cache.tag } + - { name: persist } config.cachedstorage.storage: class: Drupal\Core\Config\FileStorage factory_class: Drupal\Core\Config\FileStorageFactory diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 0b25e59..181606a 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -383,6 +383,8 @@ function install_begin_request(&$install_state) { ->addArgument(new Reference('event_dispatcher')) ->addArgument(new Reference('config.typed')); + $container->register('cache.tag.memory', 'Drupal\Core\Cache\MemoryTag'); + // Register the 'language_manager' service. $container->register('language_manager', 'Drupal\Core\Language\LanguageManager') ->addArgument(new Reference('language.default')); @@ -393,6 +395,7 @@ function install_begin_request(&$install_state) { foreach (array('bootstrap', 'config', 'cache', 'menu', 'page', 'path') as $bin) { $container ->register("cache.$bin", 'Drupal\Core\Cache\MemoryBackend') + ->addArgument(new Reference('cache.tag.memory')) ->addArgument($bin); } diff --git a/core/lib/Drupal/Core/Cache/BackendChain.php b/core/lib/Drupal/Core/Cache/BackendChain.php index 5a20ff6..82f4775 100644 --- a/core/lib/Drupal/Core/Cache/BackendChain.php +++ b/core/lib/Drupal/Core/Cache/BackendChain.php @@ -148,15 +148,6 @@ public function deleteMultiple(array $cids) { } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::deleteTags(). - */ - public function deleteTags(array $tags) { - foreach ($this->backends as $backend) { - $backend->deleteTags($tags); - } - } - - /** * Implements Drupal\Core\Cache\CacheBackendInterface::deleteAll(). */ public function deleteAll() { @@ -184,15 +175,6 @@ public function invalidateMultiple(array $cids) { } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags(). - */ - public function invalidateTags(array $tags) { - foreach ($this->backends as $backend) { - $backend->invalidateTags($tags); - } - } - - /** * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateAll(). */ public function invalidateAll() { diff --git a/core/lib/Drupal/Core/Cache/Cache.php b/core/lib/Drupal/Core/Cache/Cache.php index c2f5fe6..ebce27d 100644 --- a/core/lib/Drupal/Core/Cache/Cache.php +++ b/core/lib/Drupal/Core/Cache/Cache.php @@ -31,8 +31,8 @@ class Cache { * The list of tags to delete cache items for. */ public static function deleteTags(array $tags) { - foreach (static::getBins() as $cache_backend) { - $cache_backend->deleteTags($tags); + foreach (static::getTags() as $tag_service) { + $tag_service->deleteTags($tags); } } @@ -50,16 +50,32 @@ public static function deleteTags(array $tags) { * The list of tags to invalidate cache items for. */ public static function invalidateTags(array $tags) { - foreach (static::getBins() as $cache_backend) { - $cache_backend->invalidateTags($tags); + foreach (static::getTags() as $tag_service) { + $tag_service->invalidateTags($tags); } } /** + * Gets all cache tag services. + * + * @return array + * An array of cache tag objects keyed by service name. + */ + public static function getTags() { + $tags = array(); + $container = \Drupal::getContainer(); + foreach ($container->getParameter('cache_tags') as $service_id => $tag) { + $tags[$tag] = $container->get($service_id); + } + + return $tags; + } + + /** * Gets all cache bin services. * * @return array - * An array of cache backend objects keyed by cache bins. + * An array of cache bin objects keyed by cache bin. */ public static function getBins() { $bins = array(); diff --git a/core/lib/Drupal/Core/Cache/CacheBackendInterface.php b/core/lib/Drupal/Core/Cache/CacheBackendInterface.php index 0046918..c8b516f 100644 --- a/core/lib/Drupal/Core/Cache/CacheBackendInterface.php +++ b/core/lib/Drupal/Core/Cache/CacheBackendInterface.php @@ -43,15 +43,15 @@ * @endcode * * There are two ways to "remove" a cache item: - * - Deletion (using delete(), deleteMultiple(), deleteTags() or deleteAll()): + * - Deletion (using delete(), deleteMultiple() or deleteAll()): * Permanently removes the item from the cache. - * - Invalidation (using invalidate(), invalidateMultiple(), invalidateTags() - * or invalidateAll()): a "soft" delete that only marks the items as - * "invalid", meaning "not fresh" or "not fresh enough". Invalid items are - * not usually returned from the cache, so in most ways they behave as if they - * have been deleted. However, it is possible to retrieve the invalid entries, - * if they have not yet been permanently removed by the garbage collector, by - * passing TRUE as the second argument for get($cid, $allow_invalid). + * - Invalidation (using invalidate(), invalidateMultiple() or invalidateAll()): + * a "soft" delete that only marks the items as "invalid", meaning "not fresh" + * or "not fresh enough". Invalid items are not usually returned from the + * cache, so in most ways they behave as if they have been deleted. However, + * it is possible to retrieve the invalid entries, if they have not yet been + * permanently removed by the garbage collector, by passing TRUE as the second + * argument for get($cid, $allow_invalid). * * Cache items should be deleted if they are no longer considered useful. This * is relevant e.g. if the cache item contains references to data that has been @@ -161,7 +161,7 @@ public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array * * @see \Drupal\Core\Cache\CacheBackendInterface::invalidate() * @see \Drupal\Core\Cache\CacheBackendInterface::deleteMultiple() - * @see \Drupal\Core\Cache\CacheBackendInterface::deleteTags() + * @see \Drupal\Core\Cache\CacheTagInterface::deleteTags() * @see \Drupal\Core\Cache\CacheBackendInterface::deleteAll() */ public function delete($cid); @@ -180,39 +180,18 @@ public function delete($cid); * * @see \Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple() * @see \Drupal\Core\Cache\CacheBackendInterface::delete() - * @see \Drupal\Core\Cache\CacheBackendInterface::deleteTags() + * @see \Drupal\Core\Cache\CacheTagInterface::deleteTags() * @see \Drupal\Core\Cache\CacheBackendInterface::deleteAll() */ public function deleteMultiple(array $cids); /** - * Deletes items with any of the specified tags. - * - * If the cache items are being deleted because they are no longer "fresh", - * you may consider using invalidateTags() instead. This allows callers to - * retrieve the invalid items by calling get() with $allow_invalid set to TRUE. - * In some cases an invalid item may be acceptable rather than having to - * rebuild the cache. - * - * @param array $tags - * Associative array of tags, in the same format that is passed to - * CacheBackendInterface::set(). - * - * @see \Drupal\Core\Cache\CacheBackendInterface::set() - * @see \Drupal\Core\Cache\CacheBackendInterface::invalidateTags() - * @see \Drupal\Core\Cache\CacheBackendInterface::delete() - * @see \Drupal\Core\Cache\CacheBackendInterface::deleteMultiple() - * @see \Drupal\Core\Cache\CacheBackendInterface::deleteAll() - */ - public function deleteTags(array $tags); - - /** * Deletes all cache items in a bin. * * @see \Drupal\Core\Cache\CacheBackendInterface::invalidateAll() * @see \Drupal\Core\Cache\CacheBackendInterface::delete() * @see \Drupal\Core\Cache\CacheBackendInterface::deleteMultiple() - * @see \Drupal\Core\Cache\CacheBackendInterface::deleteTags() + * @see \Drupal\Core\Cache\CacheTagInterface::deleteTags() */ public function deleteAll(); @@ -227,7 +206,7 @@ public function deleteAll(); * * @see \Drupal\Core\Cache\CacheBackendInterface::delete() * @see \Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple() - * @see \Drupal\Core\Cache\CacheBackendInterface::invalidateTags() + * @see \Drupal\Core\Cache\CacheTagInterface::invalidateTags() * @see \Drupal\Core\Cache\CacheBackendInterface::invalidateAll() */ public function invalidate($cid); @@ -243,27 +222,12 @@ public function invalidate($cid); * * @see \Drupal\Core\Cache\CacheBackendInterface::deleteMultiple() * @see \Drupal\Core\Cache\CacheBackendInterface::invalidate() - * @see \Drupal\Core\Cache\CacheBackendInterface::invalidateTags() + * @see \Drupal\Core\Cache\CacheTagInterface::invalidateTags() * @see \Drupal\Core\Cache\CacheBackendInterface::invalidateAll() */ public function invalidateMultiple(array $cids); /** - * Marks cache items with any of the specified tags as invalid. - * - * @param array $tags - * Associative array of tags, in the same format that is passed to - * CacheBackendInterface::set(). - * - * @see \Drupal\Core\Cache\CacheBackendInterface::set() - * @see \Drupal\Core\Cache\CacheBackendInterface::deleteTags() - * @see \Drupal\Core\Cache\CacheBackendInterface::invalidate() - * @see \Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple() - * @see \Drupal\Core\Cache\CacheBackendInterface::invalidateAll() - */ - public function invalidateTags(array $tags); - - /** * Marks all cache items as invalid. * * Invalid items may be returned in later calls to get(), if the $allow_invalid @@ -275,7 +239,7 @@ public function invalidateTags(array $tags); * @see \Drupal\Core\Cache\CacheBackendInterface::deleteAll() * @see \Drupal\Core\Cache\CacheBackendInterface::invalidate() * @see \Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple() - * @see \Drupal\Core\Cache\CacheBackendInterface::invalidateTags() + * @see \Drupal\Core\Cache\CacheTagInterface::invalidateTags() */ public function invalidateAll(); diff --git a/core/lib/Drupal/Core/Cache/CacheTagBase.php b/core/lib/Drupal/Core/Cache/CacheTagBase.php new file mode 100644 index 0000000..bfcc211 --- /dev/null +++ b/core/lib/Drupal/Core/Cache/CacheTagBase.php @@ -0,0 +1,43 @@ + $values) { + if (is_array($values)) { + foreach ($values as $value) { + $flat_tags[] = "$namespace:$value"; + } + } + else { + $flat_tags[] = "$namespace:$values"; + } + } + return $flat_tags; + } + +} diff --git a/core/lib/Drupal/Core/Cache/CacheTagFactory.php b/core/lib/Drupal/Core/Cache/CacheTagFactory.php new file mode 100644 index 0000000..cb9715e --- /dev/null +++ b/core/lib/Drupal/Core/Cache/CacheTagFactory.php @@ -0,0 +1,57 @@ +settings = $settings; + } + + /** + * Instantiates a cache tag class. + * + * By default, this returns an instance of the + * Drupal\Core\Cache\DatabaseTag class. + * + * @return \Drupal\Core\Cache\CacheTagInterface + * The cache tag instance. + */ + public function get() { + $cache_tag_service = $this->settings->get('cache_tag_service'); + + if (isset($cache_tag_service)) { + $service_name = $cache_tag_service; + } + else { + $service_name = 'cache.tag.database'; + } + + return $this->container->get($service_name); + } + +} diff --git a/core/lib/Drupal/Core/Cache/CacheTagInterface.php b/core/lib/Drupal/Core/Cache/CacheTagInterface.php new file mode 100644 index 0000000..773f101 --- /dev/null +++ b/core/lib/Drupal/Core/Cache/CacheTagInterface.php @@ -0,0 +1,58 @@ +deleted should be set to TRUE if item was deleted via tags and + * $item->valid should be set to FALSE if item was invalidated. + */ + public function prepareGet(&$item); + + /** + * Clears cache tag service internal caches. + */ + public function clearCache(); +} diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php index 74d1292..d818f67 100644 --- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php +++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php @@ -23,7 +23,6 @@ class DatabaseBackend implements CacheBackendInterface { */ protected $bin; - /** * The database connection. * @@ -32,21 +31,32 @@ class DatabaseBackend implements CacheBackendInterface { protected $connection; /** + * The cache tag service. + * + * @var \Drupal\Core\Cache\CacheTagInterface + */ + protected $cacheTag; + + /** * Constructs a DatabaseBackend object. * * @param \Drupal\Core\Database\Connection $connection * The database connection. + * @param \Drupal\Core\Cache\CacheTagInterface + * The cache tag service. * @param string $bin * The cache bin for which the object is created. */ - public function __construct(Connection $connection, $bin) { + public function __construct(Connection $connection, CacheTagInterface $cache_tag, $bin) { // All cache tables should be prefixed with 'cache_', except for the // default 'cache' bin. if ($bin != 'cache') { $bin = 'cache_' . $bin; } $this->bin = $bin; + $this->connection = $connection; + $this->cacheTag = $cache_tag; } /** @@ -109,19 +119,17 @@ protected function prepareItem($cache, $allow_invalid) { $cache->tags = $cache->tags ? explode(' ', $cache->tags) : array(); - $checksum = $this->checksumTags($cache->tags); + // Check expire time. + $cache->valid = $time_valid = $cache->expire == Cache::PERMANENT || $cache->expire >= REQUEST_TIME; - // Check if deleteTags() has been called with any of the entry's tags. - if ($cache->checksum_deletions != $checksum['deletions']) { + $this->cacheTag->prepareGet($cache); + if ($cache->deleted) { + $this->delete($cache->cid); return FALSE; } - // Check expire time. - $cache->valid = $cache->expire == Cache::PERMANENT || $cache->expire >= REQUEST_TIME; - - // Check if invalidateTags() has been called with any of the entry's tags. - if ($cache->checksum_invalidations != $checksum['invalidations']) { - $cache->valid = FALSE; + if ($time_valid && !$cache->valid) { + $this->invalidate($cache->cid); } if (!$allow_invalid && !$cache->valid) { @@ -163,8 +171,8 @@ public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array * Actually set the cache. */ protected function doSet($cid, $data, $expire, $tags) { - $flat_tags = $this->flattenTags($tags); - $checksum = $this->checksumTags($flat_tags); + $checksum = $this->cacheTag->checksumTags($tags); + $flat_tags = $this->cacheTag->flattenTags($tags); $fields = array( 'serialized' => 0, 'created' => REQUEST_TIME, @@ -219,26 +227,6 @@ public function deleteMultiple(array $cids) { } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::deleteTags(). - */ - public function deleteTags(array $tags) { - $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array()); - foreach ($this->flattenTags($tags) as $tag) { - unset($tag_cache[$tag]); - try { - $this->connection->merge('cache_tags') - ->insertFields(array('deletions' => 1)) - ->expression('deletions', 'deletions + 1') - ->key(array('tag' => $tag)) - ->execute(); - } - catch (\Exception $e) { - $this->catchException($e, 'cache_tags'); - } - } - } - - /** * Implements Drupal\Core\Cache\CacheBackendInterface::deleteAll(). */ public function deleteAll() { @@ -282,26 +270,6 @@ public function invalidateMultiple(array $cids) { } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags(). - */ - public function invalidateTags(array $tags) { - try { - $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array()); - foreach ($this->flattenTags($tags) as $tag) { - unset($tag_cache[$tag]); - $this->connection->merge('cache_tags') - ->insertFields(array('invalidations' => 1)) - ->expression('invalidations', 'invalidations + 1') - ->key(array('tag' => $tag)) - ->execute(); - } - } - catch (\Exception $e) { - $this->catchException($e, 'cache_tags'); - } - } - - /** * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateAll(). */ public function invalidateAll() { @@ -333,70 +301,6 @@ public function garbageCollection() { } /** - * 'Flattens' a tags array into an array of strings. - * - * @param array $tags - * Associative array of tags to flatten. - * - * @return array - * An indexed array of flattened tag identifiers. - */ - protected function flattenTags(array $tags) { - if (isset($tags[0])) { - return $tags; - } - - $flat_tags = array(); - foreach ($tags as $namespace => $values) { - if (is_array($values)) { - foreach ($values as $value) { - $flat_tags[] = "$namespace:$value"; - } - } - else { - $flat_tags[] = "$namespace:$values"; - } - } - return $flat_tags; - } - - /** - * Returns the sum total of validations for a given set of tags. - * - * @param array $tags - * Array of flat tags. - * - * @return int - * Sum of all invalidations. - * - * @see \Drupal\Core\Cache\DatabaseBackend::flattenTags() - */ - protected function checksumTags($flat_tags) { - $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array()); - - $checksum = array( - 'invalidations' => 0, - 'deletions' => 0, - ); - - $query_tags = array_diff($flat_tags, array_keys($tag_cache)); - if ($query_tags) { - $db_tags = $this->connection->query('SELECT tag, invalidations, deletions FROM {cache_tags} WHERE tag IN (:tags)', array(':tags' => $query_tags))->fetchAllAssoc('tag', \PDO::FETCH_ASSOC); - $tag_cache += $db_tags; - - // Fill static cache with empty objects for tags not found in the database. - $tag_cache += array_fill_keys(array_diff($query_tags, array_keys($db_tags)), $checksum); - } - - foreach ($flat_tags as $tag) { - $checksum['invalidations'] += $tag_cache[$tag]['invalidations']; - $checksum['deletions'] += $tag_cache[$tag]['deletions']; - } - - return $checksum; - } - - /** * Implements Drupal\Core\Cache\CacheBackendInterface::isEmpty(). */ public function isEmpty() { diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackendFactory.php b/core/lib/Drupal/Core/Cache/DatabaseBackendFactory.php index 1d70164..d78f757 100644 --- a/core/lib/Drupal/Core/Cache/DatabaseBackendFactory.php +++ b/core/lib/Drupal/Core/Cache/DatabaseBackendFactory.php @@ -19,12 +19,20 @@ class DatabaseBackendFactory implements CacheFactoryInterface { protected $connection; /** + * The cache tag factory service. + * + * @var \Drupal\Core\Cache\CacheTagFactory + */ + protected $cacheTagFactory; + + /** * Constructs the DatabaseBackendFactory object. * * @param \Drupal\Core\Database\Connection $connection */ - function __construct(Connection $connection) { + function __construct(Connection $connection, CacheTagFactory $cache_tag_factory) { $this->connection = $connection; + $this->cacheTagFactory = $cache_tag_factory; } /** @@ -37,7 +45,7 @@ function __construct(Connection $connection) { * The cache backend object for the specified cache bin. */ function get($bin) { - return new DatabaseBackend($this->connection, $bin); + return new DatabaseBackend($this->connection, $this->cacheTagFactory->get(), $bin); } } diff --git a/core/lib/Drupal/Core/Cache/DatabaseTag.php b/core/lib/Drupal/Core/Cache/DatabaseTag.php new file mode 100644 index 0000000..4d05ef9 --- /dev/null +++ b/core/lib/Drupal/Core/Cache/DatabaseTag.php @@ -0,0 +1,153 @@ +connection = $connection; + } + + /** + * {@inheritdoc} + */ + public function invalidateTags(array $tags) { + try { + foreach ($this->flattenTags($tags) as $tag) { + unset($this->tagCache[$tag]); + $this->connection->merge('cache_tags') + ->insertFields(array('invalidations' => 1)) + ->expression('invalidations', 'invalidations + 1') + ->key(array('tag' => $tag)) + ->execute(); + } + } + catch (\Exception $e) { + $this->catchException($e, 'cache_tags'); + } + } + + /** + * {@inheritdoc} + */ + public function deleteTags(array $tags) { + foreach ($this->flattenTags($tags) as $tag) { + unset($this->tagCache[$tag]); + try { + $this->connection->merge('cache_tags') + ->insertFields(array('deletions' => 1)) + ->expression('deletions', 'deletions + 1') + ->key(array('tag' => $tag)) + ->execute(); + } + catch (\Exception $e) { + $this->catchException($e, 'cache_tags'); + } + } + } + + /** + * Returns the sum total of validations for a given set of tags. + * + * @param array $tags + * Array of tags. + * + * @return int + * Sum of all invalidations. + * + * @see Drupal\Core\Cache\DatabaseBackend::flattenTags() + */ + public function checksumTags(array $tags) { + $flat_tags = $this->flattenTags($tags); + $checksum = array( + 'invalidations' => 0, + 'deletions' => 0, + ); + + $query_tags = array_diff($flat_tags, array_keys($this->tagCache)); + if ($query_tags) { + $db_tags = $this->connection->query('SELECT tag, invalidations, deletions FROM {cache_tags} WHERE tag IN (:tags)', array(':tags' => $query_tags))->fetchAllAssoc('tag', \PDO::FETCH_ASSOC); + $this->tagCache += $db_tags; + + // Fill static cache with empty objects for tags not found in the database. + $this->tagCache += array_fill_keys(array_diff($query_tags, array_keys($db_tags)), $checksum); + } + + foreach ($flat_tags as $tag) { + $checksum['invalidations'] += $this->tagCache[$tag]['invalidations']; + $checksum['deletions'] += $this->tagCache[$tag]['deletions']; + } + + return $checksum; + } + + /** + * {@inheritdoc} + */ + public function prepareGet(&$item) { + $checksum = $this->checksumTags($item->tags); + + // Check if deleteTags() has been called with any of the entry's tags. + $item->deleted = $item->checksum_deletions != $checksum['deletions']; + + // Check if invalidateTags() has been called with any of the entry's tags. + if ($item->checksum_invalidations != $checksum['invalidations']) { + $item->valid = FALSE; + } + } + + /** + * Act on an exception when cache might be stale. + * + * If the cache_tags table does not yet exist, that's fine but if the table + * exists and yet the query failed, then the cache is stale and the + * exception needs to propagate. + * + * @param $e + * The exception. + * @param string|null $table_name + * The table name, defaults to $this->bin. Can be cache_tags. + */ + protected function catchException(\Exception $e, $table_name = NULL) { + if ($this->connection->schema()->tableExists($table_name ?: $this->bin)) { + throw $e; + } + } + + /** + * {@inheritdoc}. + */ + public function clearCache() { + $this->tagCache = array(); + } +} diff --git a/core/lib/Drupal/Core/Cache/ListCacheBinsPass.php b/core/lib/Drupal/Core/Cache/ListCacheBinsPass.php index 5381186..2031aed 100644 --- a/core/lib/Drupal/Core/Cache/ListCacheBinsPass.php +++ b/core/lib/Drupal/Core/Cache/ListCacheBinsPass.php @@ -11,20 +11,20 @@ use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; /** - * Adds cache_bins parameter to the container. + * Adds cache_bins and cache_tags parameters to the container. */ class ListCacheBinsPass implements CompilerPassInterface { /** - * Implements CompilerPassInterface::process(). - * - * Collects the cache bins into the cache_bins parameter. + * {@inheritdoc} */ public function process(ContainerBuilder $container) { - $cache_bins = array(); - foreach ($container->findTaggedServiceIds('cache.bin') as $id => $attributes) { - $cache_bins[$id] = substr($id, strpos($id, '.') + 1); + foreach (array('cache.bin' => 'cache_bins', 'cache.tag' => 'cache_tags') as $name => $param) { + $params = array(); + foreach ($container->findTaggedServiceIds($name) as $id => $attributes) { + $params[$id] = substr($id, strpos($id, '.') + 1); + } + $container->setParameter($param, $params); } - $container->setParameter('cache_bins', $cache_bins); } } diff --git a/core/lib/Drupal/Core/Cache/MemoryBackend.php b/core/lib/Drupal/Core/Cache/MemoryBackend.php index a7faca6..9d6596a 100644 --- a/core/lib/Drupal/Core/Cache/MemoryBackend.php +++ b/core/lib/Drupal/Core/Cache/MemoryBackend.php @@ -24,12 +24,20 @@ class MemoryBackend implements CacheBackendInterface { protected $cache = array(); /** + * The cache tag service. + * + * @var \Drupal\Core\Cache\CacheTagInterface + */ + protected $cacheTag; + + /** * Constructs a MemoryBackend object. * * @param string $bin * The cache bin for which the object is created. */ - public function __construct($bin) { + public function __construct(CacheTagInterface $cache_tag, $bin) { + $this->cacheTag = $cache_tag; } /** @@ -83,7 +91,17 @@ protected function prepareItem($cache, $allow_invalid) { } // Check expire time. - $cache->valid = $cache->expire == Cache::PERMANENT || $cache->expire >= REQUEST_TIME; + $cache->valid = $time_valid = $cache->expire == Cache::PERMANENT || $cache->expire >= REQUEST_TIME; + + $this->cacheTag->prepareGet($cache); + if ($cache->deleted) { + $this->delete($cache->cid); + return FALSE; + } + + if ($time_valid && !$cache->valid) { + $this->invalidate($cache->cid); + } if (!$allow_invalid && !$cache->valid) { return FALSE; @@ -96,12 +114,15 @@ protected function prepareItem($cache, $allow_invalid) { * Implements Drupal\Core\Cache\CacheBackendInterface::set(). */ public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) { + $checksum = $this->cacheTag->checksumTags($tags); $this->cache[$cid] = (object) array( 'cid' => $cid, 'data' => $data, 'created' => REQUEST_TIME, 'expire' => $expire, 'tags' => $this->flattenTags($tags), + 'checksum_invalidations' => $checksum['invalidations'], + 'checksum_deletions' => $checksum['deletions'], ); } @@ -120,18 +141,6 @@ public function deleteMultiple(array $cids) { } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::deleteTags(). - */ - public function deleteTags(array $tags) { - $flat_tags = $this->flattenTags($tags); - foreach ($this->cache as $cid => $item) { - if (array_intersect($flat_tags, $item->tags)) { - unset($this->cache[$cid]); - } - } - } - - /** * Implements Drupal\Core\Cache\CacheBackendInterface::deleteAll(). */ public function deleteAll() { @@ -157,18 +166,6 @@ public function invalidateMultiple(array $cids) { } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags(). - */ - public function invalidateTags(array $tags) { - $flat_tags = $this->flattenTags($tags); - foreach ($this->cache as $cid => $item) { - if (array_intersect($flat_tags, $item->tags)) { - $this->cache[$cid]->expire = REQUEST_TIME - 1; - } - } - } - - /** * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateAll(). */ public function invalidateAll() { diff --git a/core/lib/Drupal/Core/Cache/MemoryBackendFactory.php b/core/lib/Drupal/Core/Cache/MemoryBackendFactory.php index c9e288d..917110d 100644 --- a/core/lib/Drupal/Core/Cache/MemoryBackendFactory.php +++ b/core/lib/Drupal/Core/Cache/MemoryBackendFactory.php @@ -10,10 +10,26 @@ class MemoryBackendFactory implements CacheFactoryInterface { /** + * The cache tag factory service. + * + * @var \Drupal\Core\Cache\CacheTagFactory + */ + protected $cacheTagFactory; + + /** + * Constructs the DatabaseBackendFactory object. + * + * @param \Drupal\Core\Database\Connection $connection + */ + function __construct(CacheTagFactory $cache_tag_factory) { + $this->cacheTagFactory = $cache_tag_factory; + } + + /** * {@inheritdoc} */ function get($bin) { - return new MemoryBackend($bin); + return new MemoryBackend($this->cacheTagFactory->get(), $bin); } } diff --git a/core/lib/Drupal/Core/Cache/MemoryTag.php b/core/lib/Drupal/Core/Cache/MemoryTag.php new file mode 100644 index 0000000..b82248b --- /dev/null +++ b/core/lib/Drupal/Core/Cache/MemoryTag.php @@ -0,0 +1,104 @@ +storage[$tag])) { + $this->storage[$tag] = array( + 'invalidations' => 0, + 'deletions' => 0, + ); + } + } + + /** + * {@inheritdoc} + */ + public function invalidateTags(array $tags) { + foreach ($this->flattenTags($tags) as $tag) { + $this->ensureItem($tag); + $this->storage[$tag]['invalidations']++; + } + } + + /** + * {@inheritdoc} + */ + public function deleteTags(array $tags) { + foreach ($this->flattenTags($tags) as $tag) { + $this->ensureItem($tag); + $this->storage[$tag]['deletions']++; + } + } + + /** + * Returns the sum total of validations for a given set of tags. + * + * @param array $tags + * Array of tags. + * + * @return int + * Sum of all invalidations. + * + * @see Drupal\Core\Cache\DatabaseBackend::flattenTags() + */ + public function checksumTags(array $tags) { + $flat_tags = $this->flattenTags($tags); + $checksum = array( + 'invalidations' => 0, + 'deletions' => 0, + ); + + foreach ($flat_tags as $tag) { + $this->ensureItem($tag); + $checksum['invalidations'] += $this->storage[$tag]['invalidations']; + $checksum['deletions'] += $this->storage[$tag]['deletions']; + } + + return $checksum; + } + + /** + * {@inheritdoc} + */ + public function prepareGet(&$item) { + $checksum = $this->checksumTags($item->tags); + + // Check if deleteTags() has been called with any of the entry's tags. + $item->deleted = $item->checksum_deletions != $checksum['deletions']; + + // Check if invalidateTags() has been called with any of the entry's tags. + if ($item->checksum_invalidations != $checksum['invalidations']) { + $item->valid = FALSE; + } + } + + /** + * {@inheritdoc}. + */ + public function clearCache() { + // Nothing to be done here. + } +} diff --git a/core/lib/Drupal/Core/Cache/NullBackend.php b/core/lib/Drupal/Core/Cache/NullBackend.php index 0364663..287d947 100644 --- a/core/lib/Drupal/Core/Cache/NullBackend.php +++ b/core/lib/Drupal/Core/Cache/NullBackend.php @@ -63,11 +63,6 @@ public function deleteMultiple(array $cids) {} public function deleteAll() {} /** - * Implements Drupal\Core\Cache\CacheBackendInterface::deleteTags(). - */ - public function deleteTags(array $tags) {} - - /** * Implements Drupal\Core\Cache\CacheBackendInterface::invalidate(). */ public function invalidate($cid) {} @@ -78,11 +73,6 @@ public function invalidate($cid) {} public function invalidateMultiple(array $cids) {} /** - * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags(). - */ - public function invalidateTags(array $tags) {} - - /** * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateAll(). */ public function invalidateAll() {} diff --git a/core/lib/Drupal/Core/DependencyInjection/UpdateServiceProvider.php b/core/lib/Drupal/Core/DependencyInjection/UpdateServiceProvider.php index 4776214..bcf8c06 100644 --- a/core/lib/Drupal/Core/DependencyInjection/UpdateServiceProvider.php +++ b/core/lib/Drupal/Core/DependencyInjection/UpdateServiceProvider.php @@ -36,7 +36,8 @@ public function register(ContainerBuilder $container) { $container->register('module_handler', 'Drupal\Core\Extension\UpdateModuleHandler') ->addArgument('%container.modules%'); $container - ->register('cache_factory', 'Drupal\Core\Cache\MemoryBackendFactory'); + ->register('cache_factory', 'Drupal\Core\Cache\MemoryBackendFactory') + ->addArgument(new Reference('cache_tag_factory')); $container ->register('router.builder', 'Drupal\Core\Routing\RouteBuilderStatic'); diff --git a/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php b/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php index 086f206..3ee545b 100644 --- a/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php +++ b/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php @@ -13,6 +13,7 @@ use Drupal\field\FieldException; use Drupal\Core\TypedData\DataDefinition; use Drupal\field\FieldInstanceInterface; +use Drupal\field\Field as FieldInfo; /** * Defines the Field instance entity. @@ -249,7 +250,7 @@ public function __construct(array $values, $entity_type = 'field_instance') { // Field instances configuration is stored with a 'field_uuid' property // unambiguously identifying the field. if (isset($values['field_uuid'])) { - $field = field_info_field_by_id($values['field_uuid']); + $field = FieldInfo::fieldInfo()->getFieldById($values['field_uuid']); if (!$field) { throw new FieldException(format_string('Attempt to create an instance of unknown field @uuid', array('@uuid' => $values['field_uuid']))); } diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php index c3fc74e..04064ce 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php @@ -142,7 +142,7 @@ function testStringTranslation() { // Reset the tag cache on the tester side in order to pick up the call to // Cache::deleteTags() on the tested side. - drupal_static_reset('Drupal\Core\Cache\CacheBackendInterface::tagCache'); + $this->container->get('cache.tag.database')->clearCache(); $this->assertTrue($name != $translation && t($name, array(), array('langcode' => $langcode)) == $translation, 't() works for non-English.'); // Refresh the locale() cache to get fresh data from t() below. We are in diff --git a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php index 92b161f..1dcb454 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php @@ -190,6 +190,7 @@ public function containerBuild(ContainerBuilder $container) { $container->register('lock', 'Drupal\Core\Lock\NullLockBackend'); $this->settingsSet('cache', array('default' => 'cache.backend.memory')); + $this->settingsSet('cache_tag_service', 'cache.tag.memory'); $container ->register('config.storage', 'Drupal\Core\Config\FileStorage') diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 3917ffa..334a4a0 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -775,6 +775,7 @@ protected function setUp() { // Execute the non-interactive installer. require_once DRUPAL_ROOT . '/core/includes/install.core.inc'; $this->settingsSet('cache', array('default' => 'cache.backend.memory')); + $this->settingsSet('cache_tag_service', 'cache.tag.memory'); $parameters = $this->installParameters(); install_drupal($parameters); @@ -1004,7 +1005,7 @@ protected function resetAll() { */ protected function refreshVariables() { // Clear the tag cache. - drupal_static_reset('Drupal\Core\Cache\CacheBackendInterface::tagCache'); + $this->container->get('cache.tag.database')->clearCache(); \Drupal::service('config.factory')->reset(); \Drupal::state()->resetCache(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/BackendChainUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Cache/BackendChainUnitTest.php index 3323b1a..c2207d5 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Cache/BackendChainUnitTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Cache/BackendChainUnitTest.php @@ -9,6 +9,7 @@ use Drupal\Core\Cache\BackendChain; use Drupal\Core\Cache\MemoryBackend; +use Drupal\Core\Cache\MemoryTag; /** * Tests BackendChain using GenericCacheBackendUnitTestBase. @@ -27,10 +28,11 @@ protected function createCacheBackend($bin) { $chain = new BackendChain($bin); // We need to create some various backends in the chain. + $cache_tag = new MemoryTag(); $chain - ->appendBackend(new MemoryBackend('foo')) - ->prependBackend(new MemoryBackend('bar')) - ->appendBackend(new MemoryBackend('baz')); + ->appendBackend(new MemoryBackend($cache_tag, 'foo')) + ->prependBackend(new MemoryBackend($cache_tag, 'bar')) + ->appendBackend(new MemoryBackend($cache_tag, 'baz')); return $chain; } diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/DatabaseBackendUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Cache/DatabaseBackendUnitTest.php index 3b607a5..d344d58 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Cache/DatabaseBackendUnitTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Cache/DatabaseBackendUnitTest.php @@ -37,7 +37,7 @@ public static function getInfo() { * A new DatabaseBackend object. */ protected function createCacheBackend($bin) { - return new DatabaseBackend($this->container->get('database'), $bin); + return new DatabaseBackend($this->container->get('database'), $this->container->get('cache_tag_factory')->get(), $bin); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/GenericCacheBackendUnitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Cache/GenericCacheBackendUnitTestBase.php index e558816..a5142db 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Cache/GenericCacheBackendUnitTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Cache/GenericCacheBackendUnitTestBase.php @@ -353,66 +353,6 @@ public function testDeleteMultiple() { } /** - * Tests Drupal\Core\Cache\CacheBackendInterface::deleteTags(). - */ - function testDeleteTags() { - $backend = $this->getCacheBackend(); - - // Create two cache entries with the same tag and tag value. - $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag' => 2)); - $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag' => 2)); - $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.'); - - // Delete test_tag of value 1. This should delete both entries. - $backend->deleteTags(array('test_tag' => 2)); - $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two cache items invalidated after deleting a cache tag.'); - $this->assertFalse($backend->get('test_cid_invalidate1', TRUE) || $backend->get('test_cid_invalidate2', TRUE), 'Two cache items deleted after deleting a cache tag.'); - - // Create two cache entries with the same tag and an array tag value. - $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag' => array(1))); - $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag' => array(1))); - $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.'); - - // Delete test_tag of value 1. This should delete both entries. - $backend->deleteTags(array('test_tag' => array(1))); - $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two cache items invalidated after deleted a cache tag.'); - $this->assertFalse($backend->get('test_cid_invalidate1', TRUE) || $backend->get('test_cid_invalidate2', TRUE), 'Two cache items deleted after deleting a cache tag.'); - - // Create three cache entries with a mix of tags and tag values. - $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag' => array(1))); - $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag' => array(2))); - $backend->set('test_cid_invalidate3', $this->defaultValue, Cache::PERMANENT, array('test_tag_foo' => array(3))); - $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2') && $backend->get('test_cid_invalidate3'), 'Three cached items were created.'); - $backend->deleteTags(array('test_tag_foo' => array(3))); - $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Cached items not matching the tag were not deleted.'); - $this->assertFalse($backend->get('test_cid_invalidated3', TRUE), 'Cache item matching the tag was deleted.'); - - // Create cache entry in multiple bins. Two cache entries - // (test_cid_invalidate1 and test_cid_invalidate2) still exist from previous - // tests. - $tags = array('test_tag' => array(1, 2, 3)); - $bins = array('path', 'bootstrap', 'page'); - foreach ($bins as $bin) { - $this->getCacheBackend($bin)->set('test', $this->defaultValue, Cache::PERMANENT, $tags); - $this->assertTrue($this->getCacheBackend($bin)->get('test'), 'Cache item was set in bin.'); - } - - // Delete tag in mulitple bins. - foreach ($bins as $bin) { - $this->getCacheBackend($bin)->deleteTags(array('test_tag' => array(2))); - } - - // Test that cache entry has been deleted in multple bins. - foreach ($bins as $bin) { - $this->assertFalse($this->getCacheBackend($bin)->get('test', TRUE), 'Tag deletion affected item in bin.'); - } - // Test that the cache entry with a matching tag has been invalidated. - $this->assertFalse($this->getCacheBackend($bin)->get('test_cid_invalidate2', TRUE), 'Cache items matching tag were deleted.'); - // Test that the cache entry with without a matching tag still exists. - $this->assertTrue($this->getCacheBackend($bin)->get('test_cid_invalidate1'), 'Cache items not matching tag were not invalidated.'); - } - - /** * Test Drupal\Core\Cache\CacheBackendInterface::deleteAll(). */ public function testDeleteAll() { @@ -460,66 +400,6 @@ function testInvalidate() { } /** - * Tests Drupal\Core\Cache\CacheBackendInterface::invalidateTags(). - */ - function testInvalidateTags() { - $backend = $this->getCacheBackend(); - - // Create two cache entries with the same tag and tag value. - $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag' => 2)); - $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag' => 2)); - $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.'); - - // Invalidate test_tag of value 1. This should invalidate both entries. - $backend->invalidateTags(array('test_tag' => 2)); - $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two cache items invalidated after invalidating a cache tag.'); - $this->assertTrue($backend->get('test_cid_invalidate1', TRUE) && $backend->get('test_cid_invalidate2', TRUE), 'Cache items not deleted after invalidating a cache tag.'); - - // Create two cache entries with the same tag and an array tag value. - $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag' => array(1))); - $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag' => array(1))); - $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.'); - - // Invalidate test_tag of value 1. This should invalidate both entries. - $backend->invalidateTags(array('test_tag' => array(1))); - $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two caches removed after invalidating a cache tag.'); - $this->assertTrue($backend->get('test_cid_invalidate1', TRUE) && $backend->get('test_cid_invalidate2', TRUE), 'Cache items not deleted after invalidating a cache tag.'); - - // Create three cache entries with a mix of tags and tag values. - $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag' => array(1))); - $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag' => array(2))); - $backend->set('test_cid_invalidate3', $this->defaultValue, Cache::PERMANENT, array('test_tag_foo' => array(3))); - $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2') && $backend->get('test_cid_invalidate3'), 'Three cached items were created.'); - $backend->invalidateTags(array('test_tag_foo' => array(3))); - $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Cache items not matching the tag were not invalidated.'); - $this->assertFalse($backend->get('test_cid_invalidated3'), 'Cached item matching the tag was removed.'); - - // Create cache entry in multiple bins. Two cache entries - // (test_cid_invalidate1 and test_cid_invalidate2) still exist from previous - // tests. - $tags = array('test_tag' => array(1, 2, 3)); - $bins = array('path', 'bootstrap', 'page'); - foreach ($bins as $bin) { - $this->getCacheBackend($bin)->set('test', $this->defaultValue, Cache::PERMANENT, $tags); - $this->assertTrue($this->getCacheBackend($bin)->get('test'), 'Cache item was set in bin.'); - } - - // Invalidate tag in mulitple bins. - foreach ($bins as $bin) { - $this->getCacheBackend($bin)->invalidateTags(array('test_tag' => array(2))); - } - - // Test that cache entry has been invalidated in multple bins. - foreach ($bins as $bin) { - $this->assertFalse($this->getCacheBackend($bin)->get('test'), 'Tag invalidation affected item in bin.'); - } - // Test that the cache entry with a matching tag has been invalidated. - $this->assertFalse($this->getCacheBackend($bin)->get('test_cid_invalidate2'), 'Cache items matching tag were invalidated.'); - // Test that the cache entry with without a matching tag still exists. - $this->assertTrue($this->getCacheBackend($bin)->get('test_cid_invalidate1'), 'Cache items not matching tag were not invalidated.'); - } - - /** * Test Drupal\Core\Cache\CacheBackendInterface::invalidateAll(). */ public function testInvalidateAll() { diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/MemoryBackendUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Cache/MemoryBackendUnitTest.php index d3a29e3..9ae5488 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Cache/MemoryBackendUnitTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Cache/MemoryBackendUnitTest.php @@ -8,6 +8,7 @@ namespace Drupal\system\Tests\Cache; use Drupal\Core\Cache\MemoryBackend; +use Drupal\Core\Cache\MemoryTag; /** * Tests MemoryBackend using GenericCacheBackendUnitTestBase. @@ -29,6 +30,6 @@ public static function getInfo() { * A new MemoryBackend object. */ protected function createCacheBackend($bin) { - return new MemoryBackend($bin); + return new MemoryBackend(new MemoryTag(), $bin); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorTest.php index b305d28..a701027 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorTest.php @@ -9,6 +9,7 @@ use Drupal\Core\Cache\MemoryBackendFactory; use Drupal\Core\Cache\MemoryBackend; +use Drupal\Core\Cache\MemoryTag; use Drupal\system\Tests\Plugin\Discovery\DiscoveryTestBase; use Drupal\Component\Plugin\Discovery\StaticDiscovery; use Drupal\Core\Plugin\Discovery\CacheDecorator; @@ -47,7 +48,7 @@ public function setUp() { // Use a non-db cache backend, so that we can use DiscoveryTestBase (which // extends UnitTestBase). // @todo switch to injecting the MemoryBackend http://drupal.org/node/1903346 - \Drupal::getContainer()->set("cache.$this->cacheBin", new MemoryBackend($this->cacheBin)); + \Drupal::getContainer()->set("cache.$this->cacheBin", new MemoryBackend(new MemoryTag(), $this->cacheBin)); // Create discovery objects to test. $this->emptyDiscovery = new StaticDiscovery(); diff --git a/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php b/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php index 75c0a97..8841720 100644 --- a/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php @@ -10,6 +10,7 @@ use Drupal\Core\Cache\BackendChain; use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\MemoryBackend; +use Drupal\Core\Cache\MemoryTag; use Drupal\Tests\UnitTestCase; /** @@ -59,9 +60,10 @@ public function setUp() { parent::setUp(); // Set up three memory backends to be used in the chain. - $this->firstBackend = new MemoryBackend('foo'); - $this->secondBackend = new MemoryBackend('bar'); - $this->thirdBackend = new MemoryBackend('baz'); + $cache_tag = new MemoryTag(); + $this->firstBackend = new MemoryBackend($cache_tag, 'foo'); + $this->secondBackend = new MemoryBackend($cache_tag, 'bar'); + $this->thirdBackend = new MemoryBackend($cache_tag, 'baz'); // Set an initial fixed dataset for all testing. The next three data // collections will test two edge cases (last backend has the data, and @@ -232,83 +234,6 @@ public function testDeleteAllPropagation() { } /** - * Test that the delete tags operation is propagated to all backends - * in the chain. - */ - public function testDeleteTagsPropagation() { - // Create two cache entries with the same tag and tag value. - $this->chain->set('test_cid_clear1', 'foo', Cache::PERMANENT, array('test_tag' => 2)); - $this->chain->set('test_cid_clear2', 'foo', Cache::PERMANENT, array('test_tag' => 2)); - $this->assertNotSame(FALSE, $this->firstBackend->get('test_cid_clear1') - && $this->firstBackend->get('test_cid_clear2') - && $this->secondBackend->get('test_cid_clear1') - && $this->secondBackend->get('test_cid_clear2') - && $this->thirdBackend->get('test_cid_clear1') - && $this->thirdBackend->get('test_cid_clear2'), - 'Two cache items were created in all backends.'); - - // Invalidate test_tag of value 1. This should invalidate both entries. - $this->chain->deleteTags(array('test_tag' => 2)); - $this->assertSame(FALSE, $this->firstBackend->get('test_cid_clear1') - && $this->firstBackend->get('test_cid_clear2') - && $this->secondBackend->get('test_cid_clear1') - && $this->secondBackend->get('test_cid_clear2') - && $this->thirdBackend->get('test_cid_clear1') - && $this->thirdBackend->get('test_cid_clear2'), - 'Two caches removed from all backends after clearing a cache tag.'); - - // Create two cache entries with the same tag and an array tag value. - $this->chain->set('test_cid_clear1', 'foo', Cache::PERMANENT, array('test_tag' => array(1))); - $this->chain->set('test_cid_clear2', 'foo', Cache::PERMANENT, array('test_tag' => array(1))); - $this->assertNotSame(FALSE, $this->firstBackend->get('test_cid_clear1') - && $this->firstBackend->get('test_cid_clear2') - && $this->secondBackend->get('test_cid_clear1') - && $this->secondBackend->get('test_cid_clear2') - && $this->thirdBackend->get('test_cid_clear1') - && $this->thirdBackend->get('test_cid_clear2'), - 'Two cache items were created in all backends.'); - - // Invalidate test_tag of value 1. This should invalidate both entries. - $this->chain->deleteTags(array('test_tag' => array(1))); - $this->assertSame(FALSE, $this->firstBackend->get('test_cid_clear1') - && $this->firstBackend->get('test_cid_clear2') - && $this->secondBackend->get('test_cid_clear1') - && $this->secondBackend->get('test_cid_clear2') - && $this->thirdBackend->get('test_cid_clear1') - && $this->thirdBackend->get('test_cid_clear2'), - 'Two caches removed from all backends after clearing a cache tag.'); - - // Create three cache entries with a mix of tags and tag values. - $this->chain->set('test_cid_clear1', 'foo', Cache::PERMANENT, array('test_tag' => array(1))); - $this->chain->set('test_cid_clear2', 'foo', Cache::PERMANENT, array('test_tag' => array(2))); - $this->chain->set('test_cid_clear3', 'foo', Cache::PERMANENT, array('test_tag_foo' => array(3))); - $this->assertNotSame(FALSE, $this->firstBackend->get('test_cid_clear1') - && $this->firstBackend->get('test_cid_clear2') - && $this->firstBackend->get('test_cid_clear3') - && $this->secondBackend->get('test_cid_clear1') - && $this->secondBackend->get('test_cid_clear2') - && $this->secondBackend->get('test_cid_clear3') - && $this->thirdBackend->get('test_cid_clear1') - && $this->thirdBackend->get('test_cid_clear2') - && $this->thirdBackend->get('test_cid_clear3'), - 'Three cached items were created in all backends.'); - - $this->chain->deleteTags(array('test_tag_foo' => array(3))); - $this->assertNotSame(FALSE, $this->firstBackend->get('test_cid_clear1') - && $this->firstBackend->get('test_cid_clear2') - && $this->secondBackend->get('test_cid_clear1') - && $this->secondBackend->get('test_cid_clear2') - && $this->thirdBackend->get('test_cid_clear1') - && $this->thirdBackend->get('test_cid_clear2'), - 'Cached items not matching the tag were not cleared from any of the backends.'); - - $this->assertSame(FALSE, $this->firstBackend->get('test_cid_clear3') - && $this->secondBackend->get('test_cid_clear3') - && $this->thirdBackend->get('test_cid_clear3'), - 'Cached item matching the tag was removed from all backends.'); - } - - /** * Test that removing bin propagates to all backends. */ public function testRemoveBin() { diff --git a/core/tests/Drupal/Tests/Core/Cache/CacheCollectorTest.php b/core/tests/Drupal/Tests/Core/Cache/CacheCollectorTest.php index 05a987c..7740523 100644 --- a/core/tests/Drupal/Tests/Core/Cache/CacheCollectorTest.php +++ b/core/tests/Drupal/Tests/Core/Cache/CacheCollectorTest.php @@ -27,6 +27,13 @@ class CacheCollectorTest extends UnitTestCase { protected $cache; /** + * The cache tag backend to use. + * + * @var \Drupal\Core\Cache\CacheTagInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $cacheTag; + + /** * The lock backend that should be used. * * @var \PHPUnit_Framework_MockObject_MockObject @@ -60,11 +67,12 @@ public static function getInfo() { */ protected function setUp() { $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); + $this->cacheTag = $this->getMock('Drupal\Core\Cache\CacheTagInterface'); $this->lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface'); $this->cid = $this->randomName(); $this->collector = new CacheCollectorHelper($this->cid, $this->cache, $this->lock); - $this->getContainerWithCacheBins($this->cache); + $this->getContainerWithCacheTags($this->cacheTag); } @@ -387,7 +395,7 @@ public function testUpdateCacheClear() { $this->cache->expects($this->once()) ->method('delete') ->with($this->cid); - $this->cache->expects($this->never()) + $this->cacheTag->expects($this->never()) ->method('deleteTags'); $this->collector->clear(); $this->assertEquals($value, $this->collector->get($key)); @@ -414,7 +422,7 @@ public function testUpdateCacheClearTags() { // Clear the collected cache using the tags, should call it again. $this->cache->expects($this->never()) ->method('delete'); - $this->cache->expects($this->once()) + $this->cacheTag->expects($this->once()) ->method('deleteTags') ->with($tags); $this->collector->clear(); diff --git a/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php b/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php index 6c60af4..81a6248 100644 --- a/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\Core\Config; +use Drupal\Core\Cache\MemoryTag; use Drupal\Tests\UnitTestCase; use Drupal\Core\Config\CachedStorage; use Drupal\Core\Cache\MemoryBackend; @@ -51,7 +52,7 @@ public function testListAllPrimedPersistentCache() { $storage->expects($this->never())->method('listAll'); $response = array("$prefix." . $this->randomName(), "$prefix." . $this->randomName()); - $cache = new MemoryBackend(__FUNCTION__); + $cache = new MemoryBackend(new MemoryTag(), __FUNCTION__); $cache->set('find:' . $prefix, $response); $cachedStorage = new CachedStorage($storage, $cache); $this->assertEquals($response, $cachedStorage->listAll($prefix)); @@ -75,7 +76,7 @@ public function testGetMultipleOnPrimedCache() { ); $storage = $this->getMock('Drupal\Core\Config\StorageInterface'); $storage->expects($this->never())->method('readMultiple'); - $cache = new MemoryBackend(__FUNCTION__); + $cache = new MemoryBackend(new MemoryTag(), __FUNCTION__); foreach ($configCacheValues as $key => $value) { $cache->set($key, $value); } @@ -102,7 +103,7 @@ public function testGetMultipleOnPartiallyPrimedCache() { 'foo' => 'bar', ), ); - $cache = new MemoryBackend(__FUNCTION__); + $cache = new MemoryBackend(new MemoryTag(), __FUNCTION__); foreach ($configCacheValues as $key => $value) { $cache->set($key, $value); } @@ -137,7 +138,7 @@ public function testGetMultipleOnPartiallyPrimedCache() { */ public function testReadNonExistentFileCacheMiss() { $name = 'config.does_not_exist'; - $cache = new MemoryBackend(__FUNCTION__); + $cache = new MemoryBackend(new MemoryTag(), __FUNCTION__); $storage = $this->getMock('Drupal\Core\Config\StorageInterface'); $storage->expects($this->once()) ->method('read') @@ -157,7 +158,7 @@ public function testReadNonExistentFileCacheMiss() { */ public function testReadNonExistentFileCached() { $name = 'config.does_not_exist'; - $cache = new MemoryBackend(__FUNCTION__); + $cache = new MemoryBackend(new MemoryTag(), __FUNCTION__); $cache->set($name, FALSE); $storage = $this->getMock('Drupal\Core\Config\StorageInterface'); diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php index 4da13aa..b4b5937 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php @@ -66,6 +66,13 @@ class EntityManagerTest extends UnitTestCase { protected $cache; /** + * The cache tag backend to use. + * + * @var \Drupal\Core\Cache\CacheTagInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $cacheTag; + + /** * The language manager. * * @var \Drupal\Core\Language\LanguageManager|\PHPUnit_Framework_MockObject_MockObject @@ -111,6 +118,8 @@ protected function setUp() { $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); + $this->cacheTag = $this->getMock('Drupal\Core\Cache\CacheTagInterface'); + $this->languageManager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager') ->disableOriginalConstructor() ->getMock(); @@ -122,7 +131,7 @@ protected function setUp() { $this->formBuilder = $this->getMock('Drupal\Core\Form\FormBuilderInterface'); - $this->container = $this->getContainerWithCacheBins($this->cache); + $this->container = $this->getContainerWithCacheTags($this->cacheTag); $this->discovery = $this->getMock('Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface'); } @@ -609,7 +618,7 @@ protected function setUpEntityWithFieldDefinition($custom_invoke_all = FALSE, $f */ public function testClearCachedFieldDefinitions() { $this->setUpEntityManager(); - $this->cache->expects($this->once()) + $this->cacheTag->expects($this->once()) ->method('deleteTags') ->with(array('entity_field_info' => TRUE)); diff --git a/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php b/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php index 6efddf7..97116d1 100644 --- a/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php +++ b/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php @@ -44,6 +44,13 @@ class ThemeHandlerTest extends UnitTestCase { protected $cacheBackend; /** + * The mocked cache tag service. + * + * @var \Drupal\Core\Cache\CacheTagInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $cacheTag; + + /** * The mocked config factory. * * @var \Drupal\Core\Config\ConfigFactory|\PHPUnit_Framework_MockObject_MockObject @@ -96,6 +103,7 @@ protected function setUp() { $this->configFactory = $this->getConfigFactoryStub(array('system.theme' => array(), 'system.theme.disabled' => array())); $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); + $this->cacheTag = $this->getMock('Drupal\Core\Cache\CacheTagInterface'); $this->infoParser = $this->getMock('Drupal\Core\Extension\InfoParserInterface'); $this->configInstaller = $this->getMock('Drupal\Core\Config\ConfigInstallerInterface'); $this->routeBuilder = $this->getMockBuilder('Drupal\Core\Routing\RouteBuilder') @@ -106,7 +114,7 @@ protected function setUp() { ->getMock(); $this->themeHandler = new TestThemeHandler($this->configFactory, $this->moduleHandler, $this->cacheBackend, $this->infoParser, $this->configInstaller, $this->routeBuilder, $this->systemListingInfo); - $this->getContainerWithCacheBins($this->cacheBackend); + $this->getContainerWithCacheTags($this->cacheTag); } /** diff --git a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php index 894a6e1..cf4692e 100644 --- a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php @@ -161,18 +161,23 @@ public function testDefaultPluginManagerWithFilledCache() { */ public function testCacheClearWithTags() { $cid = $this->randomName(); - $cache_backend = $this->getMockBuilder('Drupal\Core\Cache\MemoryBackend') + $memory_tag = $this->getMockBuilder('Drupal\Core\Cache\MemoryTag') ->disableOriginalConstructor() ->getMock(); - $cache_backend + $memory_tag ->expects($this->once()) ->method('deleteTags') ->with(array('tag' => TRUE)); + + $cache_backend = $this->getMockBuilder('Drupal\Core\Cache\MemoryBackend') + ->setConstructorArgs(array($memory_tag)) + ->disableOriginalConstructor() + ->getMock(); $cache_backend ->expects($this->never()) ->method('deleteMultiple'); - $this->getContainerWithCacheBins($cache_backend); + $this->getContainerWithCacheTags($memory_tag); $language = new Language(array('id' => 'en')); $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); diff --git a/core/tests/Drupal/Tests/UnitTestCase.php b/core/tests/Drupal/Tests/UnitTestCase.php index a76611c..6c239d6 100644 --- a/core/tests/Drupal/Tests/UnitTestCase.php +++ b/core/tests/Drupal/Tests/UnitTestCase.php @@ -10,6 +10,7 @@ use Drupal\Component\Utility\Random; use Drupal\Component\Utility\String; use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Cache\CacheTagInterface; use Drupal\Core\DependencyInjection\ContainerBuilder; /** @@ -200,25 +201,24 @@ public function getStringTranslationStub() { /** * Sets up a container with cache bins. * - * @param \Drupal\Core\Cache\CacheBackendInterface $backend - * The cache backend to set up. + * @param \Drupal\Core\Cache\CacheTagInterface $backend + * The cache tag to set up. * * @return \Symfony\Component\DependencyInjection\ContainerInterface|\PHPUnit_Framework_MockObject_MockObject * The container with the cache bins set up. */ - protected function getContainerWithCacheBins(CacheBackendInterface $backend) { + protected function getContainerWithCacheTags(CacheTagInterface $backend) { $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); $container->expects($this->any()) ->method('getParameter') - ->with('cache_bins') - ->will($this->returnValue(array('cache.test' => 'test'))); + ->with('cache_tags') + ->will($this->returnValue(array('cache.tag.test' => 'test'))); $container->expects($this->any()) ->method('get') - ->with('cache.test') + ->with('cache.tag.test') ->will($this->returnValue($backend)); \Drupal::setContainer($container); return $container; } - }