TwigEnvironment.php 5.2 KB

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