From 15f3276676662d70b9fddc128e93eb2be63cc73a Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 1 Oct 2010 14:11:58 +0200 Subject: [PATCH] changed the implementation of template inheritance --- CHANGELOG | 2 + lib/Twig/Node/Block.php | 2 +- lib/Twig/Node/BlockReference.php | 2 +- lib/Twig/Node/Module.php | 79 +++++++++++++++---------- lib/Twig/Node/Parent.php | 4 +- lib/Twig/Template.php | 48 +++++++++------- test/Twig/Tests/Node/BlockReferenceTest.php | 2 +- test/Twig/Tests/Node/BlockTest.php | 2 +- test/Twig/Tests/Node/ModuleTest.php | 35 +++++++---- test/Twig/Tests/Node/ParentTest.php | 2 +- test/Twig/Tests/Node/SandboxedModuleTest.php | 15 +++-- 11 files changed, 115 insertions(+), 78 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c647692..566662b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,6 +5,8 @@ Backward incompatibilities: * the odd and even filters are now tests: {{ foo|odd }} must now be written {{ foo is odd }} + * the implementation of template inheritance has been rewritten + (blocks can now be called individually and still work with inheritance) * fixed error handling for if tag when a syntax error occurs within a subparse process * added a way to implement custom logic for resolving token parsers given a tag name * fixed js escaper to be stricter (now uses a whilelist-based js escaper) diff --git a/lib/Twig/Node/Block.php b/lib/Twig/Node/Block.php index 5547d95..d73e98d 100644 --- a/lib/Twig/Node/Block.php +++ b/lib/Twig/Node/Block.php @@ -33,7 +33,7 @@ class Twig_Node_Block extends Twig_Node { $compiler ->addDebugInfo($this) - ->write(sprintf("public function block_%s(\$context, \$parents)\n", $this['name']), "{\n") + ->write(sprintf("public function block_%s(\$context, array \$blocks = array())\n", $this['name']), "{\n") ->indent() ; diff --git a/lib/Twig/Node/BlockReference.php b/lib/Twig/Node/BlockReference.php index a09b4dc..1314f7b 100644 --- a/lib/Twig/Node/BlockReference.php +++ b/lib/Twig/Node/BlockReference.php @@ -33,7 +33,7 @@ class Twig_Node_BlockReference extends Twig_Node { $compiler ->addDebugInfo($this) - ->write(sprintf("\$this->getBlock('%s', \$context);\n", $this['name'])) + ->write(sprintf("\$this->getBlock('%s', \$context, \$blocks);\n", $this['name'])) ; } } diff --git a/lib/Twig/Node/Module.php b/lib/Twig/Node/Module.php index add0e31..630c25d 100644 --- a/lib/Twig/Node/Module.php +++ b/lib/Twig/Node/Module.php @@ -42,6 +42,8 @@ class Twig_Node_Module extends Twig_Node $this->compileConstructor($compiler); } + $this->compileGetParent($compiler); + $this->compileDisplayHeader($compiler); $this->compileDisplayBody($compiler); @@ -55,6 +57,48 @@ class Twig_Node_Module extends Twig_Node $this->compileClassFooter($compiler); } + protected function compileGetParent($compiler) + { + if (null === $this->parent) { + return; + } + + $compiler + ->write("public function getParent(array \$context)\n", "{\n") + ->indent() + ->write("if (null === \$this->parent) {\n") + ->indent(); + ; + + if ($this->parent instanceof Twig_Node_Expression_Constant) { + $compiler + ->write("\$this->parent = \$this->env->loadTemplate(") + ->subcompile($this->parent) + ->raw(");\n") + ; + } else { + $compiler + ->write("\$this->parent = ") + ->subcompile($this->parent) + ->raw(";\n") + ->write("if (!\$this->parent") + ->raw(" instanceof Twig_Template) {\n") + ->indent() + ->write("\$this->parent = \$this->env->loadTemplate(\$this->parent);\n") + ->outdent() + ->write("}\n") + ; + } + + $compiler + ->outdent() + ->write("}\n\n") + ->write("return \$this->parent;\n") + ->outdent() + ->write("}\n\n") + ; + } + protected function compileDisplayBody($compiler) { if (null !== $this->parent) { @@ -66,36 +110,7 @@ class Twig_Node_Module extends Twig_Node } $compiler - ->write("if (null === \$this->parent) {\n") - ->indent(); - ; - - if ($this->parent instanceof Twig_Node_Expression_Constant) { - $compiler - ->write("\$this->parent = clone \$this->env->loadTemplate(") - ->subcompile($this->parent) - ->raw(");\n") - ; - } else { - $compiler - ->write("\$parent = ") - ->subcompile($this->parent) - ->raw(";\n") - ->write("if (!\$parent") - ->raw(" instanceof Twig_Template) {\n") - ->indent() - ->write("\$parent = \$this->env->loadTemplate(\$parent);\n") - ->outdent() - ->write("}\n") - ->write("\$this->parent = clone \$parent;\n") - ; - } - - $compiler - ->write("\$this->parent->pushBlocks(\$this->blocks);\n") - ->outdent() - ->write("}\n") - ->write("\$this->parent->display(\$context);\n") + ->write("\$this->getParent(\$context)->display(\$context, array_merge(\$this->blocks, \$blocks));\n") ; } else { $compiler->subcompile($this->body); @@ -131,7 +146,7 @@ class Twig_Node_Module extends Twig_Node foreach ($this->blocks as $name => $node) { $compiler - ->write(sprintf("'%s' => array(array(\$this, 'block_%s')),\n", $name, $name)) + ->write(sprintf("'%s' => array(\$this, 'block_%s'),\n", $name, $name)) ; } @@ -146,7 +161,7 @@ class Twig_Node_Module extends Twig_Node protected function compileDisplayHeader($compiler) { $compiler - ->write("public function display(array \$context)\n", "{\n") + ->write("public function display(array \$context, array \$blocks = array())\n", "{\n") ->indent() ; } diff --git a/lib/Twig/Node/Parent.php b/lib/Twig/Node/Parent.php index deed784..dc779cf 100644 --- a/lib/Twig/Node/Parent.php +++ b/lib/Twig/Node/Parent.php @@ -33,7 +33,9 @@ class Twig_Node_Parent extends Twig_Node { $compiler ->addDebugInfo($this) - ->write("\$this->getParent(\$context, \$parents);\n") + ->write("\$this->getParentBlock(") + ->string($this['name']) + ->raw(", \$context, \$blocks);\n") ; } } diff --git a/lib/Twig/Template.php b/lib/Twig/Template.php index bbbee48..2cc752d 100644 --- a/lib/Twig/Template.php +++ b/lib/Twig/Template.php @@ -22,44 +22,48 @@ abstract class Twig_Template implements Twig_TemplateInterface $this->blocks = array(); } - public function __clone() - { - foreach ($this->blocks as $name => $calls) { - foreach ($calls as $i => $call) { - $this->blocks[$name][$i][0] = $this; - } - } - } - public function getEnvironment() { return $this->env; } - public function getBlock($name, array $context) + public function getParent(array $context) { - return call_user_func($this->blocks[$name][0], $context, array_slice($this->blocks[$name], 1)); + return false; } - public function hasBlock($name) + public function getParentBlock($name, array $context, array $blocks = array()) { - return isset($this->blocks[$name][0]); + if (false !== $parent = $this->getParent($context)) { + return $parent->getBlock($name, $context, $blocks); + } else { + throw new \LogicException('This template has no parent.'); + } } - protected function getParent($context, $parents) + public function getBlock($name, array $context, array $blocks = array()) { - return call_user_func($parents[0], $context, array_slice($parents, 1)); + if (isset($blocks[$name])) { + $b = $blocks; + unset($b[$name]); + return call_user_func($blocks[$name], $context, $b); + } elseif (isset($this->blocks[$name])) { + return call_user_func($this->blocks[$name], $context, $blocks); + } + + if (false !== $parent = $this->getParent($context)) { + return $parent->getBlock($name, $context, array_merge($this->blocks, $blocks)); + } } - public function pushBlocks($blocks) + public function hasBlock($name) { - foreach ($blocks as $name => $call) { - if (!isset($this->blocks[$name])) { - $this->blocks[$name] = array(); - } + return isset($this->blocks[$name]); + } - $this->blocks[$name] = array_merge($call, $this->blocks[$name]); - } + public function getBlockNames() + { + return array_keys($this->blocks); } /** diff --git a/test/Twig/Tests/Node/BlockReferenceTest.php b/test/Twig/Tests/Node/BlockReferenceTest.php index 77e3986..b5e7fcf 100644 --- a/test/Twig/Tests/Node/BlockReferenceTest.php +++ b/test/Twig/Tests/Node/BlockReferenceTest.php @@ -35,7 +35,7 @@ class Twig_Tests_Node_BlockReferenceTest extends Twig_Tests_Node_TestCase public function getTests() { return array( - array(new Twig_Node_BlockReference('foo', 0), '$this->getBlock(\'foo\', $context);'), + array(new Twig_Node_BlockReference('foo', 0), '$this->getBlock(\'foo\', $context, $blocks);'), ); } } diff --git a/test/Twig/Tests/Node/BlockTest.php b/test/Twig/Tests/Node/BlockTest.php index 6c4d86c..eb4c4ff 100644 --- a/test/Twig/Tests/Node/BlockTest.php +++ b/test/Twig/Tests/Node/BlockTest.php @@ -41,7 +41,7 @@ class Twig_Tests_Node_BlockTest extends Twig_Tests_Node_TestCase return array( array($node, <<env->loadTemplate("foo.twig", true); if (null === \$this->parent) { - \$this->parent = clone \$this->env->loadTemplate("layout.twig"); - \$this->parent->pushBlocks(\$this->blocks); + \$this->parent = \$this->env->loadTemplate("layout.twig"); } - \$this->parent->display(\$context); + + return \$this->parent; + } + + public function display(array \$context, array \$blocks = array()) + { + \$context['macro'] = \$this->env->loadTemplate("foo.twig", true); + \$this->getParent(\$context)->display(\$context, array_merge(\$this->blocks, \$blocks)); } } @@ -121,17 +126,21 @@ class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template { protected \$parent; - public function display(array \$context) + public function getParent(array \$context) { if (null === \$this->parent) { - \$parent = (true) ? ("foo") : ("foo"); - if (!\$parent instanceof Twig_Template) { - \$parent = \$this->env->loadTemplate(\$parent); + \$this->parent = (true) ? ("foo") : ("foo"); + if (!\$this->parent instanceof Twig_Template) { + \$this->parent = \$this->env->loadTemplate(\$this->parent); } - \$this->parent = clone \$parent; - \$this->parent->pushBlocks(\$this->blocks); } - \$this->parent->display(\$context); + + return \$this->parent; + } + + public function display(array \$context, array \$blocks = array()) + { + \$this->getParent(\$context)->display(\$context, array_merge(\$this->blocks, \$blocks)); } } diff --git a/test/Twig/Tests/Node/ParentTest.php b/test/Twig/Tests/Node/ParentTest.php index 8ea761c..6b40b24 100644 --- a/test/Twig/Tests/Node/ParentTest.php +++ b/test/Twig/Tests/Node/ParentTest.php @@ -35,7 +35,7 @@ class Twig_Tests_Node_ParentTest extends Twig_Tests_Node_TestCase public function getTests() { $tests = array(); - $tests[] = array(new Twig_Node_Parent('foo', 0), '$this->getParent($context, $parents);'); + $tests[] = array(new Twig_Node_Parent('foo', 0), '$this->getParentBlock("foo", $context, $blocks);'); return $tests; } diff --git a/test/Twig/Tests/Node/SandboxedModuleTest.php b/test/Twig/Tests/Node/SandboxedModuleTest.php index e823741..4551133 100644 --- a/test/Twig/Tests/Node/SandboxedModuleTest.php +++ b/test/Twig/Tests/Node/SandboxedModuleTest.php @@ -65,7 +65,7 @@ class Twig_Tests_Node_SandboxedModuleTest extends Twig_Tests_Node_TestCase /* foo.twig */ class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template { - public function display(array \$context) + public function display(array \$context, array \$blocks = array()) { \$this->checkSecurity(); echo "foo"; @@ -99,13 +99,18 @@ class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template { protected \$parent; - public function display(array \$context) + public function getParent(array \$context) { if (null === \$this->parent) { - \$this->parent = clone \$this->env->loadTemplate("layout.twig"); - \$this->parent->pushBlocks(\$this->blocks); + \$this->parent = \$this->env->loadTemplate("layout.twig"); } - \$this->parent->display(\$context); + + return \$this->parent; + } + + public function display(array \$context, array \$blocks = array()) + { + \$this->getParent(\$context)->display(\$context, array_merge(\$this->blocks, \$blocks)); } protected function checkSecurity() { -- 1.7.2.5