diff --git a/core/lib/Drupal/Core/Template/TwigEnvironment.php b/core/lib/Drupal/Core/Template/TwigEnvironment.php index 6f6e7d2..da75924 100644 --- a/core/lib/Drupal/Core/Template/TwigEnvironment.php +++ b/core/lib/Drupal/Core/Template/TwigEnvironment.php @@ -9,7 +9,6 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Cache\CacheBackendInterface; -use Drupal\Core\PhpStorage\PhpStorageFactory; use Drupal\Core\Render\SafeString; /** @@ -23,20 +22,6 @@ class TwigEnvironment extends \Twig_Environment { /** - * The cache object used for auto-refresh via mtime. - * - * @var \Drupal\Core\Cache\CacheBackendInterface - */ - protected $cache_object = NULL; - - /** - * The PhpStorage object used for storing the templates. - * - * @var \Drupal\Core\PhpStorage\PhpStorageFactory - */ - protected $storage = NULL; - - /** * The template cache filename prefix. * * @var string @@ -66,7 +51,6 @@ class TwigEnvironment extends \Twig_Environment { * The options for the Twig environment. */ public function __construct($root, CacheBackendInterface $cache, $twig_extension_hash, \Twig_LoaderInterface $loader = NULL, $options = array()) { - $this->cache_object = $cache; $this->templateCacheFilenamePrefix = $twig_extension_hash; // Ensure that twig.engine is loaded, given that it is needed to render a @@ -84,118 +68,13 @@ public function __construct($root, CacheBackendInterface $cache, $twig_extension // Ensure autoescaping is always on. $options['autoescape'] = 'html'; - $this->loader = $loader; - parent::__construct($this->loader, $options); - } - - /** - * Checks if the compiled template needs an update. - */ - protected function isFresh($cache_filename, $name) { - $cid = 'twig:' . $cache_filename; - $obj = $this->cache_object->get($cid); - $mtime = isset($obj->data) ? $obj->data : FALSE; - return $mtime === FALSE || $this->isTemplateFresh($name, $mtime); - } - - /** - * Compile the source and write the compiled template to disk. - */ - public function updateCompiledTemplate($cache_filename, $name) { - $source = $this->loader->getSource($name); - $compiled_source = $this->compileSource($source, $name); - $this->storage()->save($cache_filename, $compiled_source); - // Save the last modification time - $cid = 'twig:' . $cache_filename; - $this->cache_object->set($cid, REQUEST_TIME); - } - - /** - * {@inheritdoc} - */ - public function getCacheFilename($name) { - // We override the cache filename in order to avoid issues with not using - // shared filesystems. The Twig templates for example rely on available Twig - // extensions, so we use the twig extension hash which varies by extensions - // and their mtime. - // @see \Drupal\Core\DependencyInjection\Compiler\TwigExtensionPass - - if (!$this->getCache()) { - return FALSE; + if ($options['cache'] === TRUE) { + // @todo Try to use $this->setCache()? + $options['cache'] = new TwigPhpStorageCache($cache, $twig_extension_hash); } - $class = substr($this->getTemplateClass($name), strlen($this->getTemplateClassPrefix())); - - // The first part is what is invalidated. - return $this->templateCacheFilenamePrefix . '_' . basename($name) . '_' . $class; - } - - /** - * Implements Twig_Environment::loadTemplate(). - * - * We need to overwrite this function to integrate with drupal_php_storage(). - * - * This is a straight copy from loadTemplate() changed to use - * drupal_php_storage(). - * - * @param string $name - * The template name or the string which should be rendered as template. - * @param int $index - * The index if it is an embedded template. - * - * @return \Twig_TemplateInterface - * A template instance representing the given template name. - * - * @throws \Twig_Error_Loader - * When the template cannot be found. - * @throws \Twig_Error_Syntax - * When an error occurred during compilation. - */ - public function loadTemplate($name, $index = NULL) { - $cls = $this->getTemplateClass($name, $index); - - if (isset($this->loadedTemplates[$cls])) { - return $this->loadedTemplates[$cls]; - } - - if (!class_exists($cls, FALSE)) { - $cache_filename = $this->getCacheFilename($name); - - if ($cache_filename !== FALSE) { - // If autoreload is on, check that the template has not been - // modified since the last compilation. - if ($this->isAutoReload() && !$this->isFresh($cache_filename, $name)) { - $this->updateCompiledTemplate($cache_filename, $name); - } - - if (!$this->storage()->load($cache_filename)) { - $this->updateCompiledTemplate($cache_filename, $name); - $this->storage()->load($cache_filename); - } - } - if (!class_exists($cls, FALSE)) { - $compiled_source = $this->compileSource($this->loader->getSource($name), $name); - eval('?' . '>' . $compiled_source); - } - } - - if (!$this->runtimeInitialized) { - $this->initRuntime(); - } - - return $this->loadedTemplates[$cls] = new $cls($this); - } - - /** - * 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; + $this->loader = $loader; + parent::__construct($this->loader, $options); } /** diff --git a/core/lib/Drupal/Core/Template/TwigPhpStorageCache.php b/core/lib/Drupal/Core/Template/TwigPhpStorageCache.php new file mode 100644 index 0000000..22d33b0 --- /dev/null +++ b/core/lib/Drupal/Core/Template/TwigPhpStorageCache.php @@ -0,0 +1,120 @@ +cache_object = $cache; + $this->templateCacheFilenamePrefix = $twig_extension_hash; + } + + /** + * 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) { + // We override the cache filename in order to avoid issues with not using + // shared filesystems. The Twig templates for example rely on available Twig + // extensions, so we use the twig extension hash which varies by extensions + // and their mtime. + // @see \Drupal\Core\DependencyInjection\Compiler\TwigExtensionPass + $hash = hash('sha256', $className); + + // The first part is what is invalidated. + return $this->templateCacheFilenamePrefix . '_' . basename($name) . '_' . $hash; + } + + /** + * {@inheritdoc} + */ + public function has($key) { + return $this->storage()->exists($key); + } + + /** + * {@inheritdoc} + */ + public function load($key) { + if ($this->has($key)) { + $this->storage()->load($key); + } + else { + // @todo Add race condition handling stuff around here? + // Compile and eval? How do we get to the Twig env from here? + // Maybe we can just fall back to the null cache somehow or incorporate this into write(). + } + } + + /** + * {@inheritdoc} + */ + public function write($key, $content) { + $this->storage()->save($key, $content); + // Save the last modification time. + $cid = 'twig:' . $key; + $this->cache_object->set($cid, REQUEST_TIME); + } + + /** + * {@inheritdoc} + */ + public function getTimestamp($key) { + $cid = 'twig:' . $key; + if ($cache = $this->cache_object->get($cid)) { + return $cache->data; + } + } + +}