Пример #1
0
 /**
  * {@inheritdoc}
  */
 public function get($bin)
 {
     if (!isset($this->cacheBackends[$bin])) {
         $cache_backend = $this->cacheFactory->get($bin);
         $this->cacheBackends[$bin] = new CacheBackendWrapper($this->cacheDataCollector, $cache_backend, $bin);
     }
     return $this->cacheBackends[$bin];
 }
Пример #2
0
 /**
  * Constructs a new CachedStorage.
  *
  * @param \Drupal\Core\Config\StorageInterface $storage
  *   A configuration storage to be cached.
  * @param \Drupal\Core\Cache\CacheFactoryInterface $cache_factory
  *   A cache factory used for getting cache backends.
  */
 public function __construct(StorageInterface $storage, CacheFactoryInterface $cache_factory)
 {
     $this->storage = $storage;
     $this->cacheFactory = $cache_factory;
     $collection = $this->getCollectionName();
     if ($collection == StorageInterface::DEFAULT_COLLECTION) {
         $bin = 'config';
     } else {
         $bin = 'config_' . str_replace('.', '_', $collection);
     }
     $this->cache = $this->cacheFactory->get($bin);
 }
Пример #3
0
 /**
  * Asserts a render cache item.
  *
  * @param string $cid
  *   The expected cache ID.
  * @param mixed $data
  *   The expected data for that cache ID.
  * @param string $bin
  *   The expected cache bin.
  */
 protected function assertRenderCacheItem($cid, $data, $bin = 'render') {
   $cache_backend = $this->cacheFactory->get($bin);
   $cached = $cache_backend->get($cid);
   $this->assertNotFalse($cached, sprintf('Expected cache item "%s" exists.', $cid));
   if ($cached !== FALSE) {
     $this->assertEquals($data, $cached->data, sprintf('Cache item "%s" has the expected data.', $cid));
     $this->assertSame(Cache::mergeTags($data['#cache']['tags'], ['rendered']), $cached->tags, "The cache item's cache tags also has the 'rendered' cache tag.");
   }
 }
Пример #4
0
 /**
  * Test file storage on a cache hit in CachedStorage::read().
  */
 public function testReadNonExistentFileCached()
 {
     $name = 'config.does_not_exist';
     $cache = new MemoryBackend(__FUNCTION__);
     $cache->set($name, FALSE);
     $storage = $this->getMock('Drupal\\Core\\Config\\StorageInterface');
     $storage->expects($this->never())->method('read');
     $this->cacheFactory->expects($this->once())->method('get')->with('config')->will($this->returnValue($cache));
     $cachedStorage = new CachedStorage($storage, $this->cacheFactory);
     $this->assertFalse($cachedStorage->read($name));
 }
