/Drupal 8

public function CacheContextsManager::optimizeTokens

public CacheContextsManager::optimizeTokens(array $context_tokens)

Optimizes cache context tokens (the minimal representative subset).

A minimal representative subset means that any cache context token in the given set of cache context tokens that is a property of another cache context cache context token in the set, is removed.

Hence a minimal representative subset is the most compact representation possible of a set of cache context tokens, that still captures the entire universe of variations.

If a cache context is being optimized away, it is able to set cacheable metadata for itself which will be bubbled up.

For example, when caching per user ('user'), also caching per role ('user.roles') is meaningless because "per role" is implied by "per user".

In the following examples, remember that the period indicates hierarchy and the colon can be used to get a specific value of a calculated cache context:

  • ['a', 'a.b'] -> ['a']
  • ['a', 'a.b.c'] -> ['a']
  • ['a.b', 'a.b.c'] -> ['a.b']
  • ['a', 'a.b', 'a.b.c'] -> ['a']
  • ['x', 'x:foo'] -> ['x']
  • ['a', 'a.b.c:bar'] -> ['a']


string[] $context_tokens: A set of cache context tokens.

Return value

string[] A representative subset of the given set of cache context tokens..


core/lib/Drupal/Core/Cache/Context/CacheContextsManager.php, line 159


Converts cache context tokens into cache keys.




public function optimizeTokens(array $context_tokens) {
  $optimized_content_tokens = [];
  foreach ($context_tokens as $context_token) {

    // Extract the parameter if available.
    $parameter = NULL;
    $context_id = $context_token;
    if (strpos($context_token, ':') !== FALSE) {
      list($context_id, $parameter) = explode(':', $context_token);

    // Context tokens without:
    // - a period means they don't have a parent
    // - a colon means they're not a specific value of a cache context
    // hence no optimizations are possible.
    if (strpos($context_token, '.') === FALSE && strpos($context_token, ':') === FALSE) {
      $optimized_content_tokens[] = $context_token;
    // Check cacheability. If the context defines a max-age of 0, then it
    // can not be optimized away. Pass the parameter along if we have one.
    elseif ($this->getService($context_id)->getCacheableMetadata($parameter)->getCacheMaxAge() === 0) {
      $optimized_content_tokens[] = $context_token;
    // The context token has a period or a colon. Iterate over all ancestor
    // cache contexts. If one exists, omit the context token.
    else {
      $ancestor_found = FALSE;
      // Treat a colon like a period, that allows us to consider 'a' the
      // ancestor of 'a:foo', without any additional code for the colon.
      $ancestor = str_replace(':', '.', $context_token);
      do {
        $ancestor = substr($ancestor, 0, strrpos($ancestor, '.'));
        if (in_array($ancestor, $context_tokens)) {
          // An ancestor cache context is in $context_tokens, hence this cache
          // context is implied.
          $ancestor_found = TRUE;

      } while (!$ancestor_found && strpos($ancestor, '.') !== FALSE);
      if (!$ancestor_found) {
        $optimized_content_tokens[] = $context_token;
  return $optimized_content_tokens;

© 2001–2016 by the original authors
Licensed under the GNU General Public License, version 2 and later.
Drupal is a registered trademark of Dries Buytaert.