Pull merge.
[yaffs-website] / web / core / lib / Drupal / Core / Template / TwigEnvironment.php
1 <?php
2
3 namespace Drupal\Core\Template;
4
5 use Drupal\Core\Cache\CacheBackendInterface;
6 use Drupal\Core\PhpStorage\PhpStorageFactory;
7 use Drupal\Core\Render\Markup;
8 use Drupal\Core\State\StateInterface;
9
10 /**
11  * A class that defines a Twig environment for Drupal.
12  *
13  * Instances of this class are used to store the configuration and extensions,
14  * and are used to load templates from the file system or other locations.
15  *
16  * @see core\vendor\twig\twig\lib\Twig\Environment.php
17  */
18 class TwigEnvironment extends \Twig_Environment {
19
20   /**
21    * Key name of the Twig cache prefix metadata key-value pair in State.
22    */
23   const CACHE_PREFIX_METADATA_KEY = 'twig_extension_hash_prefix';
24
25   /**
26    * The state service.
27    *
28    * @var \Drupal\Core\State\StateInterface
29    */
30   protected $state;
31
32   /**
33    * Static cache of template classes.
34    *
35    * @var array
36    */
37   protected $templateClasses;
38
39   protected $twigCachePrefix = '';
40
41   /**
42    * Constructs a TwigEnvironment object and stores cache and storage
43    * internally.
44    *
45    * @param string $root
46    *   The app root.
47    * @param \Drupal\Core\Cache\CacheBackendInterface $cache
48    *   The cache bin.
49    * @param string $twig_extension_hash
50    *   The Twig extension hash.
51    * @param \Drupal\Core\State\StateInterface $state
52    *   The state service.
53    * @param \Twig_LoaderInterface $loader
54    *   The Twig loader or loader chain.
55    * @param array $options
56    *   The options for the Twig environment.
57    */
58   public function __construct($root, CacheBackendInterface $cache, $twig_extension_hash, StateInterface $state, \Twig_LoaderInterface $loader = NULL, $options = []) {
59     $this->state = $state;
60
61     // Ensure that twig.engine is loaded, given that it is needed to render a
62     // template because functions like TwigExtension::escapeFilter() are called.
63     require_once $root . '/core/themes/engines/twig/twig.engine';
64
65     $this->templateClasses = [];
66
67     $options += [
68       // @todo Ensure garbage collection of expired files.
69       'cache' => TRUE,
70       'debug' => FALSE,
71       'auto_reload' => NULL,
72     ];
73     // Ensure autoescaping is always on.
74     $options['autoescape'] = 'html';
75
76     $policy = new TwigSandboxPolicy();
77     $sandbox = new \Twig_Extension_Sandbox($policy, TRUE);
78     $this->addExtension($sandbox);
79
80     if ($options['cache'] === TRUE) {
81       $current = $state->get(static::CACHE_PREFIX_METADATA_KEY, ['twig_extension_hash' => '']);
82       if ($current['twig_extension_hash'] !== $twig_extension_hash || empty($current['twig_cache_prefix'])) {
83         $current = [
84           'twig_extension_hash' => $twig_extension_hash,
85           // Generate a new prefix which invalidates any existing cached files.
86           'twig_cache_prefix' => uniqid(),
87
88         ];
89         $state->set(static::CACHE_PREFIX_METADATA_KEY, $current);
90       }
91       $this->twigCachePrefix = $current['twig_cache_prefix'];
92
93       $options['cache'] = new TwigPhpStorageCache($cache, $this->twigCachePrefix);
94     }
95
96     $this->loader = $loader;
97     parent::__construct($this->loader, $options);
98   }
99
100   /**
101    * Invalidates all compiled Twig templates.
102    *
103    * @see \drupal_flush_all_caches
104    */
105   public function invalidate() {
106     PhpStorageFactory::get('twig')->deleteAll();
107     $this->templateClasses = [];
108     $this->loadedTemplates = [];
109     $this->state->delete(static::CACHE_PREFIX_METADATA_KEY);
110   }
111
112   /**
113    * Get the cache prefixed used by \Drupal\Core\Template\TwigPhpStorageCache
114    *
115    * @return string
116    *   The file cache prefix, or empty string if the cache is disabled.
117    */
118   public function getTwigCachePrefix() {
119     return $this->twigCachePrefix;
120   }
121
122   /**
123    * Gets the template class associated with the given string.
124    *
125    * @param string $name
126    *   The name for which to calculate the template class name.
127    * @param int $index
128    *   The index if it is an embedded template.
129    *
130    * @return string
131    *   The template class name.
132    */
133   public function getTemplateClass($name, $index = NULL) {
134     // We override this method to add caching because it gets called multiple
135     // times when the same template is used more than once. For example, a page
136     // rendering 50 nodes without any node template overrides will use the same
137     // node.html.twig for the output of each node and the same compiled class.
138     $cache_index = $name . (NULL === $index ? '' : '_' . $index);
139     if (!isset($this->templateClasses[$cache_index])) {
140       $this->templateClasses[$cache_index] = $this->templateClassPrefix . hash('sha256', $this->loader->getCacheKey($name)) . (NULL === $index ? '' : '_' . $index);
141     }
142     return $this->templateClasses[$cache_index];
143   }
144
145   /**
146    * Renders a twig string directly.
147    *
148    * Warning: You should use the render element 'inline_template' together with
149    * the #template attribute instead of this method directly.
150    * On top of that you have to ensure that the template string is not dynamic
151    * but just an ordinary static php string, because there may be installations
152    * using read-only PHPStorage that want to generate all possible twig
153    * templates as part of a build step. So it is important that an automated
154    * script can find the templates and extract them. This is only possible if
155    * the template is a regular string.
156    *
157    * @param string $template_string
158    *   The template string to render with placeholders.
159    * @param array $context
160    *   An array of parameters to pass to the template.
161    *
162    * @return \Drupal\Component\Render\MarkupInterface|string
163    *   The rendered inline template as a Markup object.
164    *
165    * @see \Drupal\Core\Template\Loader\StringLoader::exists()
166    */
167   public function renderInline($template_string, array $context = []) {
168     // Prefix all inline templates with a special comment.
169     $template_string = '{# inline_template_start #}' . $template_string;
170     return Markup::create($this->loadTemplate($template_string, NULL)->render($context));
171   }
172
173 }