Пример #5
0
 /**
  * {@inheritdoc}
  */
 public function set(array &$elements, array $pre_bubbling_elements)
 {
     // Form submissions rely on the form being built during the POST request,
     // and render caching of forms prevents this from happening.
     // @todo remove the isMethodSafe() check when
     //       https://www.drupal.org/node/2367555 lands.
     if (!$this->requestStack->getCurrentRequest()->isMethodSafe() || !($cid = $this->createCacheID($elements))) {
         return FALSE;
     }
     $data = $this->getCacheableRenderArray($elements);
     $bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'render';
     $expire = $elements['#cache']['max-age'] === Cache::PERMANENT ? Cache::PERMANENT : (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME') + $elements['#cache']['max-age'];
     $cache = $this->cacheFactory->get($bin);
     // Calculate the pre-bubbling CID.
     $pre_bubbling_cid = $this->createCacheID($pre_bubbling_elements);
     // Two-tier caching: detect different CID post-bubbling, create redirect,
     // update redirect if different set of cache contexts.
     // @see \Drupal\Core\Render\RendererInterface::render()
     // @see ::get()
     if ($pre_bubbling_cid && $pre_bubbling_cid !== $cid) {
         // The cache redirection strategy we're implementing here is pretty
         // simple in concept. Suppose we have the following render structure:
         // - A (pre-bubbling, specifies #cache['keys'] = ['foo'])
         // -- B (specifies #cache['contexts'] = ['b'])
         //
         // At the time that we're evaluating whether A's rendering can be
         // retrieved from cache, we won't know the contexts required by its
         // children (the children might not even be built yet), so cacheGet()
         // will only be able to get what is cached for a $cid of 'foo'. But at
         // the time we're writing to that cache, we do know all the contexts that
         // were specified by all children, so what we need is a way to
         // persist that information between the cache write and the next cache
         // read. So, what we can do is store the following into 'foo':
         // [
         //   '#cache_redirect' => TRUE,
         //   '#cache' => [
         //     ...
         //     'contexts' => ['b'],
         //   ],
         // ]
         //
         // This efficiently lets cacheGet() redirect to a $cid that includes all
         // of the required contexts. The strategy is on-demand: in the case where
         // there aren't any additional contexts required by children that aren't
         // already included in the parent's pre-bubbled #cache information, no
         // cache redirection is needed.
         //
         // When implementing this redirection strategy, special care is needed to
         // resolve potential cache ping-pong problems. For example, consider the
         // following render structure:
         // - A (pre-bubbling, specifies #cache['keys'] = ['foo'])
         // -- B (pre-bubbling, specifies #cache['contexts'] = ['b'])
         // --- C (pre-bubbling, specifies #cache['contexts'] = ['c'])
         // --- D (pre-bubbling, specifies #cache['contexts'] = ['d'])
         //
         // Additionally, suppose that:
         // - C only exists for a 'b' context value of 'b1'
         // - D only exists for a 'b' context value of 'b2'
         // This is an acceptable variation, since B specifies that its contents
         // vary on context 'b'.
         //
         // A naive implementation of cache redirection would result in the
         // following:
         // - When a request is processed where context 'b' = 'b1', what would be
         //   cached for a $pre_bubbling_cid of 'foo' is:
         //   [
         //     '#cache_redirect' => TRUE,
         //     '#cache' => [
         //       ...
         //       'contexts' => ['b', 'c'],
         //     ],
         //   ]
         // - When a request is processed where context 'b' = 'b2', we would
         //   retrieve the above from cache, but when following that redirection,
         //   get a cache miss, since we're processing a 'b' context value that
         //   has not yet been cached. Given the cache miss, we would continue
         //   with rendering the structure, perform the required context bubbling
         //   and then overwrite the above item with:
         //   [
         //     '#cache_redirect' => TRUE,
         //     '#cache' => [
         //       ...
         //       'contexts' => ['b', 'd'],
         //     ],
         //   ]
         // - Now, if a request comes in where context 'b' = 'b1' again, the above
         //   would redirect to a cache key that doesn't exist, since we have not
         //   yet cached an item that includes 'b'='b1' and something for 'd'. So
         //   we would process this request as a cache miss, at the end of which,
         //   we would overwrite the above item back to:
         //   [
         //     '#cache_redirect' => TRUE,
         //     '#cache' => [
         //       ...
         //       'contexts' => ['b', 'c'],
         //     ],
         //   ]
         // - The above would always result in accurate renderings, but would
         //   result in poor performance as we keep processing requests as cache
         //   misses even though the target of the redirection is cached, and
         //   it's only the redirection element itself that is creating the
         //   ping-pong problem.
         //
         // A way to resolve the ping-pong problem is to eventually reach a cache
         // state where the redirection element includes all of the contexts used
         // throughout all requests:
         // [
         //   '#cache_redirect' => TRUE,
         //   '#cache' => [
         //     ...
         //     'contexts' => ['b', 'c', 'd'],
         //   ],
         // ]
         //
         // We can't reach that state right away, since we don't know what the
         // result of future requests will be, but we can incrementally move
         // towards that state by progressively merging the 'contexts' value
         // across requests. That's the strategy employed below and tested in
         // \Drupal\Tests\Core\Render\RendererBubblingTest::testConditionalCacheContextBubblingSelfHealing().
         // The set of cache contexts for this element, including the bubbled ones,
         // for which we are handling a cache miss.
         $cache_contexts = $data['#cache']['contexts'];
         // Get the contexts by which this element should be varied according to
         // the current redirecting cache item, if any.
         $stored_cache_contexts = [];
         $stored_cache_tags = [];
         if ($stored_cache_redirect = $cache->get($pre_bubbling_cid)) {
             $stored_cache_contexts = $stored_cache_redirect->data['#cache']['contexts'];
             $stored_cache_tags = $stored_cache_redirect->data['#cache']['tags'];
         }
         // Calculate the union of the cache contexts for this request and the
         // stored cache contexts.
         $merged_cache_contexts = Cache::mergeContexts($stored_cache_contexts, $cache_contexts);
         // Stored cache contexts incomplete: this request causes cache contexts to
         // be added to the redirecting cache item.
         if (array_diff($merged_cache_contexts, $stored_cache_contexts)) {
             $redirect_data = ['#cache_redirect' => TRUE, '#cache' => ['keys' => $elements['#cache']['keys'], 'contexts' => $merged_cache_contexts, 'tags' => Cache::mergeTags($stored_cache_tags, $data['#cache']['tags'])]];
             $cache->set($pre_bubbling_cid, $redirect_data, $expire, Cache::mergeTags($redirect_data['#cache']['tags'], ['rendered']));
         }
         // Current cache contexts incomplete: this request only uses a subset of
         // the cache contexts stored in the redirecting cache item. Vary by these
         // additional (conditional) cache contexts as well, otherwise the
         // redirecting cache item would be pointing to a cache item that can never
         // exist.
         if (array_diff($merged_cache_contexts, $cache_contexts)) {
             // Recalculate the cache ID.
             $recalculated_cid_pseudo_element = ['#cache' => ['keys' => $elements['#cache']['keys'], 'contexts' => $merged_cache_contexts]];
             $cid = $this->createCacheID($recalculated_cid_pseudo_element);
             // Ensure the about-to-be-cached data uses the merged cache contexts.
             $data['#cache']['contexts'] = $merged_cache_contexts;
         }
     }
     $cache->set($cid, $data, $expire, Cache::mergeTags($data['#cache']['tags'], ['rendered']));
 }
Пример #6
0
 /**
  * Sets up a memory-based render cache back-end.
  */
 protected function setupMemoryCache()
 {
     $this->memoryCache = $this->memoryCache ?: new MemoryBackend('render');
     $this->cacheFactory->expects($this->atLeastOnce())->method('get')->willReturn($this->memoryCache);
 }