From: Fabien Potencier Date: Sun, 19 Dec 2010 10:42:50 +0000 (+0100) Subject: made macro return their value instead of echoing directly X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=7fc5927721e0e68dd3461f1bc77e165fce9a3e5f;p=web%2Fkonrad%2Ftwig.git made macro return their value instead of echoing directly --- diff --git a/CHANGELOG b/CHANGELOG index 945352d..efa3759 100644 --- 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 diff --git a/lib/Twig/Extension/Core.php b/lib/Twig/Extension/Core.php index 74517b5..056f696 100644 --- a/lib/Twig/Extension/Core.php +++ b/lib/Twig/Extension/Core.php @@ -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 index 0000000..878ee07 --- /dev/null +++ b/lib/Twig/Markup.php @@ -0,0 +1,31 @@ + + */ +class Twig_Markup extends Exception +{ + protected $content; + + public function __construct($content) + { + $this->content = $content; + } + + public function __toString() + { + return $this->content; + } +} diff --git a/lib/Twig/Node/Macro.php b/lib/Twig/Node/Macro.php index 070c3c7..7b9d31d 100644 --- a/lib/Twig/Node/Macro.php +++ b/lib/Twig/Node/Macro.php @@ -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") ; diff --git a/lib/Twig/Sandbox/SecurityPolicy.php b/lib/Twig/Sandbox/SecurityPolicy.php index 567780b..b85b42d 100644 --- a/lib/Twig/Sandbox/SecurityPolicy.php +++ b/lib/Twig/Sandbox/SecurityPolicy.php @@ -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; } diff --git a/lib/Twig/Template.php b/lib/Twig/Template.php index 6073e35..0a38087 100644 --- a/lib/Twig/Template.php +++ b/lib/Twig/Template.php @@ -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; } } diff --git a/test/Twig/Tests/Extension/SandboxTest.php b/test/Twig/Tests/Extension/SandboxTest.php index e66cac6..34cc961 100644 --- a/test/Twig/Tests/Extension/SandboxTest.php +++ b/test/Twig/Tests/Extension/SandboxTest.php @@ -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' => <<{{ text }}

{% endmacro %} +{{ _self.test('username') }} +EOF + ), array('macro'), array('escape')); + + $this->assertEquals('

username

', $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)); diff --git a/test/Twig/Tests/Node/MacroTest.php b/test/Twig/Tests/Node/MacroTest.php index 4a1e1ca..c0dbda9 100644 --- a/test/Twig/Tests/Node/MacroTest.php +++ b/test/Twig/Tests/Node/MacroTest.php @@ -50,7 +50,10 @@ public function getfoo(\$foo = null) "foo" => \$foo, ); + ob_start(); echo "foo"; + + return ob_get_clean(); } EOF ),