core/includes/common.inc | 62 ++------- core/includes/module.inc | 2 +- core/lib/Drupal/Core/Access/AccessManager.php | 2 +- core/lib/Drupal/Core/Access/AccessResult.php | 15 +-- .../Core/Asset/LibraryDiscoveryCollector.php | 2 +- core/lib/Drupal/Core/Block/BlockBase.php | 3 +- core/lib/Drupal/Core/Cache/ApcuBackend.php | 38 +----- core/lib/Drupal/Core/Cache/Cache.php | 78 +++++++++++- core/lib/Drupal/Core/Cache/CacheCollector.php | 3 +- core/lib/Drupal/Core/Cache/DatabaseBackend.php | 64 +++------- core/lib/Drupal/Core/Cache/MemoryBackend.php | 40 ++---- core/lib/Drupal/Core/Cache/PhpBackend.php | 37 +----- core/lib/Drupal/Core/Config/CachedStorage.php | 8 +- .../Drupal/Core/Config/Entity/ConfigEntityBase.php | 2 +- .../lib/Drupal/Core/Datetime/Entity/DateFormat.php | 4 +- core/lib/Drupal/Core/Entity/Entity.php | 9 +- core/lib/Drupal/Core/Entity/EntityManager.php | 20 +-- core/lib/Drupal/Core/Entity/EntityViewBuilder.php | 9 +- .../Core/Entity/Sql/SqlContentEntityStorage.php | 6 +- .../Core/EventSubscriber/HtmlViewSubscriber.php | 58 +-------- .../MenuRouterRebuildSubscriber.php | 2 +- core/lib/Drupal/Core/Extension/ThemeHandler.php | 2 +- .../lib/Drupal/Core/Menu/ContextualLinkManager.php | 2 +- core/lib/Drupal/Core/Menu/LocalActionManager.php | 2 +- core/lib/Drupal/Core/Menu/LocalTaskManager.php | 2 +- core/lib/Drupal/Core/Menu/MenuLinkTree.php | 4 +- core/lib/Drupal/Core/Menu/MenuTreeStorage.php | 10 +- .../Core/Page/DefaultHtmlFragmentRenderer.php | 16 ++- .../Drupal/Core/Plugin/DefaultPluginManager.php | 1 + core/lib/Drupal/Core/Render/Element/Page.php | 4 +- core/lib/Drupal/Core/Theme/Registry.php | 8 +- core/lib/Drupal/Core/Theme/ThemeAccessCheck.php | 2 +- core/lib/Drupal/Core/Utility/Token.php | 2 +- .../src/Plugin/Block/AggregatorFeedBlock.php | 5 +- .../src/Tests/AggregatorRenderingTest.php | 8 +- core/modules/block/src/BlockViewBuilder.php | 7 +- core/modules/block/src/Entity/Block.php | 2 +- core/modules/block/src/Tests/BlockCacheTest.php | 2 +- core/modules/block/src/Tests/BlockTest.php | 29 +++-- .../block/src/Tests/BlockViewBuilderTest.php | 17 +-- .../tests/modules/block_test/block_test.module | 3 +- core/modules/book/src/BookManager.php | 12 +- core/modules/breakpoint/src/BreakpointManager.php | 6 +- .../comment/src/Tests/CommentCacheTagsTest.php | 2 +- .../Tests/CommentDefaultFormatterCacheTagsTest.php | 22 ++-- .../tests/src/Unit/Entity/CommentLockTest.php | 4 +- .../config_translation/src/ConfigMapperManager.php | 2 +- .../ContentTranslationManageAccessCheckTest.php | 2 +- .../src/Tests/EditorFileReferenceFilterTest.php | 13 +- .../src/Tests/EntityReferenceFormatterTest.php | 15 ++- core/modules/filter/filter.module | 2 +- core/modules/filter/src/Element/ProcessedText.php | 13 +- core/modules/filter/src/FilterPluginManager.php | 2 +- core/modules/filter/src/FilterProcessResult.php | 3 +- core/modules/filter/src/Tests/FilterAPITest.php | 10 +- .../src/Plugin/Filter/FilterTestCacheTags.php | 4 +- core/modules/locale/locale.module | 2 +- core/modules/locale/src/LocaleLookup.php | 2 +- .../menu_ui/src/Tests/MenuCacheTagsTest.php | 8 +- core/modules/node/src/Tests/NodeCacheTagsTest.php | 2 +- .../FieldFormatter/ResponsiveImageFormatter.php | 10 +- .../rest/src/LinkManager/RelationLinkManager.php | 2 +- .../rest/src/LinkManager/TypeLinkManager.php | 2 +- core/modules/shortcut/shortcut.module | 8 +- .../shortcut/src/Tests/ShortcutCacheTagsTest.php | 2 +- core/modules/system/core.api.php | 4 +- core/modules/system/src/Form/ThemeSettingsForm.php | 4 +- .../src/Plugin/Block/SystemBrandingBlock.php | 8 +- .../system/src/Plugin/Block/SystemMenuBlock.php | 6 +- .../system/src/Tests/Bootstrap/PageCacheTest.php | 10 +- .../src/Tests/Cache/DatabaseBackendTagTest.php | 10 +- .../Cache/GenericCacheBackendUnitTestBase.php | 73 +++++++---- .../Tests/Cache/PageCacheTagsIntegrationTest.php | 16 +-- .../modules/system/src/Tests/Common/RenderTest.php | 31 ++--- .../src/Tests/Entity/EntityCacheTagsTestBase.php | 35 +++--- .../Entity/EntityWithUriCacheTagsTestBase.php | 14 +-- .../system/src/Tests/Theme/RegistryTest.php | 4 +- .../src/Controller/SystemTestController.php | 4 +- .../text/src/Tests/Formatter/TextFormatterTest.php | 6 +- .../toolbar/src/Tests/ToolbarAdminMenuTest.php | 16 +-- core/modules/toolbar/toolbar.module | 2 +- core/modules/tour/src/Tests/TourCacheTagsTest.php | 8 +- core/modules/user/src/Entity/Role.php | 1 + core/modules/user/src/PermissionsHash.php | 3 +- .../views/src/Plugin/ViewsHandlerManager.php | 2 +- .../views/src/Plugin/ViewsPluginManager.php | 2 +- .../src/Plugin/views/cache/CachePluginBase.php | 6 +- .../src/Plugin/views/display/DisplayPluginBase.php | 2 +- core/modules/views/src/ViewsData.php | 4 +- core/modules/views/views.api.php | 2 +- .../Drupal/Tests/Core/Access/AccessManagerTest.php | 2 +- .../Drupal/Tests/Core/Access/AccessResultTest.php | 51 ++++---- .../Core/Asset/LibraryDiscoveryCollectorTest.php | 2 +- .../Cache/BackendChainImplementationUnitTest.php | 20 +-- .../Drupal/Tests/Core/Cache/CacheCollectorTest.php | 2 +- core/tests/Drupal/Tests/Core/Cache/CacheTest.php | 139 +++++++++++++++++++++ .../Config/Entity/ConfigEntityBaseUnitTest.php | 2 +- .../Core/Config/Entity/ConfigEntityStorageTest.php | 17 +-- .../Drupal/Tests/Core/Entity/EntityManagerTest.php | 16 +-- .../Drupal/Tests/Core/Entity/EntityUnitTest.php | 10 +- .../Entity/Sql/SqlContentEntityStorageTest.php | 2 +- .../Tests/Core/Menu/LocalActionManagerTest.php | 2 +- .../Tests/Core/Menu/LocalTaskIntegrationTest.php | 2 +- .../Tests/Core/Menu/LocalTaskManagerTest.php | 4 +- .../Tests/Core/Plugin/DefaultPluginManagerTest.php | 4 +- 105 files changed, 656 insertions(+), 634 deletions(-) diff --git a/core/includes/common.inc b/core/includes/common.inc index 3b6f1dd..70f8438 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -29,7 +29,6 @@ use Drupal\Core\PhpStorage\PhpStorageFactory; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Datetime\DrupalDateTime; -use Drupal\Core\EventSubscriber\HtmlViewSubscriber; use Drupal\Core\Routing\GeneratorNotInitializedException; use Drupal\Core\Template\Attribute; use Drupal\Core\Render\Element; @@ -1769,41 +1768,6 @@ function drupal_merge_attached(array $a, array $b) { } /** - * Merges sets of cache tags. - * - * The cache tags array is returned in a format that is valid for - * \Drupal\Core\Cache\CacheBackendInterface::set(). - * - * When caching elements, it is necessary to collect all cache tags into a - * single array, from both the element itself and all child elements. This - * allows items to be invalidated based on all tags attached to the content - * they're constituted from. - * - * @param array $tags - * The first set of cache tags. - * @param array $other - * The other set of cache tags. - * - * @return array - * The merged set of cache tags. - */ -function drupal_merge_cache_tags(array $tags, array $other) { - foreach ($other as $namespace => $values) { - if (is_array($values)) { - foreach ($values as $value) { - $tags[$namespace][$value] = $value; - } - } - else { - if (!isset($tags[$namespace])) { - $tags[$namespace] = $values; - } - } - } - return $tags; -} - -/** * Adds attachments to a render() structure. * * Libraries, JavaScript, CSS and other types of custom structures are attached @@ -2303,7 +2267,7 @@ function drupal_page_set_cache(Response $response, Request $request) { $expire = ($date > (new DateTime())) ? $date->getTimestamp() : Cache::PERMANENT; $cid = drupal_page_cache_get_cid($request); - $tags = HtmlViewSubscriber::convertHeaderToCacheTags($response->headers->get('X-Drupal-Cache-Tags')); + $tags = explode(' ', $response->headers->get('X-Drupal-Cache-Tags')); \Drupal::cache('render')->set($cid, $response, $expire, $tags); } @@ -2494,11 +2458,11 @@ function drupal_prepare_page($page) { // @todo Remove this once https://drupal.org/node/1869476 lands. if (theme_get_setting('features.main_menu') && count(menu_main_menu())) { $main_links_source = _menu_get_links_source('main_links', 'main'); - $page['page_top']['#cache']['tags']['menu'][$main_links_source] = $main_links_source; + $page['page_top']['#cache']['tags'][] = 'menu:' . $main_links_source; } if (theme_get_setting('features.secondary_menu') && count(menu_secondary_menu())) { $secondary_links_source = _menu_get_links_source('secondary_links', 'account'); - $page['page_top']['#cache']['tags']['menu'][$secondary_links_source] = $secondary_links_source; + $page['page_top']['#cache']['tags'][] = 'menu:' . $secondary_links_source; } // If no module has taken care of the main content, add it to the page now. @@ -2710,7 +2674,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) { $frame = $stack->top(); // Update the frame, but also update the current element, to ensure it // contains up-to-date information in case it gets render cached. - $frame->tags = $element['#cache']['tags'] = drupal_merge_cache_tags($element['#cache']['tags'], $frame->tags); + $frame->tags = $element['#cache']['tags'] = Cache::mergeTags($element['#cache']['tags'], $frame->tags); $frame->attached = $element['#attached'] = drupal_merge_attached($element['#attached'], $frame->attached); $frame->postRenderCache = $element['#post_render_cache'] = NestedArray::mergeDeep($element['#post_render_cache'], $frame->postRenderCache); }; @@ -2726,7 +2690,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) { // Merge the current and the parent stack frame. $current = $stack->pop(); $parent = $stack->pop(); - $current->tags = drupal_merge_cache_tags($current->tags, $parent->tags); + $current->tags = Cache::mergeTags($current->tags, $parent->tags); $current->attached = drupal_merge_attached($current->attached, $parent->attached); $current->postRenderCache = NestedArray::mergeDeep($current->postRenderCache, $parent->postRenderCache); $stack->push($current); @@ -2943,7 +2907,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) { $stack->push(new RenderStackFrame()); _drupal_render_process_post_render_cache($elements); $post_render_additions = $stack->pop(); - $elements['#cache']['tags'] = drupal_merge_cache_tags($elements['#cache']['tags'], $post_render_additions->tags); + $elements['#cache']['tags'] = Cache::mergeTags($elements['#cache']['tags'], $post_render_additions->tags); $elements['#attached'] = drupal_merge_attached($elements['#attached'], $post_render_additions->attached); $elements['#post_render_cache'] = NestedArray::mergeDeep($elements['#post_render_cache'], $post_render_additions->postRenderCache); } @@ -3136,17 +3100,15 @@ function drupal_render_cache_set(&$markup, array $elements) { $data['#post_render_cache'] = $elements['#post_render_cache']; } - // Tag every render cache item with the "rendered" cache tag. This allows us - // to invalidate the entire render cache, regardless of the cache bin. - $cache_tags = $elements['#cache']['tags'] ?: array(); - $cache_tags['rendered'] = TRUE; - - // Persist cache tags associated with this element. - $data['#cache']['tags'] = $cache_tags; + // Persist cache tags associated with this element. Also associate the + // "rendered" cache tag. This allows us to invalidate the entire render cache, + // regardless of the cache bin. + $data['#cache']['tags'] = $elements['#cache']['tags']; + $data['#cache']['tags'][] = 'rendered'; $bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'render'; $expire = isset($elements['#cache']['expire']) ? $elements['#cache']['expire'] : Cache::PERMANENT; - \Drupal::cache($bin)->set($cid, $data, $expire, $cache_tags); + \Drupal::cache($bin)->set($cid, $data, $expire, $data['#cache']['tags']); } /** diff --git a/core/includes/module.inc b/core/includes/module.inc index 2b668cb..ac19a62 100644 --- a/core/includes/module.inc +++ b/core/includes/module.inc @@ -69,7 +69,7 @@ function system_list_reset() { // @todo Trigger an event upon module install/uninstall and theme // enable/disable, and move this into an event subscriber. // @see https://drupal.org/node/2206347 - Cache::invalidateTags(array('extension' => TRUE)); + Cache::invalidateTags(array('extension')); } /** diff --git a/core/lib/Drupal/Core/Access/AccessManager.php b/core/lib/Drupal/Core/Access/AccessManager.php index 44af37e..a68cf26 100644 --- a/core/lib/Drupal/Core/Access/AccessManager.php +++ b/core/lib/Drupal/Core/Access/AccessManager.php @@ -194,7 +194,7 @@ public function checkNamedRoute($route_name, array $parameters = array(), Accoun } catch (RouteNotFoundException $e) { // Cacheable until extensions change. - $result = AccessResult::forbidden()->addCacheTags(array('extension' => TRUE)); + $result = AccessResult::forbidden()->addCacheTags(array('extension')); return $return_as_object ? $result : $result->isAllowed(); } catch (ParamNotConvertedException $e) { diff --git a/core/lib/Drupal/Core/Access/AccessResult.php b/core/lib/Drupal/Core/Access/AccessResult.php index 9180435..528d39b 100644 --- a/core/lib/Drupal/Core/Access/AccessResult.php +++ b/core/lib/Drupal/Core/Access/AccessResult.php @@ -259,20 +259,7 @@ public function resetCacheContexts() { * @return $this */ public function addCacheTags(array $tags) { - foreach ($tags as $namespace => $values) { - if (is_array($values)) { - foreach ($values as $value) { - $this->tags[$namespace][$value] = $value; - } - ksort($this->tags[$namespace]); - } - else { - if (!isset($this->tags[$namespace])) { - $this->tags[$namespace] = $values; - } - } - } - ksort($this->tags); + $this->tags = Cache::mergeTags($this->tags, $tags); return $this; } diff --git a/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php b/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php index e174a17..dfac4fa 100644 --- a/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php +++ b/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php @@ -57,7 +57,7 @@ class LibraryDiscoveryCollector extends CacheCollector { * The library discovery parser. */ public function __construct(CacheBackendInterface $cache, LockBackendInterface $lock, LibraryDiscoveryParser $discovery_parser) { - parent::__construct($this->cacheKey, $cache, $lock, array($this->cacheKey => array(TRUE))); + parent::__construct($this->cacheKey, $cache, $lock, array($this->cacheKey)); $this->discoveryParser = $discovery_parser; } diff --git a/core/lib/Drupal/Core/Block/BlockBase.php b/core/lib/Drupal/Core/Block/BlockBase.php index 7db11f2..26ebe4f 100644 --- a/core/lib/Drupal/Core/Block/BlockBase.php +++ b/core/lib/Drupal/Core/Block/BlockBase.php @@ -482,8 +482,7 @@ public function getCacheTags() { // If a block plugin's output changes, then it must be able to invalidate a // cache tag that affects all instances of this block: across themes and // across regions. - $block_plugin_cache_tag = str_replace(':', '__', $this->getPluginID()); - return array('block_plugin' => array($block_plugin_cache_tag)); + return array('block_plugin:' . str_replace(':', '__', $this->getPluginID())); } /** diff --git a/core/lib/Drupal/Core/Cache/ApcuBackend.php b/core/lib/Drupal/Core/Cache/ApcuBackend.php index 0ec166f..50fb0fd 100644 --- a/core/lib/Drupal/Core/Cache/ApcuBackend.php +++ b/core/lib/Drupal/Core/Cache/ApcuBackend.php @@ -189,11 +189,13 @@ protected function prepareItem($cache, $allow_invalid) { * {@inheritdoc} */ public function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = array()) { + Cache::validateTags($tags); + $tags = array_unique($tags); $cache = new \stdClass(); $cache->cid = $cid; $cache->created = round(microtime(TRUE), 3); $cache->expire = $expire; - $cache->tags = implode(' ', $this->flattenTags($tags)); + $cache->tags = implode(' ', $tags); $checksum = $this->checksumTags($tags); $cache->checksum_invalidations = $checksum['invalidations']; $cache->checksum_deletions = $checksum['deletions']; @@ -285,7 +287,7 @@ public function invalidateAll() { * {@inheritdoc} */ public function deleteTags(array $tags) { - foreach ($this->flattenTags($tags) as $tag) { + foreach ($tags as $tag) { apc_inc($this->deletionsTagsPrefix . $tag, 1, $success); if (!$success) { apc_store($this->deletionsTagsPrefix . $tag, 1); @@ -297,7 +299,7 @@ public function deleteTags(array $tags) { * {@inheritdoc} */ public function invalidateTags(array $tags) { - foreach ($this->flattenTags($tags) as $tag) { + foreach ($tags as $tag) { apc_inc($this->invalidationsTagsPrefix . $tag, 1, $success); if (!$success) { apc_store($this->invalidationsTagsPrefix . $tag, 1); @@ -306,34 +308,6 @@ public function invalidateTags(array $tags) { } /** - * Flattens a tags array into a numeric array suitable for string storage. - * - * @param array $tags - * Associative array of tags to flatten. - * - * @return array - * 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 @@ -346,7 +320,7 @@ protected function checksumTags(array $tags) { $checksum = array('invalidations' => 0, 'deletions' => 0); $query_tags = array('invalidations' => array(), 'deletions' => array()); - foreach ($this->flattenTags($tags) as $tag) { + foreach ($tags as $tag) { foreach (array('deletions', 'invalidations') as $type) { if (isset(static::$tagCache[$type][$tag])) { $checksum[$type] += static::$tagCache[$type][$tag]; diff --git a/core/lib/Drupal/Core/Cache/Cache.php b/core/lib/Drupal/Core/Cache/Cache.php index ad59409..ddf444e 100644 --- a/core/lib/Drupal/Core/Cache/Cache.php +++ b/core/lib/Drupal/Core/Cache/Cache.php @@ -22,6 +22,78 @@ class Cache { const PERMANENT = CacheBackendInterface::CACHE_PERMANENT; /** + * Merges arrays of cache tags and removes duplicates. + * + * The cache tags array is returned in a format that is valid for + * \Drupal\Core\Cache\CacheBackendInterface::set(). + * + * When caching elements, it is necessary to collect all cache tags into a + * single array, from both the element itself and all child elements. This + * allows items to be invalidated based on all tags attached to the content + * they're constituted from. + * + * @param string[] … + * Arrays of cache tags to merge. + * + * @return string[] + * The merged array of cache tags. + */ + public static function mergeTags() { + $cache_tag_arrays = func_get_args(); + $cache_tags = []; + foreach ($cache_tag_arrays as $tags) { + static::validateTags($tags); + $cache_tags = array_merge($cache_tags, $tags); + } + $cache_tags = array_unique($cache_tags); + sort($cache_tags); + return $cache_tags; + } + + /** + * Validates an array of cache tags. + * + * Can be called before using cache tags in operations, to ensure validity. + * + * @param string[] $tags + * An array of cache tags. + * + * @throws \LogicException + */ + public static function validateTags(array $tags) { + if (empty($tags)) { + return; + } + foreach ($tags as $value) { + if (!is_string($value)) { + throw new \LogicException('Cache tags must be strings, ' . gettype($value) . ' given.'); + } + } + } + + /** + * Build an array of cache tags from a given prefix and an array of suffixes. + * + * Each suffix will be converted to a cache tag by appending it to the prefix, + * with a colon between them. + * + * @param string $prefix + * A prefix string. + * @param array $suffixes + * An array of suffixes. Will be cast to strings. + * + * @return string[] + * An array of cache tags. + */ + public static function buildTags($prefix, array $suffixes) { + $tags = []; + foreach ($suffixes as $suffix) { + $tags[] = $prefix . ':' . $suffix; + } + return $tags; + } + + /** * Deletes items from all bins with any of the specified tags. * * Many sites have more than one active cache backend, and each backend may @@ -31,10 +103,11 @@ class Cache { * When deleting a given list of tags, we iterate over each cache backend, and * and call deleteTags() on each. * - * @param array $tags + * @param string[] $tags * The list of tags to delete cache items for. */ public static function deleteTags(array $tags) { + static::validateTags($tags); foreach (static::getBins() as $cache_backend) { $cache_backend->deleteTags($tags); } @@ -50,10 +123,11 @@ public static function deleteTags(array $tags) { * When invalidating a given list of tags, we iterate over each cache backend, * and call invalidateTags() on each. * - * @param array $tags + * @param string[] $tags * The list of tags to invalidate cache items for. */ public static function invalidateTags(array $tags) { + static::validateTags($tags); foreach (static::getBins() as $cache_backend) { $cache_backend->invalidateTags($tags); } diff --git a/core/lib/Drupal/Core/Cache/CacheCollector.php b/core/lib/Drupal/Core/Cache/CacheCollector.php index ab261eb..d80da7d 100644 --- a/core/lib/Drupal/Core/Cache/CacheCollector.php +++ b/core/lib/Drupal/Core/Cache/CacheCollector.php @@ -114,7 +114,8 @@ * @param array $tags * (optional) The tags to specify for the cache item. */ - public function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, $tags = array()) { + public function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, array $tags = array()) { + Cache::validateTags($tags); $this->cid = $cid; $this->cache = $cache; $this->tags = $tags; diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php index 448c636..1e24968 100644 --- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php +++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php @@ -147,6 +147,10 @@ protected function prepareItem($cache, $allow_invalid) { * Implements Drupal\Core\Cache\CacheBackendInterface::set(). */ public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) { + Cache::validateTags($tags); + $tags = array_unique($tags); + // Sort the cache tags so that they are stored consistently in the database. + sort($tags); $try_again = FALSE; try { // The bin might not yet exist. @@ -170,13 +174,12 @@ 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); $deleted_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::deletedTags', array()); $invalidated_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::invalidatedTags', array()); // Remove tags that were already deleted or invalidated during this request // from the static caches so that another deletion or invalidation can // occur. - foreach ($flat_tags as $tag) { + foreach ($tags as $tag) { if (isset($deleted_tags[$tag])) { unset($deleted_tags[$tag]); } @@ -184,12 +187,12 @@ protected function doSet($cid, $data, $expire, $tags) { unset($invalidated_tags[$tag]); } } - $checksum = $this->checksumTags($flat_tags); + $checksum = $this->checksumTags($tags); $fields = array( 'serialized' => 0, 'created' => round(microtime(TRUE), 3), 'expire' => $expire, - 'tags' => implode(' ', $flat_tags), + 'tags' => implode(' ', $tags), 'checksum_invalidations' => $checksum['invalidations'], 'checksum_deletions' => $checksum['deletions'], ); @@ -234,12 +237,15 @@ public function setMultiple(array $items) { 'tags' => array(), ); - $flat_tags = $this->flattenTags($item['tags']); + Cache::validateTags($item['tags']); + $item['tags'] = array_unique($item['tags']); + // Sort the cache tags so that they are stored consistently in the DB. + sort($item['tags']); // Remove tags that were already deleted or invalidated during this // request from the static caches so that another deletion or // invalidation can occur. - foreach ($flat_tags as $tag) { + foreach ($item['tags'] as $tag) { if (isset($deleted_tags[$tag])) { unset($deleted_tags[$tag]); } @@ -248,13 +254,13 @@ public function setMultiple(array $items) { } } - $checksum = $this->checksumTags($flat_tags); + $checksum = $this->checksumTags($item['tags']); $fields = array( 'cid' => $cid, 'expire' => $item['expire'], 'created' => round(microtime(TRUE), 3), - 'tags' => implode(' ', $flat_tags), + 'tags' => implode(' ', $item['tags']), 'checksum_invalidations' => $checksum['invalidations'], 'checksum_deletions' => $checksum['deletions'], ); @@ -316,7 +322,7 @@ public function deleteMultiple(array $cids) { public function deleteTags(array $tags) { $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array()); $deleted_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::deletedTags', array()); - foreach ($this->flattenTags($tags) as $tag) { + foreach ($tags as $tag) { // Only delete tags once per request unless they are written again. if (isset($deleted_tags[$tag])) { continue; @@ -386,7 +392,7 @@ public function invalidateTags(array $tags) { try { $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array()); $invalidated_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::invalidatedTags', array()); - foreach ($this->flattenTags($tags) as $tag) { + foreach ($tags as $tag) { // Only invalidate tags once per request unless they are written again. if (isset($invalidated_tags[$tag])) { continue; @@ -437,45 +443,15 @@ 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. + * Array of cache tags. * * @return int * Sum of all invalidations. - * - * @see \Drupal\Core\Cache\DatabaseBackend::flattenTags() */ - protected function checksumTags($flat_tags) { + protected function checksumTags(array $tags) { $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array()); $checksum = array( @@ -483,7 +459,7 @@ protected function checksumTags($flat_tags) { 'deletions' => 0, ); - $query_tags = array_diff($flat_tags, array_keys($tag_cache)); + $query_tags = array_diff($tags, array_keys($tag_cache)); if ($query_tags) { $db_tags = $this->connection->query('SELECT tag, invalidations, deletions FROM {cachetags} WHERE tag IN (:tags)', array(':tags' => $query_tags))->fetchAllAssoc('tag', \PDO::FETCH_ASSOC); $tag_cache += $db_tags; @@ -492,7 +468,7 @@ protected function checksumTags($flat_tags) { $tag_cache += array_fill_keys(array_diff($query_tags, array_keys($db_tags)), $checksum); } - foreach ($flat_tags as $tag) { + foreach ($tags as $tag) { $checksum['invalidations'] += $tag_cache[$tag]['invalidations']; $checksum['deletions'] += $tag_cache[$tag]['deletions']; } diff --git a/core/lib/Drupal/Core/Cache/MemoryBackend.php b/core/lib/Drupal/Core/Cache/MemoryBackend.php index ce8e199..23fddc5 100644 --- a/core/lib/Drupal/Core/Cache/MemoryBackend.php +++ b/core/lib/Drupal/Core/Cache/MemoryBackend.php @@ -107,12 +107,16 @@ protected function prepareItem($cache, $allow_invalid) { * Implements Drupal\Core\Cache\CacheBackendInterface::set(). */ public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) { + Cache::validateTags($tags); + $tags = array_unique($tags); + // Sort the cache tags so that they are stored consistently in the database. + sort($tags); $this->cache[$cid] = (object) array( 'cid' => $cid, 'data' => serialize($data), 'created' => REQUEST_TIME, 'expire' => $expire, - 'tags' => $this->flattenTags($tags), + 'tags' => $tags, ); } @@ -143,9 +147,8 @@ 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)) { + if (array_intersect($tags, $item->tags)) { unset($this->cache[$cid]); } } @@ -180,9 +183,8 @@ 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)) { + if (array_intersect($tags, $item->tags)) { $this->cache[$cid]->expire = REQUEST_TIME - 1; } } @@ -198,34 +200,6 @@ public function invalidateAll() { } /** - * 'Flattens' a tags array into an array of strings. - * - * @param array $tags - * Associative array of tags to flatten. - * - * @return array - * An indexed array of strings. - */ - 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; - } - - /** * Implements Drupal\Core\Cache\CacheBackendInterface::garbageCollection() */ public function garbageCollection() { diff --git a/core/lib/Drupal/Core/Cache/PhpBackend.php b/core/lib/Drupal/Core/Cache/PhpBackend.php index 8613859..e188ffb 100644 --- a/core/lib/Drupal/Core/Cache/PhpBackend.php +++ b/core/lib/Drupal/Core/Cache/PhpBackend.php @@ -133,13 +133,14 @@ protected function prepareItem($cache, $allow_invalid) { * {@inheritdoc} */ public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) { + Cache::validateTags($tags); $item = (object) array( 'cid' => $cid, 'data' => $data, 'created' => round(microtime(TRUE), 3), 'expire' => $expire, + 'tags' => array_unique($tags), ); - $item->tags = $this->flattenTags($tags); $this->writeItem($this->normalizeCid($cid), $item); } @@ -163,10 +164,9 @@ public function deleteMultiple(array $cids) { * {@inheritdoc} */ public function deleteTags(array $tags) { - $flat_tags = $this->flattenTags($tags); foreach ($this->storage()->listAll() as $cidhash) { $item = $this->getByHash($cidhash); - if (is_object($item) && array_intersect($flat_tags, $item->tags)) { + if (is_object($item) && array_intersect($tags, $item->tags)) { $this->delete($item->cid); } } @@ -212,10 +212,9 @@ public function invalidateMultiple(array $cids) { * {@inheritdoc} */ public function invalidateTags(array $tags) { - $flat_tags = $this->flattenTags($tags); foreach ($this->storage()->listAll() as $cidhash) { $item = $this->getByHash($cidhash); - if ($item && array_intersect($flat_tags, $item->tags)) { + if ($item && array_intersect($tags, $item->tags)) { $this->invalidate($item->cid); } } @@ -231,34 +230,6 @@ public function invalidateAll() { } /** - * 'Flattens' a tags array into an array of strings. - * - * @param array $tags - * Associative array of tags to flatten. - * - * @return array - * An indexed array of strings. - */ - 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; - } - - /** * {@inheritdoc} */ public function garbageCollection() { diff --git a/core/lib/Drupal/Core/Config/CachedStorage.php b/core/lib/Drupal/Core/Config/CachedStorage.php index 0b089ca..b657f66 100644 --- a/core/lib/Drupal/Core/Config/CachedStorage.php +++ b/core/lib/Drupal/Core/Config/CachedStorage.php @@ -131,7 +131,7 @@ public function write($name, array $data) { // While not all written data is read back, setting the cache instead of // just deleting it avoids cache rebuild stampedes. $this->cache->set($this->getCacheKey($name), $data); - Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE)); + Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG)); $this->findByPrefixCache = array(); return TRUE; } @@ -146,7 +146,7 @@ public function delete($name) { // rebuilding the cache before the storage is gone. if ($this->storage->delete($name)) { $this->cache->delete($this->getCacheKey($name)); - Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE)); + Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG)); $this->findByPrefixCache = array(); return TRUE; } @@ -162,7 +162,7 @@ public function rename($name, $new_name) { if ($this->storage->rename($name, $new_name)) { $this->cache->delete($this->getCacheKey($name)); $this->cache->delete($this->getCacheKey($new_name)); - Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE)); + Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG)); $this->findByPrefixCache = array(); return TRUE; } @@ -224,7 +224,7 @@ protected function findByPrefix($prefix) { 'find:' . $cache_key, $this->findByPrefixCache[$cache_key], Cache::PERMANENT, - array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE) + array($this::FIND_BY_PREFIX_CACHE_TAG) ); } } diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php index c329032..3dfe714 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php @@ -163,7 +163,7 @@ public function enable() { */ public function disable() { // An entity was disabled, invalidate its own cache tag. - Cache::invalidateTags(array($this->entityTypeId => array($this->id()))); + Cache::invalidateTags($this->getCacheTag()); return $this->setStatus(FALSE); } diff --git a/core/lib/Drupal/Core/Datetime/Entity/DateFormat.php b/core/lib/Drupal/Core/Datetime/Entity/DateFormat.php index 2cc01e8..bb4efe0 100644 --- a/core/lib/Drupal/Core/Datetime/Entity/DateFormat.php +++ b/core/lib/Drupal/Core/Datetime/Entity/DateFormat.php @@ -95,14 +95,14 @@ public static function sort(ConfigEntityInterface $a, ConfigEntityInterface $b) * {@inheritdoc} */ public function getCacheTag() { - return array('rendered' => TRUE); + return ['rendered']; } /** * {@inheritdoc} */ public function getListCacheTags() { - return array('rendered' => TRUE); + return ['rendered']; } } diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php index 9424994..fc289d2 100644 --- a/core/lib/Drupal/Core/Entity/Entity.php +++ b/core/lib/Drupal/Core/Entity/Entity.php @@ -9,7 +9,6 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\DependencyInjection\DependencySerializationTrait; -use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\String; use Drupal\Component\Utility\Unicode; use Drupal\Core\Config\Entity\Exception\ConfigEntityIdLengthException; @@ -404,7 +403,7 @@ public function referencedEntities() { * {@inheritdoc} */ public function getCacheTag() { - return array($this->entityTypeId => array($this->id())); + return [$this->entityTypeId . ':' . $this->id()]; } /** @@ -412,7 +411,7 @@ public function getCacheTag() { */ public function getListCacheTags() { // @todo Add bundle-specific listing cache tag? https://drupal.org/node/2145751 - return array($this->entityTypeId . 's' => TRUE); + return [$this->entityTypeId . 's']; } /** @@ -473,7 +472,7 @@ protected function invalidateTagsOnSave($update) { $tags = $this->getListCacheTags(); if ($update) { // An existing entity was updated, also invalidate its unique cache tag. - $tags = NestedArray::mergeDeep($tags, $this->getCacheTag()); + $tags = Cache::mergeTags($tags, $this->getCacheTag()); $this->onUpdateBundleEntity(); } Cache::invalidateTags($tags); @@ -493,7 +492,7 @@ protected static function invalidateTagsOnDelete(array $entities) { // other pages than the one it's on. The one it's on is handled by its own // cache tag, but subsequent list pages would not be invalidated, hence we // must invalidate its list cache tags as well.) - $tags = NestedArray::mergeDeepArray(array($tags, $entity->getCacheTag(), $entity->getListCacheTags())); + $tags = Cache::mergeTags($tags, $entity->getCacheTag(), $entity->getListCacheTags()); $entity->onSaveOrDelete(); } Cache::invalidateTags($tags); diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php index 66ef2a6..94fe169 100644 --- a/core/lib/Drupal/Core/Entity/EntityManager.php +++ b/core/lib/Drupal/Core/Entity/EntityManager.php @@ -187,7 +187,7 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa public function __construct(\Traversable $namespaces, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, TranslationInterface $translation_manager, ClassResolverInterface $class_resolver, TypedDataManager $typed_data_manager, KeyValueStoreInterface $installed_definitions) { parent::__construct('Entity', $namespaces, $module_handler, 'Drupal\Core\Entity\EntityInterface', 'Drupal\Core\Entity\Annotation\EntityType'); - $this->setCacheBackend($cache, 'entity_type', array('entity_types' => TRUE)); + $this->setCacheBackend($cache, 'entity_type', array('entity_types')); $this->alterInfo('entity_type'); $this->languageManager = $language_manager; @@ -342,7 +342,7 @@ public function getBaseFieldDefinitions($entity_type_id) { else { // Rebuild the definitions and put it into the cache. $this->baseFieldDefinitions[$entity_type_id] = $this->buildBaseFieldDefinitions($entity_type_id); - $this->cacheBackend->set($cid, $this->baseFieldDefinitions[$entity_type_id], Cache::PERMANENT, array('entity_types' => TRUE, 'entity_field_info' => TRUE)); + $this->cacheBackend->set($cid, $this->baseFieldDefinitions[$entity_type_id], Cache::PERMANENT, array('entity_types', 'entity_field_info')); } } return $this->baseFieldDefinitions[$entity_type_id]; @@ -440,7 +440,7 @@ public function getFieldDefinitions($entity_type_id, $bundle) { else { // Rebuild the definitions and put it into the cache. $bundle_field_definitions = $this->buildBundleFieldDefinitions($entity_type_id, $bundle, $base_field_definitions); - $this->cacheBackend->set($cid, $bundle_field_definitions, Cache::PERMANENT, array('entity_types' => TRUE, 'entity_field_info' => TRUE)); + $this->cacheBackend->set($cid, $bundle_field_definitions, Cache::PERMANENT, array('entity_types', 'entity_field_info')); } // Field definitions consist of the bundle specific overrides and the // base fields, merge them together. Use array_replace() to replace base @@ -548,7 +548,7 @@ public function getFieldStorageDefinitions($entity_type_id) { else { // Rebuild the definitions and put it into the cache. $field_storage_definitions = $this->buildFieldStorageDefinitions($entity_type_id); - $this->cacheBackend->set($cid, $field_storage_definitions, Cache::PERMANENT, array('entity_types' => TRUE, 'entity_field_info' => TRUE)); + $this->cacheBackend->set($cid, $field_storage_definitions, Cache::PERMANENT, array('entity_types', 'entity_field_info')); } $this->fieldStorageDefinitions[$entity_type_id] += $field_storage_definitions; } @@ -578,7 +578,7 @@ public function getFieldMap() { } } - $this->cacheBackend->set($cid, $this->fieldMap, Cache::PERMANENT, array('entity_types' => TRUE, 'entity_field_info' => TRUE)); + $this->cacheBackend->set($cid, $this->fieldMap, Cache::PERMANENT, array('entity_types', 'entity_field_info')); } } return $this->fieldMap; @@ -651,7 +651,7 @@ public function clearCachedFieldDefinitions() { $this->fieldMapByFieldType = array(); $this->displayModeInfo = array(); $this->extraFields = array(); - Cache::deleteTags(array('entity_field_info' => TRUE)); + Cache::deleteTags(array('entity_field_info')); // The typed data manager statically caches prototype objects with injected // definitions, clear those as well. $this->typedDataManager->clearCachedDefinitions(); @@ -662,7 +662,7 @@ public function clearCachedFieldDefinitions() { */ public function clearCachedBundles() { $this->bundleInfo = array(); - Cache::deleteTags(array('entity_bundles' => TRUE)); + Cache::deleteTags(array('entity_bundles')); // Entity bundles are exposed as data types, clear that cache too. $this->typedDataManager->clearCachedDefinitions(); } @@ -702,7 +702,7 @@ public function getAllBundleInfo() { } } $this->moduleHandler->alter('entity_bundle_info', $this->bundleInfo); - $this->cacheBackend->set("entity_bundle_info:$langcode", $this->bundleInfo, Cache::PERMANENT, array('entity_types' => TRUE, 'entity_bundles' => TRUE)); + $this->cacheBackend->set("entity_bundle_info:$langcode", $this->bundleInfo, Cache::PERMANENT, array('entity_types', 'entity_bundles')); } } @@ -739,7 +739,7 @@ public function getExtraFields($entity_type_id, $bundle) { // Store in the 'static' and persistent caches. $this->extraFields[$entity_type_id][$bundle] = $info; $this->cacheBackend->set($cache_id, $info, Cache::PERMANENT, array( - 'entity_field_info' => TRUE, + 'entity_field_info', )); return $this->extraFields[$entity_type_id][$bundle]; @@ -860,7 +860,7 @@ protected function getAllDisplayModesByEntityType($display_type) { $this->displayModeInfo[$display_type][$display_mode_entity_type][$display_mode_name] = $display_mode->toArray(); } $this->moduleHandler->alter($key, $this->displayModeInfo[$display_type]); - $this->cacheBackend->set("$key:$langcode", $this->displayModeInfo[$display_type], CacheBackendInterface::CACHE_PERMANENT, array('entity_types' => TRUE, 'entity_field_info' => TRUE)); + $this->cacheBackend->set("$key:$langcode", $this->displayModeInfo[$display_type], CacheBackendInterface::CACHE_PERMANENT, array('entity_types', 'entity_field_info')); } } diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php index cd27e7d..e24735a 100644 --- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php +++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php @@ -7,7 +7,6 @@ namespace Drupal\Core\Entity; -use Drupal\Component\Utility\NestedArray; use Drupal\Core\Cache\Cache; use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\Core\Field\FieldItemInterface; @@ -160,7 +159,7 @@ protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langco '#langcode' => $langcode, // Collect cache defaults for this entity. '#cache' => array( - 'tags' => NestedArray::mergeDeep($this->getCacheTag(), $entity->getCacheTag()), + 'tags' => Cache::mergeTags($this->getCacheTag(), $entity->getCacheTag()), ), ); @@ -343,7 +342,7 @@ protected function alterBuild(array &$build, EntityInterface $entity, EntityView * {@inheritdoc} */ public function getCacheTag() { - return array($this->entityTypeId . '_view' => TRUE); + return array($this->entityTypeId . '_view'); } /** @@ -352,9 +351,9 @@ public function getCacheTag() { public function resetCache(array $entities = NULL) { if (isset($entities)) { // Always invalidate the ENTITY_TYPE_list tag. - $tags = array($this->entityTypeId . '_list' => TRUE); + $tags = array($this->entityTypeId . '_list'); foreach ($entities as $entity) { - $tags = NestedArray::mergeDeep($tags, $entity->getCacheTag()); + $tags = Cache::mergeTags($tags, $entity->getCacheTag()); } Cache::invalidateTags($tags); } diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php index 61821b0..af81e10 100644 --- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php +++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php @@ -497,8 +497,8 @@ protected function setPersistentCache($entities) { } $cache_tags = array( - $this->entityTypeId . '_values' => TRUE, - 'entity_field_info' => TRUE, + $this->entityTypeId . '_values', + 'entity_field_info', ); foreach ($entities as $id => $entity) { $this->cacheBackend->set($this->buildCacheId($id), $entity, CacheBackendInterface::CACHE_PERMANENT, $cache_tags); @@ -543,7 +543,7 @@ public function resetCache(array $ids = NULL) { else { $this->entities = array(); if ($this->entityType->isPersistentlyCacheable()) { - $this->cacheBackend->deleteTags(array($this->entityTypeId . '_values' => TRUE)); + $this->cacheBackend->deleteTags(array($this->entityTypeId . '_values')); } } } diff --git a/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php index 80c6e5a..b51e491 100644 --- a/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php @@ -77,7 +77,7 @@ public function onHtmlPage(GetResponseForControllerResultEvent $event) { // recommended. $response = new Response((string) $this->pageRenderer->render($page), $page->getStatusCode()); if ($tags = $page->getCacheTags()) { - $response->headers->set('X-Drupal-Cache-Tags', static::convertCacheTagsToHeader($tags)); + $response->headers->set('X-Drupal-Cache-Tags', implode(' ', $tags)); } if ($keys = $page->getCacheKeys()) { $response->headers->set('cache_keys', serialize($keys)); @@ -107,60 +107,4 @@ static function getSubscribedEvents() { return $events; } - /** - * Converts a cache tags array into a X-Drupal-Cache-Tags header value. - * - * @param array $tags - * Associative array of cache tags to flatten. - * - * @return string - * A space-separated list of flattened cache tag identifiers. - */ - public static function convertCacheTagsToHeader(array $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 implode(' ', $flat_tags); - } - - /** - * Converts a X-Drupal-Cache-Tags header value into a cache tags array. - * - * @param string $tags_header - * A space-separated list of flattened cache tag identifiers. - * - * @return array - * Associative array of cache tags to flatten. - */ - public static function convertHeaderToCacheTags($tags_header) { - if (!is_string($tags_header) || strlen(trim($tags_header)) == 0) { - return array(); - } - - $flat_tags = explode(' ', trim($tags_header)); - $tags = array(); - foreach ($flat_tags as $flat_tag) { - list($namespace, $value) = explode(':', $flat_tag); - if (!isset($tags[$namespace])) { - $tags[$namespace] = $value; - } - // Multiple values in this namespace. - else { - if (!is_array($tags[$namespace])) { - $tags[$namespace] = array($tags[$namespace]); - } - $tags[$namespace][] = $value; - } - } - return $tags; - } - } diff --git a/core/lib/Drupal/Core/EventSubscriber/MenuRouterRebuildSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/MenuRouterRebuildSubscriber.php index 4b0b41f..41b5a87 100644 --- a/core/lib/Drupal/Core/EventSubscriber/MenuRouterRebuildSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/MenuRouterRebuildSubscriber.php @@ -57,7 +57,7 @@ public function __construct(LockBackendInterface $lock, MenuLinkManagerInterface */ public function onRouterRebuild(Event $event) { $this->menuLinksRebuild(); - Cache::deleteTags(array('local_task' => 1)); + Cache::deleteTags(array('local_task')); } /** diff --git a/core/lib/Drupal/Core/Extension/ThemeHandler.php b/core/lib/Drupal/Core/Extension/ThemeHandler.php index f42f1bd..387da80 100644 --- a/core/lib/Drupal/Core/Extension/ThemeHandler.php +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php @@ -655,7 +655,7 @@ protected function resetSystem() { // @todo It feels wrong to have the requirement to clear the local tasks // cache here. - Cache::deleteTags(array('local_task' => 1)); + Cache::deleteTags(array('local_task')); $this->themeRegistryRebuild(); } diff --git a/core/lib/Drupal/Core/Menu/ContextualLinkManager.php b/core/lib/Drupal/Core/Menu/ContextualLinkManager.php index b99c11f..140a69a 100644 --- a/core/lib/Drupal/Core/Menu/ContextualLinkManager.php +++ b/core/lib/Drupal/Core/Menu/ContextualLinkManager.php @@ -113,7 +113,7 @@ public function __construct(ControllerResolverInterface $controller_resolver, Mo $this->moduleHandler = $module_handler; $this->requestStack = $request_stack; $this->alterInfo('contextual_links_plugins'); - $this->setCacheBackend($cache_backend, 'contextual_links_plugins:' . $language_manager->getCurrentLanguage()->getId(), array('contextual_links_plugins' => TRUE)); + $this->setCacheBackend($cache_backend, 'contextual_links_plugins:' . $language_manager->getCurrentLanguage()->getId(), array('contextual_links_plugins')); } /** diff --git a/core/lib/Drupal/Core/Menu/LocalActionManager.php b/core/lib/Drupal/Core/Menu/LocalActionManager.php index 1033d79..2e4eb92 100644 --- a/core/lib/Drupal/Core/Menu/LocalActionManager.php +++ b/core/lib/Drupal/Core/Menu/LocalActionManager.php @@ -124,7 +124,7 @@ public function __construct(ControllerResolverInterface $controller_resolver, Re $this->moduleHandler = $module_handler; $this->account = $account; $this->alterInfo('menu_local_actions'); - $this->setCacheBackend($cache_backend, 'local_action_plugins:' . $language_manager->getCurrentLanguage()->getId(), array('local_action' => TRUE)); + $this->setCacheBackend($cache_backend, 'local_action_plugins:' . $language_manager->getCurrentLanguage()->getId(), array('local_action')); } /** diff --git a/core/lib/Drupal/Core/Menu/LocalTaskManager.php b/core/lib/Drupal/Core/Menu/LocalTaskManager.php index 862050c..4e1f675 100644 --- a/core/lib/Drupal/Core/Menu/LocalTaskManager.php +++ b/core/lib/Drupal/Core/Menu/LocalTaskManager.php @@ -135,7 +135,7 @@ public function __construct(ControllerResolverInterface $controller_resolver, Re $this->account = $account; $this->moduleHandler = $module_handler; $this->alterInfo('local_tasks'); - $this->setCacheBackend($cache, 'local_task_plugins:' . $language_manager->getCurrentLanguage()->getId(), array('local_task' => TRUE)); + $this->setCacheBackend($cache, 'local_task_plugins:' . $language_manager->getCurrentLanguage()->getId(), array('local_task')); } /** diff --git a/core/lib/Drupal/Core/Menu/MenuLinkTree.php b/core/lib/Drupal/Core/Menu/MenuLinkTree.php index 25dede7..be55a81 100644 --- a/core/lib/Drupal/Core/Menu/MenuLinkTree.php +++ b/core/lib/Drupal/Core/Menu/MenuLinkTree.php @@ -124,7 +124,7 @@ public function getCurrentRouteMenuTreeParameters($menu_name) { // expanded. ->addExpandedParents($this->treeStorage->getExpanded($menu_name, $active_trail)); - $this->cache->set($cid, $parameters, CacheBackendInterface::CACHE_PERMANENT, array('menu' => $menu_name)); + $this->cache->set($cid, $parameters, CacheBackendInterface::CACHE_PERMANENT, array('menu:' . $menu_name)); } $this->cachedCurrentRouteParameters[$menu_name] = $parameters; } @@ -246,7 +246,7 @@ public function build(array $tree) { // Allow menu-specific theme overrides. $build['#theme_wrappers'][] = 'menu_tree__' . strtr($menu_name, '-', '_'); // Set cache tag. - $build['#cache']['tags']['menu'][$menu_name] = $menu_name; + $build['#cache']['tags'][] = 'menu:' . $menu_name; } return $build; diff --git a/core/lib/Drupal/Core/Menu/MenuTreeStorage.php b/core/lib/Drupal/Core/Menu/MenuTreeStorage.php index 92775b4..fb75390 100644 --- a/core/lib/Drupal/Core/Menu/MenuTreeStorage.php +++ b/core/lib/Drupal/Core/Menu/MenuTreeStorage.php @@ -193,7 +193,8 @@ public function rebuild(array $definitions) { $this->resetDefinitions(); $affected_menus = $this->getMenuNames() + $before_menus; // Invalidate any cache tagged with any menu name. - Cache::invalidateTags(array('menu' => $affected_menus)); + $cache_tags = Cache::buildTags('menu', $affected_menus); + Cache::invalidateTags($cache_tags); $this->resetDefinitions(); // Every item in the cache bin should have one of the menu cache tags but it // is not guaranteed, so invalidate everything in the bin. @@ -255,7 +256,8 @@ protected function safeExecuteSelect(SelectInterface $query) { public function save(array $link) { $affected_menus = $this->doSave($link); $this->resetDefinitions(); - Cache::invalidateTags(array('menu' => $affected_menus)); + $cache_tags = Cache::buildTags('menu', $affected_menus); + Cache::invalidateTags($cache_tags); return $affected_menus; } @@ -436,7 +438,7 @@ public function delete($id) { $this->updateParentalStatus($item); // Many children may have moved. $this->resetDefinitions(); - Cache::invalidateTags(array('menu' => $item['menu_name'])); + Cache::invalidateTags(array('menu:' . $item['menu_name'])); } } @@ -837,7 +839,7 @@ public function loadTreeData($menu_name, MenuTreeParameters $parameters) { $data['tree'] = $this->doBuildTreeData($links, $parameters->activeTrail, $parameters->minDepth); $data['definitions'] = array(); $data['route_names'] = $this->collectRoutesAndDefinitions($data['tree'], $data['definitions']); - $this->menuCacheBackend->set($tree_cid, $data, Cache::PERMANENT, array('menu' => $menu_name)); + $this->menuCacheBackend->set($tree_cid, $data, Cache::PERMANENT, array('menu:' . $menu_name)); // The definitions were already added to $this->definitions in // $this->doBuildTreeData() unset($data['definitions']); diff --git a/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php b/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php index bcb8146..164f7f5 100644 --- a/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php +++ b/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Page; +use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheableInterface; use Drupal\Core\Language\LanguageManagerInterface; @@ -68,12 +69,15 @@ public function render(HtmlFragmentInterface $fragment, $status_code = 200) { } if ($fragment instanceof CacheableInterface) { - // Collect cache tags for all the content in all the regions on the page. - $tags = $page_array['#cache']['tags']; - // Tag every render cache item with the "rendered" cache tag. This allows us - // to invalidate the entire render cache, regardless of the cache bin. - $tags['rendered'] = TRUE; - $page->setCacheTags($tags); + // Persist cache tags associated with this page. Also associate the + // "rendered" cache tag. This allows us to invalidate the entire render + // cache, regardless of the cache bin. + $cache_tags = $page_array['#cache']['tags']; + $cache_tags[] = 'rendered'; + // Only keep unique cache tags. We need to prevent duplicates here already + // rather than only in the cache layer, because they are also used by + // reverse proxies (like Varnish), not only by Drupal's page cache. + $page->setCacheTags(array_unique($cache_tags)); } return $page; diff --git a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php index a2d4cf2..5828d33 100644 --- a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php +++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php @@ -125,6 +125,7 @@ public function __construct($subdir, \Traversable $namespaces, ModuleHandlerInte * definitions should be cleared along with other, related cache entries. */ public function setCacheBackend(CacheBackendInterface $cache_backend, $cache_key, array $cache_tags = array()) { + Cache::validateTags($cache_tags); $this->cacheBackend = $cache_backend; $this->cacheKey = $cache_key; $this->cacheTags = $cache_tags; diff --git a/core/lib/Drupal/Core/Render/Element/Page.php b/core/lib/Drupal/Core/Render/Element/Page.php index f12c8f3..cf133af 100644 --- a/core/lib/Drupal/Core/Render/Element/Page.php +++ b/core/lib/Drupal/Core/Render/Element/Page.php @@ -38,8 +38,8 @@ public function getInfo() { * @return array */ public static function preRenderPage($element) { - $element['#cache']['tags']['theme'] = \Drupal::theme()->getActiveTheme()->getName(); - $element['#cache']['tags']['theme_global_settings'] = TRUE; + $element['#cache']['tags'][] = 'theme:' . \Drupal::theme()->getActiveTheme()->getName(); + $element['#cache']['tags'][] = 'theme_global_settings'; return $element; } diff --git a/core/lib/Drupal/Core/Theme/Registry.php b/core/lib/Drupal/Core/Theme/Registry.php index 0ad005d..da3122c 100644 --- a/core/lib/Drupal/Core/Theme/Registry.php +++ b/core/lib/Drupal/Core/Theme/Registry.php @@ -235,7 +235,7 @@ public function get() { public function getRuntime() { $this->init($this->themeName); if (!isset($this->runtimeRegistry)) { - $this->runtimeRegistry = new ThemeRegistry('theme_registry:runtime:' . $this->theme->getName(), $this->cache, $this->lock, array('theme_registry' => TRUE), $this->moduleHandler->isLoaded()); + $this->runtimeRegistry = new ThemeRegistry('theme_registry:runtime:' . $this->theme->getName(), $this->cache, $this->lock, array('theme_registry'), $this->moduleHandler->isLoaded()); } return $this->runtimeRegistry; } @@ -244,7 +244,7 @@ public function getRuntime() { * Persists the theme registry in the cache backend. */ protected function setCache() { - $this->cache->set('theme_registry:' . $this->theme->getName(), $this->registry, Cache::PERMANENT, array('theme_registry' => TRUE)); + $this->cache->set('theme_registry:' . $this->theme->getName(), $this->registry, Cache::PERMANENT, array('theme_registry')); } /** @@ -318,7 +318,7 @@ protected function build() { } // Only cache this registry if all modules are loaded. if ($this->moduleHandler->isLoaded()) { - $this->cache->set("theme_registry:build:modules", $cache, Cache::PERMANENT, array('theme_registry' => TRUE)); + $this->cache->set("theme_registry:build:modules", $cache, Cache::PERMANENT, array('theme_registry')); } } @@ -557,7 +557,7 @@ public function reset() { $this->runtimeRegistry = NULL; $this->registry = NULL; - Cache::invalidateTags(array('theme_registry' => TRUE)); + Cache::invalidateTags(array('theme_registry')); return $this; } diff --git a/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php b/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php index 2927756..78ffd26 100644 --- a/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php +++ b/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php @@ -26,7 +26,7 @@ class ThemeAccessCheck implements AccessInterface { */ public function access($theme) { // Cacheable until the theme is modified. - return AccessResult::allowedIf($this->checkAccess($theme))->addCacheTags(array('theme' => $theme)); + return AccessResult::allowedIf($this->checkAccess($theme))->addCacheTags(array('theme:' . $theme)); } /** diff --git a/core/lib/Drupal/Core/Utility/Token.php b/core/lib/Drupal/Core/Utility/Token.php index d03d645..a59becb 100644 --- a/core/lib/Drupal/Core/Utility/Token.php +++ b/core/lib/Drupal/Core/Utility/Token.php @@ -320,7 +320,7 @@ public function getInfo() { $this->tokenInfo = $this->moduleHandler->invokeAll('token_info'); $this->moduleHandler->alter('token_info', $this->tokenInfo); $this->cache->set($cache_id, $this->tokenInfo, CacheBackendInterface::CACHE_PERMANENT, array( - static::TOKEN_INFO_CACHE_TAG => TRUE, + static::TOKEN_INFO_CACHE_TAG, )); } } diff --git a/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php index 65f95f1..f28ff0d 100644 --- a/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php +++ b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php @@ -7,10 +7,10 @@ namespace Drupal\aggregator\Plugin\Block; -use Drupal\Component\Utility\NestedArray; use Drupal\aggregator\FeedStorageInterface; use Drupal\aggregator\ItemStorageInterface; use Drupal\Core\Block\BlockBase; +use Drupal\Core\Cache\Cache; use Drupal\Core\Entity\Query\QueryInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; @@ -195,8 +195,7 @@ public function build() { public function getCacheTags() { $cache_tags = parent::getCacheTags(); $feed = $this->feedStorage->load($this->configuration['feed']); - $cache_tags = NestedArray::mergeDeep($cache_tags, $feed->getCacheTag()); - return $cache_tags; + return Cache::mergeTags($cache_tags, $feed->getCacheTag()); } } diff --git a/core/modules/aggregator/src/Tests/AggregatorRenderingTest.php b/core/modules/aggregator/src/Tests/AggregatorRenderingTest.php index d967d1b..22515a3 100644 --- a/core/modules/aggregator/src/Tests/AggregatorRenderingTest.php +++ b/core/modules/aggregator/src/Tests/AggregatorRenderingTest.php @@ -67,8 +67,8 @@ public function testBlockLinks() { $this->assertFalse(empty($correct_titles), 'Aggregator feed page is available and has the correct title.'); $cache_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags')); $this->assertTrue(in_array('aggregator_feed:' . $feed->id(), $cache_tags)); - $this->assertTrue(in_array('aggregator_feed_view:1', $cache_tags)); - $this->assertTrue(in_array('aggregator_item_view:1', $cache_tags)); + $this->assertTrue(in_array('aggregator_feed_view', $cache_tags)); + $this->assertTrue(in_array('aggregator_item_view', $cache_tags)); // Set the number of news items to 0 to test that the block does not show // up. @@ -119,8 +119,8 @@ public function testFeedPage() { $this->assertTrue(!empty($elements), 'Individual source page contains a pager.'); $cache_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags')); $this->assertTrue(in_array('aggregator_feed:' . $feed->id(), $cache_tags)); - $this->assertTrue(in_array('aggregator_feed_view:1', $cache_tags)); - $this->assertTrue(in_array('aggregator_item_view:1', $cache_tags)); + $this->assertTrue(in_array('aggregator_feed_view', $cache_tags)); + $this->assertTrue(in_array('aggregator_item_view', $cache_tags)); } } diff --git a/core/modules/block/src/BlockViewBuilder.php b/core/modules/block/src/BlockViewBuilder.php index d92898b..d29b63c 100644 --- a/core/modules/block/src/BlockViewBuilder.php +++ b/core/modules/block/src/BlockViewBuilder.php @@ -7,7 +7,6 @@ namespace Drupal\block; -use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\String; use Drupal\Core\Cache\Cache; use Drupal\Core\Entity\EntityViewBuilder; @@ -71,12 +70,12 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la // Set cache tags; these always need to be set, whether the block is // cacheable or not, so that the page cache is correctly informed. - $build[$entity_id]['#cache']['tags'] = NestedArray::mergeDeepArray(array( + $build[$entity_id]['#cache']['tags'] = Cache::mergeTags( $this->getCacheTag(), // Block view builder cache tag. $entity->getCacheTag(), // Block entity cache tag. $entity->getListCacheTags(), // Block entity list cache tags. - $plugin->getCacheTags(), // Block plugin cache tags. - )); + $plugin->getCacheTags() // Block plugin cache tags. + ); if ($plugin->isCacheable()) { $build[$entity_id]['#pre_render'][] = array($this, 'buildBlock'); diff --git a/core/modules/block/src/Entity/Block.php b/core/modules/block/src/Entity/Block.php index ee977d0..0c71ed1 100644 --- a/core/modules/block/src/Entity/Block.php +++ b/core/modules/block/src/Entity/Block.php @@ -164,7 +164,7 @@ public function calculateDependencies() { * this block is placed in instead. */ public function getListCacheTags() { - return array('theme' => $this->theme); + return array('theme:' . $this->theme); } /** diff --git a/core/modules/block/src/Tests/BlockCacheTest.php b/core/modules/block/src/Tests/BlockCacheTest.php index 94babf7..fd9fc88 100644 --- a/core/modules/block/src/Tests/BlockCacheTest.php +++ b/core/modules/block/src/Tests/BlockCacheTest.php @@ -78,7 +78,7 @@ function testCachePerRole() { $this->assertText($old_content, 'Block is served from the cache.'); // Clear the cache and verify that the stale data is no longer there. - Cache::invalidateTags(array('block_view' => TRUE)); + Cache::invalidateTags(array('block_view')); $this->drupalGet(''); $this->assertNoText($old_content, 'Block cache clear removes stale cache data.'); $this->assertText($current_content, 'Fresh block content is displayed after clearing the cache.'); diff --git a/core/modules/block/src/Tests/BlockTest.php b/core/modules/block/src/Tests/BlockTest.php index 234077c..6453902 100644 --- a/core/modules/block/src/Tests/BlockTest.php +++ b/core/modules/block/src/Tests/BlockTest.php @@ -294,21 +294,23 @@ public function testBlockCacheTags() { $cache_entry = \Drupal::cache('render')->get($cid); $expected_cache_tags = array( 'theme:stark', - 'theme_global_settings:1', - 'block_view:1', + 'theme_global_settings', + 'block_view', 'block:powered', 'block_plugin:system_powered_by_block', - 'rendered:1', + 'rendered', ); + sort($expected_cache_tags); $this->assertIdentical($cache_entry->tags, $expected_cache_tags); $cache_entry = \Drupal::cache('render')->get('entity_view:block:powered:en:stark'); $expected_cache_tags = array( - 'block_view:1', + 'block_view', 'block:powered', 'theme:stark', 'block_plugin:system_powered_by_block', - 'rendered:1', + 'rendered', ); + sort($expected_cache_tags); $this->assertIdentical($cache_entry->tags, $expected_cache_tags); // The "Powered by Drupal" block is modified; verify a cache miss. @@ -334,30 +336,33 @@ public function testBlockCacheTags() { $cache_entry = \Drupal::cache('render')->get($cid); $expected_cache_tags = array( 'theme:stark', - 'theme_global_settings:1', - 'block_view:1', + 'theme_global_settings', + 'block_view', 'block:powered-2', 'block:powered', 'block_plugin:system_powered_by_block', - 'rendered:1', + 'rendered', ); + sort($expected_cache_tags); $this->assertEqual($cache_entry->tags, $expected_cache_tags); $expected_cache_tags = array( - 'block_view:1', + 'block_view', 'block:powered', 'theme:stark', 'block_plugin:system_powered_by_block', - 'rendered:1', + 'rendered', ); + sort($expected_cache_tags); $cache_entry = \Drupal::cache('render')->get('entity_view:block:powered:en:stark'); $this->assertIdentical($cache_entry->tags, $expected_cache_tags); $expected_cache_tags = array( - 'block_view:1', + 'block_view', 'block:powered-2', 'theme:stark', 'block_plugin:system_powered_by_block', - 'rendered:1', + 'rendered', ); + sort($expected_cache_tags); $cache_entry = \Drupal::cache('render')->get('entity_view:block:powered-2:en:stark'); $this->assertIdentical($cache_entry->tags, $expected_cache_tags); diff --git a/core/modules/block/src/Tests/BlockViewBuilderTest.php b/core/modules/block/src/Tests/BlockViewBuilderTest.php index 8c9fb13..c5523f3 100644 --- a/core/modules/block/src/Tests/BlockViewBuilderTest.php +++ b/core/modules/block/src/Tests/BlockViewBuilderTest.php @@ -8,7 +8,7 @@ namespace Drupal\block\Tests; use Drupal\Component\Utility\Html; -use Drupal\Component\Utility\NestedArray; +use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\UrlCacheContext; use Drupal\simpletest\DrupalUnitTestBase; use Symfony\Component\HttpFoundation\Request; @@ -221,7 +221,7 @@ public function testBlockViewBuilderAlter() { $request->setMethod('GET'); $default_keys = array('entity_view', 'block', 'test_block', 'en', 'cache_context.theme'); - $default_tags = array('block_view' => TRUE, 'block' => array('test_block'), 'theme' => 'stark', 'block_plugin' => array('test_cache')); + $default_tags = array('block_view', 'block:test_block', 'theme:stark', 'block_plugin:test_cache'); // Advanced: cached block, but an alter hook adds an additional cache key. $this->setBlockCacheConfig(array( @@ -236,22 +236,25 @@ public function testBlockViewBuilderAlter() { $this->assertIdentical(drupal_render($build), ''); $cache_entry = $this->container->get('cache.render')->get($cid); $this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.'); - $expected_flattened_tags = array('block_view:1', 'block:test_block', 'theme:stark', 'block_plugin:test_cache', 'rendered:1'); - $this->assertIdentical($cache_entry->tags, $expected_flattened_tags, 'The block render element has been cached with the expected cache tags.'); + $expected_tags = array('block_view', 'block:test_block', 'theme:stark', 'block_plugin:test_cache', 'rendered'); + sort($expected_tags); + $this->assertIdentical($cache_entry->tags, $expected_tags, 'The block render element has been cached with the expected cache tags.'); $this->container->get('cache.render')->delete($cid); // Advanced: cached block, but an alter hook adds an additional cache tag. $alter_add_tag = $this->randomMachineName(); \Drupal::state()->set('block_test_view_alter_cache_tag', $alter_add_tag); - $expected_tags = NestedArray::mergeDeep($default_tags, array($alter_add_tag => TRUE)); + $expected_tags = Cache::mergeTags($default_tags, array($alter_add_tag)); $build = $this->getBlockRenderArray(); + sort($build['#cache']['tags']); $this->assertIdentical($expected_tags, $build['#cache']['tags'], 'An altered cacheable block has the expected cache tags.'); $cid = drupal_render_cid_create(array('#cache' => array('keys' => $expected_keys))); $this->assertIdentical(drupal_render($build), ''); $cache_entry = $this->container->get('cache.render')->get($cid); $this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.'); - $expected_flattened_tags = array('block_view:1', 'block:test_block', 'theme:stark', 'block_plugin:test_cache', $alter_add_tag . ':1', 'rendered:1'); - $this->assertIdentical($cache_entry->tags, $expected_flattened_tags, 'The block render element has been cached with the expected cache tags.'); + $expected_tags = array('block_view', 'block:test_block', 'theme:stark', 'block_plugin:test_cache', $alter_add_tag, 'rendered'); + sort($expected_tags); + $this->assertIdentical($cache_entry->tags, $expected_tags, 'The block render element has been cached with the expected cache tags.'); $this->container->get('cache.render')->delete($cid); // Advanced: cached block, but an alter hook adds a #pre_render callback to diff --git a/core/modules/block/tests/modules/block_test/block_test.module b/core/modules/block/tests/modules/block_test/block_test.module index d4bf97f..cec0b24 100644 --- a/core/modules/block/tests/modules/block_test/block_test.module +++ b/core/modules/block/tests/modules/block_test/block_test.module @@ -6,6 +6,7 @@ */ use Drupal\Core\Block\BlockPluginInterface; +use Drupal\Core\Cache\Cache; /** * Implements hook_block_alter(). @@ -27,7 +28,7 @@ function block_test_block_view_test_cache_alter(array &$build, BlockPluginInterf $build['#cache']['keys'][] = \Drupal::state()->get('block_test_view_alter_cache_key'); } if (\Drupal::state()->get('block_test_view_alter_cache_tag') !== NULL) { - $build['#cache']['tags'][\Drupal::state()->get('block_test_view_alter_cache_tag')] = TRUE; + $build['#cache']['tags'][] = \Drupal::state()->get('block_test_view_alter_cache_tag'); } if (\Drupal::state()->get('block_test_view_alter_append_pre_render_prefix') !== NULL) { $build['#pre_render'][] = 'block_test_pre_render_alter_content'; diff --git a/core/modules/book/src/BookManager.php b/core/modules/book/src/BookManager.php index 0035cd3..7d43f1f 100644 --- a/core/modules/book/src/BookManager.php +++ b/core/modules/book/src/BookManager.php @@ -428,7 +428,7 @@ public function deleteFromBook($nid) { } $this->updateOriginalParent($original); $this->books = NULL; - \Drupal::cache('data')->deleteTags(array('bid' => $original['bid'])); + \Drupal::cache('data')->deleteTags(array('bid:' . $original['bid'])); } /** @@ -622,7 +622,7 @@ protected function doBookTreeBuild($bid, array $parameters = array()) { $this->bookTreeCollectNodeLinks($data['tree'], $data['node_links']); // Cache the data, if it is not already in the cache. - \Drupal::cache('data')->set($tree_cid, $data, Cache::PERMANENT, array('bid' => $bid)); + \Drupal::cache('data')->set($tree_cid, $data, Cache::PERMANENT, array('bid:' . $bid)); $trees[$tree_cid] = $data; } @@ -754,9 +754,11 @@ public function saveBookLink(array $link, $new) { 'bid' => $link['bid'], )); } + $cache_tags = []; foreach ($affected_bids as $bid) { - \Drupal::cache('data')->deleteTags(array('bid' => $bid)); + $cache_tags[] = 'bid:' . $bid; } + \Drupal::cache('data')->deleteTags($cache_tags); return $link; } @@ -1041,10 +1043,10 @@ public function bookSubtreeData($link) { // Cache the data, if it is not already in the cache. if (!\Drupal::cache('data')->get($tree_cid)) { - \Drupal::cache('data')->set($tree_cid, $data, Cache::PERMANENT, array('bid' => $link['bid'])); + \Drupal::cache('data')->set($tree_cid, $data, Cache::PERMANENT, array('bid:' . $link['bid'])); } // Cache the cid of the (shared) data using the book and item-specific cid. - \Drupal::cache('data')->set($cid, $tree_cid, Cache::PERMANENT, array('bid' => $link['bid'])); + \Drupal::cache('data')->set($cid, $tree_cid, Cache::PERMANENT, array('bid:' . $link['bid'])); } // Check access for the current user to each item in the tree. $this->bookTreeCheckAccess($data['tree'], $data['node_links']); diff --git a/core/modules/breakpoint/src/BreakpointManager.php b/core/modules/breakpoint/src/BreakpointManager.php index 757f2cf..3187912 100644 --- a/core/modules/breakpoint/src/BreakpointManager.php +++ b/core/modules/breakpoint/src/BreakpointManager.php @@ -113,7 +113,7 @@ public function __construct(ModuleHandlerInterface $module_handler, ThemeHandler $this->themeHandler = $theme_handler; $this->setStringTranslation($string_translation); $this->alterInfo('breakpoints'); - $this->setCacheBackend($cache_backend, 'breakpoints', array('breakpoints' => TRUE)); + $this->setCacheBackend($cache_backend, 'breakpoints', array('breakpoints')); } /** @@ -176,7 +176,7 @@ public function getBreakpointsByGroup($group) { } } uasort($breakpoints, array('Drupal\Component\Utility\SortArray', 'sortByWeightElement')); - $this->cacheBackend->set($this->cacheKey . ':' . $group, $breakpoints, Cache::PERMANENT, array('breakpoints' => TRUE)); + $this->cacheBackend->set($this->cacheKey . ':' . $group, $breakpoints, Cache::PERMANENT, array('breakpoints')); $this->breakpointsByGroup[$group] = $breakpoints; } } @@ -205,7 +205,7 @@ public function getGroups() { $groups[$plugin_definition['group']] = $plugin_definition['group']; } } - $this->cacheBackend->set($this->cacheKey . '::groups', $groups, Cache::PERMANENT, array('breakpoints' => TRUE)); + $this->cacheBackend->set($this->cacheKey . '::groups', $groups, Cache::PERMANENT, array('breakpoints')); } // Get the labels. This is not cacheable due to translation. $group_labels = array(); diff --git a/core/modules/comment/src/Tests/CommentCacheTagsTest.php b/core/modules/comment/src/Tests/CommentCacheTagsTest.php index ee69cc0..921f6ca 100644 --- a/core/modules/comment/src/Tests/CommentCacheTagsTest.php +++ b/core/modules/comment/src/Tests/CommentCacheTagsTest.php @@ -87,7 +87,7 @@ protected function getAdditionalCacheTagsForEntity(EntityInterface $entity) { return array( 'filter_format:plain_text', 'user:' . $entity->getOwnerId(), - 'user_view:1', + 'user_view', ); } diff --git a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php index e5c1c41..9c14685 100644 --- a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php +++ b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php @@ -66,9 +66,10 @@ public function testCacheTags() { ->view($commented_entity); drupal_render($build); $expected_cache_tags = array( - 'entity_test_view' => TRUE, - 'entity_test' => array($commented_entity->id()), + 'entity_test_view', + 'entity_test:' . $commented_entity->id(), ); + sort($expected_cache_tags); $this->assertEqual($build['#cache']['tags'], $expected_cache_tags, 'The test entity has the expected cache tags before it has comments.'); // Create a comment on that entity. Comment loading requires that the uid @@ -101,16 +102,15 @@ public function testCacheTags() { ->view($commented_entity); drupal_render($build); $expected_cache_tags = array( - 'entity_test_view' => TRUE, - 'entity_test' => array($commented_entity->id()), - 'comment_view' => TRUE, - 'comment' => array(1 => $comment->id()), - 'filter_format' => array( - 'plain_text' => 'plain_text', - ), - 'user_view' => TRUE, - 'user' => array(2 => 2), + 'entity_test_view', + 'entity_test:' . $commented_entity->id(), + 'comment_view', + 'comment:' . $comment->id(), + 'filter_format:plain_text', + 'user_view', + 'user:2', ); + sort($expected_cache_tags); $this->assertEqual($build['#cache']['tags'], $expected_cache_tags, 'The test entity has the expected cache tags when it has comments.'); } diff --git a/core/modules/comment/tests/src/Unit/Entity/CommentLockTest.php b/core/modules/comment/tests/src/Unit/Entity/CommentLockTest.php index a540a7e..aef189e 100644 --- a/core/modules/comment/tests/src/Unit/Entity/CommentLockTest.php +++ b/core/modules/comment/tests/src/Unit/Entity/CommentLockTest.php @@ -81,10 +81,10 @@ public function testLocks() { ->will($this->returnValue((object) array('value' => NULL))); $comment->expects($this->once()) ->method('getCacheTag') - ->will($this->returnValue(array('comment' => array($cid)))); + ->will($this->returnValue(array('comment:' . $cid))); $comment->expects($this->once()) ->method('getListCacheTags') - ->will($this->returnValue(array('comments' => TRUE))); + ->will($this->returnValue(array('comments'))); $storage = $this->getMock('Drupal\comment\CommentStorageInterface'); // preSave() should acquire the lock. (This is what's really being tested.) diff --git a/core/modules/config_translation/src/ConfigMapperManager.php b/core/modules/config_translation/src/ConfigMapperManager.php index 2335ffe..9a0b46e 100644 --- a/core/modules/config_translation/src/ConfigMapperManager.php +++ b/core/modules/config_translation/src/ConfigMapperManager.php @@ -99,7 +99,7 @@ public function __construct(CacheBackendInterface $cache_backend, LanguageManage $this->alterInfo('config_translation_info'); // Config translation only uses an info hook discovery, cache by language. $cache_key = 'config_translation_info_plugins' . ':' . $language_manager->getCurrentLanguage()->getId(); - $this->setCacheBackend($cache_backend, $cache_key, array('config_translation_info_plugins' => TRUE)); + $this->setCacheBackend($cache_backend, $cache_key, array('config_translation_info_plugins')); } /** diff --git a/core/modules/content_translation/tests/src/Unit/Access/ContentTranslationManageAccessCheckTest.php b/core/modules/content_translation/tests/src/Unit/Access/ContentTranslationManageAccessCheckTest.php index 7ea7546..6d96727 100644 --- a/core/modules/content_translation/tests/src/Unit/Access/ContentTranslationManageAccessCheckTest.php +++ b/core/modules/content_translation/tests/src/Unit/Access/ContentTranslationManageAccessCheckTest.php @@ -71,7 +71,7 @@ public function testCreateAccess() { ->will($this->returnValue(array())); $entity->expects($this->once()) ->method('getCacheTag') - ->will($this->returnValue(array('node' => 1337))); + ->will($this->returnValue(array('node:1337'))); // Set the route requirements. $route = new Route('test_route'); diff --git a/core/modules/editor/src/Tests/EditorFileReferenceFilterTest.php b/core/modules/editor/src/Tests/EditorFileReferenceFilterTest.php index e03066e..7fa1813 100644 --- a/core/modules/editor/src/Tests/EditorFileReferenceFilterTest.php +++ b/core/modules/editor/src/Tests/EditorFileReferenceFilterTest.php @@ -7,6 +7,7 @@ namespace Drupal\editor\Tests; +use Drupal\Core\Cache\Cache; use Drupal\simpletest\KernelTestBase; use Drupal\filter\FilterBag; @@ -58,12 +59,14 @@ function testEditorFileReferenceFilter() { $image->save(); $id = $image->id(); $uuid = $image->uuid(); + $cache_tag = ['file:' . $id]; file_put_contents('public://alpaca.jpg', $this->randomMachineName()); $image_2 = entity_create('file', array('uri' => 'public://alpaca.jpg')); $image_2->save(); $id_2 = $image_2->id(); $uuid_2 = $image_2->uuid(); + $cache_tag_2 = ['file:' . $id_2]; $this->pass('No data-editor-file-uuid attribute.'); $input = ''; @@ -74,19 +77,19 @@ function testEditorFileReferenceFilter() { $input = ''; $output = $test($input); $this->assertIdentical($input, $output->getProcessedText()); - $this->assertEqual(array('file' => array($id)), $output->getCacheTags()); + $this->assertEqual($cache_tag, $output->getCacheTags()); $this->pass('One data-editor-file-uuid attribute with odd capitalization.'); $input = ''; $output = $test($input); $this->assertIdentical($input, $output->getProcessedText()); - $this->assertEqual(array('file' => array($id)), $output->getCacheTags()); + $this->assertEqual($cache_tag, $output->getCacheTags()); $this->pass('One data-editor-file-uuid attribute on a non-image tag.'); $input = '