made macro return their value instead of echoing directly
authorFabien Potencier <fabien.potencier@gmail.com>
Sun, 19 Dec 2010 10:42:50 +0000 (11:42 +0100)
committerFabien Potencier <fabien.potencier@gmail.com>
Sun, 19 Dec 2010 18:50:03 +0000 (19:50 +0100)
CHANGELOG
lib/Twig/Extension/Core.php
lib/Twig/Markup.php [new file with mode: 0644]
lib/Twig/Node/Macro.php
lib/Twig/Sandbox/SecurityPolicy.php
lib/Twig/Template.php
test/Twig/Tests/Extension/SandboxTest.php
test/Twig/Tests/Node/MacroTest.php

index 945352d..efa3759 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -6,6 +6,7 @@ Backward incompatibilities:
 
 Changes:
 
+ * made macros return their value instead of echoing directly (fixes calling a macro in sandbox mode)
  * added the "from" tag to import macros as functions
  * added support for functions (a function is just syntactic sugar for a getAttribute() call)
  * made macros callable when sandbox mode is enabled
index 74517b5..056f696 100644 (file)
@@ -287,6 +287,10 @@ function twig_strtr($pattern, $replacements)
  */
 function twig_escape_filter(Twig_Environment $env, $string, $type = 'html')
 {
+    if (is_object($string) && $string instanceof Twig_Markup) {
+        return $string;
+    }
+
     if (!is_string($string) && !(is_object($string) && method_exists($string, '__toString'))) {
         return $string;
     }
diff --git a/lib/Twig/Markup.php b/lib/Twig/Markup.php
new file mode 100644 (file)
index 0000000..878ee07
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2010 Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Marks a content as safe.
+ *
+ * @package    twig
+ * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
+ */
+class Twig_Markup extends Exception
+{
+    protected $content;
+
+    public function __construct($content)
+    {
+        $this->content = $content;
+    }
+
+    public function __toString()
+    {
+        return $this->content;
+    }
+}
index 070c3c7..7b9d31d 100644 (file)
@@ -54,7 +54,10 @@ class Twig_Node_Macro extends Twig_Node
         $compiler
             ->outdent()
             ->write(");\n\n")
+            ->write("ob_start();\n")
             ->subcompile($this->getNode('body'))
+            ->raw("\n")
+            ->write("return ob_get_clean();\n")
             ->outdent()
             ->write("}\n\n")
         ;
index 567780b..b85b42d 100644 (file)
@@ -67,7 +67,7 @@ class Twig_Sandbox_SecurityPolicy implements Twig_Sandbox_SecurityPolicyInterfac
 
     public function checkMethodAllowed($obj, $method)
     {
-        if ($obj instanceof Twig_TemplateInterface) {
+        if ($obj instanceof Twig_TemplateInterface || $obj instanceof Twig_Markup) {
             return true;
         }
 
index 6073e35..0a38087 100644 (file)
@@ -186,6 +186,12 @@ abstract class Twig_Template implements Twig_TemplateInterface
             $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
         }
 
-        return call_user_func_array(array($object, $method), $arguments);
+        $ret = call_user_func_array(array($object, $method), $arguments);
+
+        if ($object instanceof Twig_TemplateInterface) {
+            return new Twig_Markup($ret);
+        }
+
+        return $ret;
     }
 }
index e66cac6..34cc961 100644 (file)
@@ -34,64 +34,64 @@ class Twig_Tests_Extension_SandboxTest extends PHPUnit_Framework_TestCase
 
     public function testSandboxGloballySet()
     {
-        $twig = $this->getEnvironment(false, self::$templates);
+        $twig = $this->getEnvironment(false, array(), self::$templates);
         $this->assertEquals('FOO', $twig->loadTemplate('1_basic')->render(self::$params), 'Sandbox does nothing if it is disabled globally');
 
-        $twig = $this->getEnvironment(true, self::$templates);
+        $twig = $this->getEnvironment(true, array(), self::$templates);
         try {
             $twig->loadTemplate('1_basic1')->render(self::$params);
             $this->fail('Sandbox throws a SecurityError exception if an unallowed method is called');
         } catch (Twig_Sandbox_SecurityError $e) {
         }
 
-        $twig = $this->getEnvironment(true, self::$templates);
+        $twig = $this->getEnvironment(true, array(), self::$templates);
         try {
             $twig->loadTemplate('1_basic2')->render(self::$params);
             $this->fail('Sandbox throws a SecurityError exception if an unallowed filter is called');
         } catch (Twig_Sandbox_SecurityError $e) {
         }
 
-        $twig = $this->getEnvironment(true, self::$templates);
+        $twig = $this->getEnvironment(true, array(), self::$templates);
         try {
             $twig->loadTemplate('1_basic3')->render(self::$params);
             $this->fail('Sandbox throws a SecurityError exception if an unallowed tag is used in the template');
         } catch (Twig_Sandbox_SecurityError $e) {
         }
 
-        $twig = $this->getEnvironment(true, self::$templates);
+        $twig = $this->getEnvironment(true, array(), self::$templates);
         try {
             $twig->loadTemplate('1_basic4')->render(self::$params);
             $this->fail('Sandbox throws a SecurityError exception if an unallowed property is called in the template');
         } catch (Twig_Sandbox_SecurityError $e) {
         }
 
-        $twig = $this->getEnvironment(true, self::$templates);
+        $twig = $this->getEnvironment(true, array(), self::$templates);
         try {
             $twig->loadTemplate('1_basic5')->render(self::$params);
             $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
         } catch (Twig_Sandbox_SecurityError $e) {
         }
 
-        $twig = $this->getEnvironment(true, self::$templates);
+        $twig = $this->getEnvironment(true, array(), self::$templates);
         try {
             $twig->loadTemplate('1_basic6')->render(self::$params);
             $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
         } catch (Twig_Sandbox_SecurityError $e) {
         }
 
-        $twig = $this->getEnvironment(true, self::$templates, array(), array(), array('Object' => 'foo'));
+        $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('Object' => 'foo'));
         $this->assertEquals('foo', $twig->loadTemplate('1_basic1')->render(self::$params), 'Sandbox allow some methods');
 
