From 9b285a7536e1729dc4db55f8cbba087d34112db4 Mon Sep 17 00:00:00 2001
From: Fabian Franz <github@fabian-franz.de>
Date: Fri, 28 Sep 2012 00:22:25 +0200
Subject: [PATCH] Issues #1780730,1759178: Fix twig engine by utilizing self-references to

original version.
---
 core/lib/Drupal/Core/Template/TemplateData.php     |    3 +-
 .../Core/Template/TwigReferenceFunctions.php       |   36 ++++++++++++++++
 core/themes/engines/twig/twig.engine               |   43 +++++++++++++++++--
 3 files changed, 75 insertions(+), 7 deletions(-)
 create mode 100644 core/lib/Drupal/Core/Template/TwigReferenceFunctions.php

diff --git a/core/lib/Drupal/Core/Template/TemplateData.php b/core/lib/Drupal/Core/Template/TemplateData.php
index 20151a0..4e30dfd 100644
--- a/core/lib/Drupal/Core/Template/TemplateData.php
+++ b/core/lib/Drupal/Core/Template/TemplateData.php
@@ -48,8 +48,7 @@ class TemplateData extends Twig_Markup implements ArrayAccess {
 
   function render() {
     #preprocess($this->type, $this->context);
-    $template = twig()->loadTemplate(drupal_get_path('theme', $GLOBALS['theme']) . '/' . $this->type . '.twig');
-    return $template->render($this->context);
+    return twig_render_template(drupal_get_path('theme', $GLOBALS['theme']) . '/' . $this->type . '.twig', $this->context);
   }
 
   function __toString() {
diff --git a/core/lib/Drupal/Core/Template/TwigReferenceFunctions.php b/core/lib/Drupal/Core/Template/TwigReferenceFunctions.php
new file mode 100644
index 0000000..cbd2561
--- /dev/null
+++ b/core/lib/Drupal/Core/Template/TwigReferenceFunctions.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Template\TwigReferenceFunctions.
+ */
+
+namespace Drupal\Core\Template;
+
+class TwigReferenceFunctions {
+
+    /**
+     * Magic function to call functions called from twig templates by reference.
+     *
+     * This checks if the array provided by value is containing a reference to the
+     * original version. If yes it replaces the argument with its reference.
+     *
+     * @see twig_add_writable_self_reference
+    */
+    public static function __callStatic($name, $arguments) {
+      foreach ($arguments as $key => $val) {
+        if (isset($val['#writable_ref']) && is_array($val['#writable_ref'])) {
+          $arguments[$key] = &$val['#writable_ref'];
+        }
+      }
+
+      // Needed to pass by reference -- could also restrict to maximum one argument instead
+      $args = array();
+      foreach($arguments as $key => &$arg) {
+        $args[$key] = &$arg;
+      }
+
+      return call_user_func_array( $name, $args );
+    }
+}
+
diff --git a/core/themes/engines/twig/twig.engine b/core/themes/engines/twig/twig.engine
index 46a694d..a36e6af 100644
--- a/core/themes/engines/twig/twig.engine
+++ b/core/themes/engines/twig/twig.engine
@@ -6,6 +6,36 @@
  */
 use Drupal\Core\Template\TwigNodeVisitor;
 use Drupal\Core\Template\TemplateData;
+use Drupal\Core\Template\TwigReferenceFunctions;
+
+/**
+ * Add a writable self reference
+ *
+ * This is needed to workaround a limitation
+ * of twig to only provide variables by value.
+ *
+ * By saving a reference to the original array
+ * the variable can be passed by reference.
+ *
+ * @see TwigReferenceFunctions
+ *
+*/
+function twig_add_writable_self_reference(&$elements, &$original) {
+  $needs_selfreference = FALSE;
+
+  foreach ($elements as $key => $val) {
+    if (!is_array($val) || $key[0] == '#') {
+      continue;
+    }
+    if (!is_numeric($key)) {
+      $needs_selfreference = TRUE;
+    }
+    twig_add_writable_self_reference($elements[$key], $original[$key]);
+  }
+  if ($needs_selfreference) {
+    $elements['#writable_ref'] = &$original;
+  }
+}
 
 /**
  * Implements hook_theme().
@@ -35,6 +65,8 @@ function twig_init($template) {
  */
 function twig_render_template($template_file, $variables) {
   // TODO: i think twig_render_template() might be a place to cast the array into an object (by aquariumtap)
+  $original = $variables;
+  twig_add_writable_self_reference($variables, $original);
   return twig()->loadTemplate($template_file)->render($variables);
 }
 
@@ -57,11 +89,12 @@ function twig($reset = TRUE) {
     $functions = array('t', 'attributes', 'drupal_attributes', 'drupal_render', 'render', 'myrender', 'hide', 'show', 'unset', 'count');
     foreach ($functions as $function) {
       /* @TODO We decided to always use filters at DrupalCon Munich. */
-      $twig->addFunction($function, new Twig_Function_Function($function));
-      $twig->addFilter($function, new Twig_Filter_Function($function));
-      // @TODO remove URL once http://drupal.org/node/1778610 is resolved.
-      $twig->addFunction('url', new Twig_Function_Function('url'));
+      $twig->addFunction($function, new Twig_Function_Function('Drupal\Core\Template\TwigReferenceFunctions::' . $function));
+      $twig->addFilter($function, new Twig_Filter_Function('Drupal\Core\Template\TwigReferenceFunctions::' . $function));
     }
+    // @TODO remove URL once http://drupal.org/node/1778610 is resolved.
+    $twig->addFunction('url', new Twig_Function_Function('Drupal\Core\Template\TwigReferenceFunctions::' . 'url'));
+
     $twig->disableStrictVariables();
   }
 
@@ -106,7 +139,7 @@ class DrupalHideNode extends Twig_Node {
   public function compile(Twig_Compiler $compiler) {
     $compiler
       ->addDebugInfo($this)
-      ->write('hide(')
+      ->write('Drupal\Core\Template\TwigReferenceFunctions::hide(')
       ->subcompile($this->getNode('expr'))
       ->raw(");\n")
     ;
-- 
1.7.4.1

