TwigPhpStorageCache.php 3.64 KB
Newer Older
1 2 3 4
<?php

namespace Drupal\Core\Template;

5
use Drupal\Component\Utility\Crypt;
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\PhpStorage\PhpStorageFactory;

/**
 * Provides an alternate cache storage for Twig using PhpStorage.
 *
 * This class is designed to work on setups with multiple webheads using a local
 * filesystem for the twig cache. When generating the cache key, a hash value
 * depending on the enabled extensions is included. This prevents stale
 * templates from being reused when twig extensions are enabled or disabled.
 *
 * @see \Drupal\Core\DependencyInjection\Compiler\TwigExtensionPass
 */
class TwigPhpStorageCache implements \Twig_CacheInterface {

21 22 23 24 25
  /**
   * The maximum length for each part of the cache key suffix.
   */
  const SUFFIX_SUBSTRING_LENGTH = 25;

26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
  /**
   * The cache object used for auto-refresh via mtime.
   *
   * @var \Drupal\Core\Cache\CacheBackendInterface
   */
  protected $cache;

  /**
   * The PhpStorage object used for storing the templates.
   *
   * @var \Drupal\Component\PhpStorage\PhpStorageInterface
   */
  protected $storage;

  /**
   * The template cache filename prefix.
   *
   * @var string
   */
  protected $templateCacheFilenamePrefix;

  /**
   * Store cache backend and other information internally.
   *
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
   *   The cache bin.
52 53
   * @param string $twig_cache_prefix
   *   A Twig cache file prefix that changes when Twig extensions change.
54
   */
55
  public function __construct(CacheBackendInterface $cache, $twig_cache_prefix) {
56
    $this->cache = $cache;
57
    $this->templateCacheFilenamePrefix = $twig_cache_prefix;
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
  }

  /**
   * Gets the PHP code storage object to use for the compiled Twig files.
   *
   * @return \Drupal\Component\PhpStorage\PhpStorageInterface
   */
  protected function storage() {
    if (!isset($this->storage)) {
      $this->storage = PhpStorageFactory::get('twig');
    }
    return $this->storage;
  }

  /**
   * {@inheritdoc}
   */
  public function generateKey($name, $className) {
76 77
    if (strpos($name, '{# inline_template_start #}') === 0) {
      // $name is an inline template, and can have characters that are not valid
78 79
      // for a filename. $suffix is unique for each inline template so we just
      // use the generic name 'inline-template' here.
80 81 82 83 84 85
      $name = 'inline-template';
    }
    else {
      $name = basename($name);
    }

86 87 88 89 90 91 92 93 94 95 96 97
    // Windows (and some encrypted Linux systems) only support 255 characters in
    // a path. On Windows a requirements error is displayed and installation is
    // blocked if Drupal's public files path is longer than 120 characters.
    // Thus, to always be less than 255, file paths may not be more than 135
    // characters long. Using the default PHP file storage class, the Twig cache
    // file path will be 124 characters long at most, which provides a margin of
    // safety.
    $suffix = substr($name, 0, self::SUFFIX_SUBSTRING_LENGTH) . '_';
    $suffix .= substr(Crypt::hashBase64($className), 0, self::SUFFIX_SUBSTRING_LENGTH);

    // The cache prefix is what gets invalidated.
    return $this->templateCacheFilenamePrefix . '_' . $suffix;
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
  }

  /**
   * {@inheritdoc}
   */
  public function load($key) {
    $this->storage()->load($key);
  }

  /**
   * {@inheritdoc}
   */
  public function write($key, $content) {
    $this->storage()->save($key, $content);
    // Save the last mtime.
    $cid = 'twig:' . $key;
    $this->cache->set($cid, REQUEST_TIME);
  }

  /**
   * {@inheritdoc}
   */
  public function getTimestamp($key) {
    $cid = 'twig:' . $key;
    if ($cache = $this->cache->get($cid)) {
      return $cache->data;
    }
    else {
      return 0;
    }
  }

}