/** * Merges the values of another bubbleable metadata object with this one. * * @param \Drupal\Core\Render\BubbleableMetadata $other * The other bubbleable metadata object. * @return static * A new bubbleable metadata object, with the merged data. * * @todo Add unit test for this in * \Drupal\Tests\Core\Render\BubbleableMetadataTest when * drupal_merge_attached() no longer is a procedural function and remove * the '@codeCoverageIgnore' annotation. */ public function merge(BubbleableMetadata $other) { $result = new BubbleableMetadata(); $result->contexts = Cache::mergeContexts($this->contexts, $other->contexts); $result->tags = Cache::mergeTags($this->tags, $other->tags); $result->maxAge = Cache::mergeMaxAges($this->maxAge, $other->maxAge); $result->attached = Renderer::mergeAttachments($this->attached, $other->attached); $result->postRenderCache = NestedArray::mergeDeep($this->postRenderCache, $other->postRenderCache); return $result; }
/** * Merges cacheable metadata from conditions onto the access result object. * * @param \Drupal\Core\Access\AccessResult $access * The access result object. * @param \Drupal\Core\Condition\ConditionInterface[] $conditions * List of visibility conditions. */ protected function mergeCacheabilityFromConditions(AccessResult $access, array $conditions) { foreach ($conditions as $condition) { if ($condition instanceof CacheableDependencyInterface) { $access->addCacheTags($condition->getCacheTags()); $access->addCacheContexts($condition->getCacheContexts()); $access->setCacheMaxAge(Cache::mergeMaxAges($access->getCacheMaxAge(), $condition->getCacheMaxAge())); } } }
/** * Applies the cacheability of the current display to the given render array. * * @param array $element * The render array with updated cacheability metadata. */ protected function applyDisplayCachablityMetadata(array &$element) { /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache */ $cache = $this->getPlugin('cache'); (new CacheableMetadata())->setCacheTags(Cache::mergeTags($this->view->getCacheTags(), isset($this->display['cache_metadata']['tags']) ? $this->display['cache_metadata']['tags'] : []))->setCacheContexts(isset($this->display['cache_metadata']['contexts']) ? $this->display['cache_metadata']['contexts'] : [])->setCacheMaxAge(Cache::mergeMaxAges($cache->getCacheMaxAge(), isset($this->display['cache_metadata']['max-age']) ? $this->display['cache_metadata']['max-age'] : Cache::PERMANENT))->merge(CacheableMetadata::createFromRenderArray($element))->applyTo($element); }
/** * {@inheritdoc} */ public function getCacheMaxAge() { $max_age = parent::getCacheMaxAge(); foreach ($this->getAllEntities() as $entity) { $max_age = Cache::mergeMaxAges($max_age, $entity->getCacheMaxAge()); } return $max_age; }
/** * Gets the max age for the current view. * * @return int */ public function getCacheMaxAge() { $max_age = $this->getDefaultCacheMaxAge(); $max_age = Cache::mergeMaxAges($max_age, $this->view->getQuery()->getCacheMaxAge()); return $max_age; }
/** * Inherits the cacheability of the other access result, if any. * * inheritCacheability() differs from addCacheableDependency() in how it * handles max-age, because it is designed to inherit the cacheability of the * second operand in the andIf() and orIf() operations. There, the situation * "allowed, max-age=0 OR allowed, max-age=1000" needs to yield max-age 1000 * as the end result. * * @param \Drupal\Core\Access\AccessResultInterface $other * The other access result, whose cacheability (if any) to inherit. * * @return $this */ public function inheritCacheability(AccessResultInterface $other) { $this->addCacheableDependency($other); if ($other instanceof CacheableDependencyInterface) { if ($this->getCacheMaxAge() !== 0 && $other->getCacheMaxAge() !== 0) { $this->setCacheMaxAge(Cache::mergeMaxAges($this->getCacheMaxAge(), $other->getCacheMaxAge())); } else { $this->setCacheMaxAge($other->getCacheMaxAge()); } } return $this; }
/** * {@inheritdoc} */ public function getCacheMaxAge() { $max_age = Cache::PERMANENT; // Asks all subplugins (argument defaults, argument validator and styles). if (($plugin = $this->getPlugin('argument_default')) && $plugin instanceof CacheableDependencyInterface) { $max_age = Cache::mergeMaxAges($max_age, $plugin->getCacheMaxAge()); } if (($plugin = $this->getPlugin('argument_validator')) && $plugin instanceof CacheableDependencyInterface) { $max_age = Cache::mergeMaxAges($max_age, $plugin->getCacheMaxAge()); } // Summaries use style plugins. if (($plugin = $this->getPlugin('style')) && $plugin instanceof CacheableDependencyInterface) { $max_age = Cache::mergeMaxAges($max_age, $plugin->getCacheMaxAge()); } return $max_age; }
/** * @covers ::mergeMaxAges * * @dataProvider mergeMaxAgesProvider */ public function testMergeMaxAges($a, $b, $expected) { $this->assertSame($expected, Cache::mergeMaxAges($a, $b)); }
/** * {@inheritdoc} */ public function render() { $build = array(); $build['#markup'] = $this->view->style_plugin->render(); // Wrap the output in a pre tag if this is for a live preview. if (!empty($this->view->live_preview)) { $build['#prefix'] = '<pre>'; $build['#markup'] = SafeMarkup::checkPlain($build['#markup']); $build['#suffix'] = '</pre>'; } // Defaults for bubbleable rendering metadata. $build['#cache']['tags'] = isset($build['#cache']['tags']) ? $build['#cache']['tags'] : array(); $build['#cache']['max-age'] = isset($build['#cache']['max-age']) ? $build['#cache']['max-age'] : Cache::PERMANENT; /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache */ $cache = $this->getPlugin('cache'); $build['#cache']['tags'] = Cache::mergeTags($build['#cache']['tags'], $cache->getCacheTags()); $build['#cache']['max-age'] = Cache::mergeMaxAges($build['#cache']['max-age'], $cache->getCacheMaxAge()); return $build; }
/** * {@inheritdoc} */ public function build() { $build = []; // Default the max page age to permanent. $max_page_age = Cache::PERMANENT; $page = $this->executable->getPage(); // Set default page cache keys that include the page and display. $page_cache_keys = ['page_manager_page', $page->id(), $this->configuration['uuid']]; $page_cache_contexts = []; $contexts = $this->getContexts(); foreach ($this->getRegionAssignments() as $region => $blocks) { if (!$blocks) { continue; } $region_name = Html::getClass("block-region-{$region}"); $build['regions'][$region]['#prefix'] = '<div class="' . $region_name . '">'; $build['regions'][$region]['#suffix'] = '</div>'; /** @var $blocks \Drupal\Core\Block\BlockPluginInterface[] */ $weight = 0; foreach ($blocks as $block_id => $block) { if ($block instanceof ContextAwarePluginInterface) { $this->contextHandler()->applyContextMapping($block, $contexts); } if (!$block->access($this->account)) { continue; } $max_age = $block->getCacheMaxAge(); $block_build = ['#theme' => 'block', '#attributes' => [], '#weight' => $weight++, '#configuration' => $block->getConfiguration(), '#plugin_id' => $block->getPluginId(), '#base_plugin_id' => $block->getBaseId(), '#derivative_plugin_id' => $block->getDerivativeId(), '#block_plugin' => $block, '#pre_render' => [[$this, 'buildBlock']], '#cache' => ['keys' => ['page_manager_page', $page->id(), 'block', $block_id], 'tags' => Cache::mergeTags($page->getCacheTags(), $block->getCacheTags()), 'contexts' => $block->getCacheContexts(), 'max-age' => $max_age]]; // Build the cache key and a list of all contexts for the whole page. $page_cache_keys[] = $block_id; $page_cache_contexts = Cache::mergeContexts($page_cache_contexts, $block_build['#cache']['contexts']); if (!empty($block_build['#configuration']['label'])) { $block_build['#configuration']['label'] = SafeMarkup::checkPlain($block_build['#configuration']['label']); } // Update the page max age, set it to the lowest max age of all blocks. $max_page_age = Cache::mergeMaxAges($max_age, $max_page_age); $build['regions'][$region][$block_id] = $block_build; } } $build['#title'] = $this->renderPageTitle($this->configuration['page_title']); if ($max_page_age !== 0) { // If all blocks of this page can be cached, then the max page age is not // 0. In this case, we additionally cache the whole page, so we need // to fetch fewer caches. Also explicitly provide the cache contexts, // additional contexts might still bubble up from the block content, but // if not, then we save a cache redirection. // We don't have to set those values in case we can't cache all blocks, // as they will bubble up from the blocks. $build['regions']['#cache'] = ['keys' => $page_cache_keys, 'contexts' => $page_cache_contexts, 'max-age' => $max_page_age]; } return $build; }
/** * {@inheritdoc} */ public function getCacheMaxAge() { $max_age = parent::getCacheMaxAge(); // @todo Configurability of this will be removed in // https://www.drupal.org/node/2458763. return Cache::mergeMaxAges($max_age, (int) $this->configuration['cache']['max_age']); }
/** * {@inheritdoc} */ public function getCacheMaxAge() { $max_age = Cache::PERMANENT; // Applied contexts can affect the cache max age when this plugin is // involved in caching, collect and return them. foreach ($this->getContexts() as $context) { /** @var $context \Drupal\Core\Cache\CacheableDependencyInterface */ if ($context instanceof CacheableDependencyInterface) { $max_age = Cache::mergeMaxAges($max_age, $context->getCacheMaxAge()); } } return $max_age; }
/** * {@inheritdoc} */ public function getCacheMaxAge() { $cache_max_age = Cache::mergeMaxAges(parent::getCacheMaxAge(), $this->view->storage->getCacheMaxAge()); return $cache_max_age; }
/** * {@inheritdoc} * * The entire HTML: takes a #type 'page' and wraps it in a #type 'html'. */ public function renderResponse(array $main_content, Request $request, RouteMatchInterface $route_match) { list($page, $title) = $this->prepare($main_content, $request, $route_match); if (!isset($page['#type']) || $page['#type'] !== 'page') { throw new \LogicException('Must be #type page'); } $page['#title'] = $title; // Now render the rendered page.html.twig template inside the html.html.twig // template, and use the bubbled #attached metadata from $page to ensure we // load all attached assets. $html = ['#type' => 'html', 'page' => $page]; $html += element_info('html'); // The special page regions will appear directly in html.html.twig, not in // page.html.twig, hence add them here, just before rendering html.html.twig. $this->buildPageTopAndBottom($html); // The three parts of rendered markup in html.html.twig (page_top, page and // page_bottom) must be rendered with drupal_render_root(), so that their // #post_render_cache callbacks are executed (which may attach additional // assets). // html.html.twig must be able to render the final list of attached assets, // and hence may not execute any #post_render_cache_callbacks (because they // might add yet more assets to be attached), and therefore it must be // rendered with drupal_render(), not drupal_render_root(). $this->renderer->render($html['page'], TRUE); if (isset($html['page_top'])) { $this->renderer->render($html['page_top'], TRUE); } if (isset($html['page_bottom'])) { $this->renderer->render($html['page_bottom'], TRUE); } $content = $this->renderer->render($html); // Expose the cache contexts and cache tags associated with this page in a // X-Drupal-Cache-Contexts and X-Drupal-Cache-Tags header respectively. Also // associate the "rendered" cache tag. This allows us to invalidate the // entire render cache, regardless of the cache bin. $cache_contexts = []; $cache_tags = ['rendered']; $cache_max_age = Cache::PERMANENT; foreach (['page_top', 'page', 'page_bottom'] as $region) { if (isset($html[$region])) { $cache_contexts = Cache::mergeContexts($cache_contexts, $html[$region]['#cache']['contexts']); $cache_tags = Cache::mergeTags($cache_tags, $html[$region]['#cache']['tags']); $cache_max_age = Cache::mergeMaxAges($cache_max_age, $html[$region]['#cache']['max-age']); } } // Set the generator in the HTTP header. list($version) = explode('.', \Drupal::VERSION, 2); $response = new Response($content, 200, ['X-Drupal-Cache-Tags' => implode(' ', $cache_tags), 'X-Drupal-Cache-Contexts' => implode(' ', $this->cacheContexts->optimizeTokens($cache_contexts)), 'X-Generator' => 'Drupal ' . $version . ' (https://www.drupal.org)']); // If an explicit non-infinite max-age is specified by a part of the page, // respect that by applying it to the response's headers. if ($cache_max_age !== Cache::PERMANENT) { $response->setMaxAge($cache_max_age); } return $response; }
/** * Inherits the cacheability of the other access result, if any. * * @param \Drupal\Core\Access\AccessResultInterface $other * The other access result, whose cacheability (if any) to inherit. * * @return $this */ public function inheritCacheability(AccessResultInterface $other) { if ($other instanceof CacheableInterface) { $this->setCacheable($other->isCacheable()); $this->addCacheContexts($other->getCacheContexts()); $this->addCacheTags($other->getCacheTags()); $this->setCacheMaxAge(Cache::mergeMaxAges($this->getCacheMaxAge(), $other->getCacheMaxAge())); } else { $this->setCacheable(FALSE); } return $this; }