-        $twig = $this->getEnvironment(true, self::$templates, array(), array(), array('Object' => '__toString'));
+        $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('Object' => '__toString'));
         $this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allow some methods');
 
-        $twig = $this->getEnvironment(true, self::$templates, array(), array('upper'));
+        $twig = $this->getEnvironment(true, array(), self::$templates, array(), array('upper'));
         $this->assertEquals('FABIEN', $twig->loadTemplate('1_basic2')->render(self::$params), 'Sandbox allow some filters');
 
-        $twig = $this->getEnvironment(true, self::$templates, array('if'));
+        $twig = $this->getEnvironment(true, array(), self::$templates, array('if'));
         $this->assertEquals('foo', $twig->loadTemplate('1_basic3')->render(self::$params), 'Sandbox allow some tags');
 
-        $twig = $this->getEnvironment(true, self::$templates, array(), array(), array(), array('Object' => 'bar'));
+        $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array('Object' => 'bar'));
         $this->assertEquals('bar', $twig->loadTemplate('1_basic4')->render(self::$params), 'Sandbox allow some properties');
     }
 
@@ -102,7 +102,7 @@ class Twig_Tests_Extension_SandboxTest extends PHPUnit_Framework_TestCase
             '2_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
         );
 
-        $twig = $this->getEnvironment(false, self::$templates);
+        $twig = $this->getEnvironment(false, array(), self::$templates);
         $this->assertEquals('fooFOOfoo', $twig->loadTemplate('2_basic')->render(self::$params), 'Sandbox does nothing if disabled globally and sandboxed not used for the include');
 
         self::$templates = array(
@@ -110,7 +110,7 @@ class Twig_Tests_Extension_SandboxTest extends PHPUnit_Framework_TestCase
             '3_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
         );
 
-        $twig = $this->getEnvironment(true, self::$templates);
+        $twig = $this->getEnvironment(true, array(), self::$templates);
         try {
             $twig->loadTemplate('3_basic')->render(self::$params);
             $this->fail('Sandbox throws a SecurityError exception when the included file is sandboxed');
@@ -118,10 +118,21 @@ class Twig_Tests_Extension_SandboxTest extends PHPUnit_Framework_TestCase
         }
     }
 
-    protected function getEnvironment($sandboxed, $templates, $tags = array(), $filters = array(), $methods = array(), $properties = array())
+    public function testMacrosInASandbox()
+    {
+        $twig = $this->getEnvironment(true, array('autoescape' => true), array('index' => <<<EOF
+{% macro test(text) %}<p>{{ text }}</p>{% endmacro %}
+{{ _self.test('username') }}
+EOF
+        ), array('macro'), array('escape'));
+
+        $this->assertEquals('<p>username</p>', $twig->loadTemplate('index')->render(array()));
+    }
+
+    protected function getEnvironment($sandboxed, $options, $templates, $tags = array(), $filters = array(), $methods = array(), $properties = array())
     {
         $loader = new Twig_Loader_Array($templates);
-        $twig = new Twig_Environment($loader, array('debug' => true, 'cache' => false, 'autoescape' => false));
+        $twig = new Twig_Environment($loader, array_merge(array('debug' => true, 'cache' => false, 'autoescape' => false), $options));
         $policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties);
         $twig->addExtension(new Twig_Extension_Sandbox($policy, $sandboxed));
 
index 4a1e1ca..c0dbda9 100644 (file)
@@ -50,7 +50,10 @@ public function getfoo(\$foo = null)
         "foo" => \$foo,
     );
 
+    ob_start();
     echo "foo";
+
+    return ob_get_clean();
 }
 EOF
             ),