class RenderCache

Same name and namespace in other branches
  1. 9 core/lib/Drupal/Core/Render/RenderCache.php \Drupal\Core\Render\RenderCache
  2. 8.9.x core/lib/Drupal/Core/Render/RenderCache.php \Drupal\Core\Render\RenderCache
  3. 11.x core/lib/Drupal/Core/Render/RenderCache.php \Drupal\Core\Render\RenderCache

Wraps the caching logic for the render caching system.

@internal

Hierarchy

Expanded class hierarchy of RenderCache

File

core/lib/Drupal/Core/Render/RenderCache.php, line 15

Namespace

Drupal\Core\Render
View source
class RenderCache implements RenderCacheInterface {
  
  /**
   * The request stack.
   *
   * @var \Symfony\Component\HttpFoundation\RequestStack
   */
  protected $requestStack;
  
  /**
   * The variation cache factory.
   *
   * @var \Drupal\Core\Cache\VariationCacheFactoryInterface
   */
  protected $cacheFactory;
  
  /**
   * The cache contexts manager.
   *
   * @var \Drupal\Core\Cache\Context\CacheContextsManager
   */
  protected $cacheContextsManager;
  
  /**
   * Constructs a new RenderCache object.
   *
   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
   *   The request stack.
   * @param \Drupal\Core\Cache\VariationCacheFactoryInterface $cache_factory
   *   The variation cache factory.
   * @param \Drupal\Core\Cache\Context\CacheContextsManager $cache_contexts_manager
   *   The cache contexts manager.
   */
  public function __construct(RequestStack $request_stack, $cache_factory, CacheContextsManager $cache_contexts_manager) {
    if ($cache_factory instanceof CacheFactoryInterface) {
      @trigger_error('Injecting ' . __CLASS__ . ' with the "cache_factory" service is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Use "variation_cache_factory" instead. See https://www.drupal.org/node/3365546', E_USER_DEPRECATED);
      $cache_factory = \Drupal::service('variation_cache_factory');
    }
    $this->requestStack = $request_stack;
    $this->cacheFactory = $cache_factory;
    $this->cacheContextsManager = $cache_contexts_manager;
  }
  
  /**
   * {@inheritdoc}
   */
  public function get(array $elements) {
    if (!$this->isElementCacheable($elements)) {
      return FALSE;
    }
    $bin = $elements['#cache']['bin'] ?? 'render';
    if (($cache_bin = $this->cacheFactory
      ->get($bin)) && ($cache = $cache_bin->get($elements['#cache']['keys'], CacheableMetadata::createFromRenderArray($elements)))) {
      if (!$this->requestStack
        ->getCurrentRequest()
        ->isMethodCacheable()) {
        if (!empty(array_filter($cache->tags, fn(string $tag) => str_starts_with($tag, 'CACHE_MISS_IF_UNCACHEABLE_HTTP_METHOD:')))) {
          return FALSE;
        }
      }
      return $cache->data;
    }
    return FALSE;
  }
  
  /**
   * {@inheritdoc}
   */
  public function set(array &$elements, array $pre_bubbling_elements) {
    // Avoid setting cache items on POST requests, this ensures that cache items
    // with a very low hit rate won't enter the cache. All render elements
    // except forms will still be retrieved from cache when available.
    if (!$this->requestStack
      ->getCurrentRequest()
      ->isMethodCacheable() || !$this->isElementCacheable($elements)) {
      return FALSE;
    }
    $bin = $elements['#cache']['bin'] ?? 'render';
    $cache_bin = $this->cacheFactory
      ->get($bin);
    $data = $this->getCacheableRenderArray($elements);
    $cache_bin->set($elements['#cache']['keys'], $data, CacheableMetadata::createFromRenderArray($data)->addCacheTags([
      'rendered',
    ]), CacheableMetadata::createFromRenderArray($pre_bubbling_elements));
  }
  
  /**
   * {@inheritdoc}
   */
  public function getCacheableRenderArray(array $elements) {
    $data = [
      '#markup' => $elements['#markup'],
      '#attached' => $elements['#attached'],
      '#cache' => [
        'contexts' => $elements['#cache']['contexts'],
        'tags' => $elements['#cache']['tags'],
        'max-age' => $elements['#cache']['max-age'],
      ],
    ];
    // Preserve cacheable items if specified. If we are preserving any cacheable
    // children of the element, we assume we are only interested in their
    // individual markup and not the parent's one, thus we empty it to minimize
    // the cache entry size.
    if (!empty($elements['#cache_properties']) && is_array($elements['#cache_properties'])) {
      $data['#cache_properties'] = $elements['#cache_properties'];
      // Extract all the cacheable items from the element using cache
      // properties.
      $cacheable_items = array_intersect_key($elements, array_flip($elements['#cache_properties']));
      $cacheable_children = Element::children($cacheable_items);
      if ($cacheable_children) {
        $data['#markup'] = '';
        // Cache only cacheable children's markup.
        foreach ($cacheable_children as $key) {
          // We can assume that #markup is safe at this point.
          $cacheable_items[$key] = [
            '#markup' => Markup::create($cacheable_items[$key]['#markup']),
          ];
        }
      }
      $data += $cacheable_items;
    }
    $data['#markup'] = Markup::create($data['#markup']);
    return $data;
  }
  
  /**
   * Checks whether a renderable array can be cached.
   *
   * This allows us to not even have to instantiate the cache backend if a
   * renderable array does not have any cache keys or specifies a zero cache
   * max age.
   *
   * @param array $element
   *   A renderable array.
   *
   * @return bool
   *   Whether the renderable array is cacheable.
   */
  protected function isElementCacheable(array $element) {
    // If the maximum age is zero, then caching is effectively prohibited.
    if (isset($element['#cache']['max-age']) && $element['#cache']['max-age'] === 0) {
      return FALSE;
    }
    return isset($element['#cache']['keys']);
  }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title Overrides
RenderCache::$cacheContextsManager protected property The cache contexts manager.
RenderCache::$cacheFactory protected property The variation cache factory.
RenderCache::$requestStack protected property The request stack.
RenderCache::get public function Gets the cached, pre-rendered element of a renderable element from cache. Overrides RenderCacheInterface::get 1
RenderCache::getCacheableRenderArray public function Gets a cacheable render array for a render array and its rendered output. Overrides RenderCacheInterface::getCacheableRenderArray
RenderCache::isElementCacheable protected function Checks whether a renderable array can be cached.
RenderCache::set public function Caches the rendered output of a renderable array. Overrides RenderCacheInterface::set 1
RenderCache::__construct public function Constructs a new RenderCache object. 1

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.