From e553135b3a04d77aca667e4dbcefd79ad2e17eec Mon Sep 17 00:00:00 2001
From: Fabian Franz <github@fabian-franz.de>
Date: Thu, 8 Nov 2012 17:36:14 +0100
Subject: [PATCH] Issue #1815250: Declare render variables and optimize templates at compile time.

Still todo:

* Need to make sure the template class name is based on the content of dynamic vars.
* Need to make sure this still works with sub templates.
---
 core/includes/theme.inc                           |    5 +++++
 core/lib/Drupal/Core/Template/TwigEnvironment.php |   12 ++++++++++++
 core/lib/Drupal/Core/Template/TwigNodeVisitor.php |   13 +++++++++++--
 core/modules/node/node.module                     |    3 +++
 core/themes/engines/twig/twig.engine              |    8 +++++++-
 5 files changed, 38 insertions(+), 3 deletions(-)

diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index d2fe902..a65c8cb 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -995,6 +995,9 @@ function theme($hook, $variables = array()) {
   }
   elseif (!empty($info['render element'])) {
     $variables += array($info['render element'] => array());
+
+    // Declare render variables
+    $variables['theme_render_variables'][] = $info['render element'];
   }
 
   // Invoke the variable processors, if any. The processors may specify
@@ -2458,6 +2461,8 @@ function template_preprocess(&$variables, $hook) {
     'title_attributes' => clone $default_attributes,
     'content_attributes' => clone $default_attributes,
   );
+  $variables['theme_render_variables'][] = 'title_prefix';
+  $variables['theme_render_variables'][] = 'title_suffix';
 
   // Initialize html class attribute for the current hook.
   $variables['attributes']['class'][] = drupal_html_class($hook);
diff --git a/core/lib/Drupal/Core/Template/TwigEnvironment.php b/core/lib/Drupal/Core/Template/TwigEnvironment.php
index bc7ab65..09630c5 100644
--- a/core/lib/Drupal/Core/Template/TwigEnvironment.php
+++ b/core/lib/Drupal/Core/Template/TwigEnvironment.php
@@ -18,6 +18,7 @@
 class TwigEnvironment extends \Twig_Environment {
   protected $cache_object = NULL;
   protected $storage = NULL;
+  protected $dynamic_vars = array();
 
   /**
    * Constructs a TwigEnvironment object and stores cache and storage
@@ -97,4 +98,15 @@ public function loadTemplate($name, $index = NULL) {
     return $this->loadedTemplates[$cls] = new $cls($this);
   }
 
+  public function setDynamicVars($vars, $name) {
+    $this->dynamic_vars[$name] = $vars;
+  }
+
+  public function getDynamicVars($name) {
+    if (isset($this->dynamic_vars[$name])) {
+      return $this->dynamic_vars[$name];
+    }
+    return NULL;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Template/TwigNodeVisitor.php b/core/lib/Drupal/Core/Template/TwigNodeVisitor.php
index 0cf5465..48bc609 100644
--- a/core/lib/Drupal/Core/Template/TwigNodeVisitor.php
+++ b/core/lib/Drupal/Core/Template/TwigNodeVisitor.php
@@ -27,10 +27,16 @@ class TwigNodeVisitor implements \Twig_NodeVisitorInterface {
    */
   protected $isReference = FALSE;
 
+  protected $dynamic_vars = array();
+
   /**
    * Implements Twig_NodeVisitorInterface::enterNode().
    */
   function enterNode(\Twig_NodeInterface $node, \Twig_Environment $env) {
+   if ($node instanceof \Twig_Node_Module) {
+     $filename = $node->getAttribute('filename');
+     $this->dynamic_vars = $env->getDynamicVars($filename);
+   }
    if ($node instanceof \Twig_Node_Expression_Function) {
       $name = $node->getAttribute('name');
       $func = $env->getFunction($name);
@@ -61,7 +67,7 @@ function enterNode(\Twig_NodeInterface $node, \Twig_Environment $env) {
    * @see twig_render
    */
   function leaveNode(\Twig_NodeInterface $node, \Twig_Environment $env) {
-    if ($node instanceof \Twig_Node_Print) {
+    if ($node instanceof \Twig_Node_Print && $this->isReference) {
       $this->isReference = FALSE;
 
       $class = get_class($node);
@@ -74,7 +80,10 @@ function leaveNode(\Twig_NodeInterface $node, \Twig_Environment $env) {
     if ($this->isReference) {
       if ($node instanceof \Twig_Node_Expression_Name) {
         $name = $node->getAttribute('name');
-        return new TwigNodeExpressionNameReference($name, $node->getLine());
+        if (!isset($this->dynamic_vars['render_variables']) || in_array($name, $this->dynamic_vars['render_variables'])) {
+          return new TwigNodeExpressionNameReference($name, $node->getLine());
+        }
+        $this->isReference = FALSE;
       }
       elseif ($node instanceof \Twig_Function_Function) {
         // Do something!
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 50395b1..8901065 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -1233,6 +1233,9 @@ function template_preprocess_node(&$variables) {
   // Clean up name so there are no underscores.
   $variables['theme_hook_suggestions'][] = 'node__' . $node->type;
   $variables['theme_hook_suggestions'][] = 'node__' . $node->nid;
+
+  // Declare our used render variables
+  $variables['theme_render_variables'][] = 'content';
 }
 
 /**
diff --git a/core/themes/engines/twig/twig.engine b/core/themes/engines/twig/twig.engine
index e7de4cf..80dce4d 100644
--- a/core/themes/engines/twig/twig.engine
+++ b/core/themes/engines/twig/twig.engine
@@ -46,7 +46,13 @@ function twig_init($template) {
  */
 function twig_render_template($template_file, $variables) {
   $variables['_references']=array();
-  return drupal_container()->get('twig')->loadTemplate($template_file)->render($variables);
+  $twig = drupal_container()->get('twig');
+
+  $dynamic_vars = array(
+    'render_variables' => $variables['theme_render_variables']
+  );
+  $twig->setDynamicVars($dynamic_vars, $template_file);
+  return $twig->loadTemplate($template_file)->render($variables);
 }
 
 /**
-- 
1.7.4.1

