annotate core/lib/Drupal/Core/Template/TwigEnvironment.php @ 19:fa3358dc1485 tip

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