new Twig_TokenParser_Extends(),
new Twig_TokenParser_Include(),
new Twig_TokenParser_Block(),
+ new Twig_TokenParser_Use(),
new Twig_TokenParser_Filter(),
new Twig_TokenParser_Macro(),
new Twig_TokenParser_Import(),
*/
class Twig_Node_Module extends Twig_Node
{
- public function __construct(Twig_NodeInterface $body, Twig_Node_Expression $parent = null, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, $filename)
+ public function __construct(Twig_NodeInterface $body, Twig_Node_Expression $parent = null, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, Twig_NodeInterface $traits, $filename)
{
- parent::__construct(array('parent' => $parent, 'body' => $body, 'blocks' => $blocks, 'macros' => $macros), array('filename' => $filename), 1);
+ parent::__construct(array('parent' => $parent, 'body' => $body, 'blocks' => $blocks, 'macros' => $macros, 'traits' => $traits), array('filename' => $filename), 1);
}
/**
{
$this->compileClassHeader($compiler);
- if (count($this->getNode('blocks'))) {
+ if (count($this->getNode('blocks')) || count($this->getNode('traits'))) {
$this->compileConstructor($compiler);
}
$this->compileGetTemplateName($compiler);
+ $this->compileIsTraitable($compiler);
+
$this->compileClassFooter($compiler);
}
->write("public function __construct(Twig_Environment \$env)\n", "{\n")
->indent()
->write("parent::__construct(\$env);\n\n")
- ->write("\$this->blocks = array(\n")
+ ;
+
+ if (count($this->getNode('traits'))) {
+ // traits
+ foreach ($this->getNode('traits') as $i => $node) {
+ $compiler
+ ->write(sprintf('$_trait_%s = $this->env->loadTemplate(', $i))
+ ->subcompile($node)
+ ->raw(");\n")
+ ->write(sprintf("if (!\$_trait_%s->isTraitable()) {\n", $i))
+ ->indent()
+ ->write("throw new Twig_Error_Runtime('Template \"'.")
+ ->subcompile($node)
+ ->raw(".'\" cannot be used as a trait.');\n")
+ ->outdent()
+ ->write("}\n\n");
+ ;
+ }
+
+ $compiler
+ ->write("\$this->blocks = array_replace(\n")
+ ->indent()
+ ;
+
+ for ($i = count($this->getNode('traits')) - 1; $i >= 0; --$i) {
+ $compiler
+ ->write(sprintf("\$_trait_%s->getBlocks(),\n", $i))
+ ;
+ }
+
+ $compiler
+ ->write("array(\n")
+ ;
+ } else {
+ $compiler
+ ->write("\$this->blocks = array(\n")
+ ;
+ }
+
+ // blocks
+ $compiler
->indent()
;
;
}
+ if (count($this->getNode('traits'))) {
+ $compiler
+ ->outdent()
+ ->write(")\n")
+ ;
+ }
+
$compiler
->outdent()
->write(");\n")
->repr($this->getAttribute('filename'))
->raw(";\n")
->outdent()
+ ->write("}\n\n")
+ ;
+ }
+
+ protected function compileIsTraitable(Twig_Compiler $compiler)
+ {
+ // A template can be used as a trait if:
+ // * it has no parent
+ // * it has no macros
+ // * it has no body
+ //
+ // Put another way, a template can be used as a trait if it
+ // only contains blocks and use statements.
+ $traitable = null === $this->getNode('parent') && 0 === count($this->getNode('macros'));
+ if ($traitable) {
+ if (!count($nodes = $this->getNode('body'))) {
+ $nodes = new Twig_Node(array($this->getNode('body')));
+ }
+
+ foreach ($nodes as $node) {
+ if ($node instanceof Twig_Node_Text && ctype_space($node->getAttribute('data'))) {
+ continue;
+ }
+
+ if ($node instanceof Twig_Node_BlockReference) {
+ continue;
+ }
+
+ $traitable = false;
+ break;
+ }
+ }
+
+ $compiler
+ ->write("public function isTraitable()\n", "{\n")
+ ->indent()
+ ->write(sprintf("return %s;\n", $traitable ? 'true' : 'false'))
+ ->outdent()
->write("}\n")
;
}
public function __construct(Twig_Node_Module $node, array $usedFilters, array $usedTags, array $usedFunctions)
{
- parent::__construct($node->getNode('body'), $node->getNode('parent'), $node->getNode('blocks'), $node->getNode('macros'), $node->getAttribute('filename'), $node->getLine(), $node->getNodeTag());
+ parent::__construct($node->getNode('body'), $node->getNode('parent'), $node->getNode('blocks'), $node->getNode('macros'), $node->getNode('traits'), $node->getAttribute('filename'), $node->getLine(), $node->getNodeTag());
$this->usedFilters = $usedFilters;
$this->usedTags = $usedTags;
protected $reservedMacroNames;
protected $importedFunctions;
protected $tmpVarCount;
+ protected $traits;
/**
* Constructor.
$this->parent = null;
$this->blocks = array();
$this->macros = array();
+ $this->traits = array();
$this->blockStack = array();
$this->importedFunctions = array(array());
throw $e;
}
- $node = new Twig_Node_Module($body, $this->parent, new Twig_Node($this->blocks), new Twig_Node($this->macros), $this->stream->getFilename());
+ $node = new Twig_Node_Module($body, $this->parent, new Twig_Node($this->blocks), new Twig_Node($this->macros), new Twig_Node($this->traits), $this->stream->getFilename());
$traverser = new Twig_NodeTraverser($this->env, $this->visitors);
$this->macros[$name] = $node;
}
+ public function addTrait($name)
+ {
+ $this->traits[] = $name;
+ }
+
public function addImportedFunction($alias, $name, Twig_Node_Expression $node)
{
$this->importedFunctions[0][$alias] = array('name' => $name, 'node' => $node);
}
/**
+ * Returns all blocks.
+ *
+ * @return array An array of blocks
+ */
+ public function getBlocks()
+ {
+ return $this->blocks;
+ }
+
+ /**
* Displays the template with the given context.
*
* @param array $context An array of parameters to pass to the template
--- /dev/null
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2011 Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+class Twig_TokenParser_Use extends Twig_TokenParser
+{
+ /**
+ * Parses a token and returns a node.
+ *
+ * @param Twig_Token $token A Twig_Token instance
+ *
+ * @return Twig_NodeInterface A Twig_NodeInterface instance
+ */
+ public function parse(Twig_Token $token)
+ {
+ $this->parser->addTrait($this->parser->getExpressionParser()->parseExpression());
+
+ $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
+
+ return null;
+ }
+
+ /**
+ * Gets the tag name associated with this token parser.
+ *
+ * @param string The tag name
+ */
+ public function getTag()
+ {
+ return 'use';
+ }
+}
$parent = new Twig_Node_Expression_Constant('layout.twig', 0);
$blocks = new Twig_Node();
$macros = new Twig_Node();
+ $traits = new Twig_Node();
$filename = 'foo.twig';
- $node = new Twig_Node_Module($body, $parent, $blocks, $macros, $filename);
+ $node = new Twig_Node_Module($body, $parent, $blocks, $macros, $traits, $filename);
$this->assertEquals($body, $node->getNode('body'));
$this->assertEquals($blocks, $node->getNode('blocks'));
$extends = null;
$blocks = new Twig_Node();
$macros = new Twig_Node();
+ $traits = new Twig_Node();
$filename = 'foo.twig';
- $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $filename);
+ $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, $filename);
$tests[] = array($node, <<<EOF
<?php
{
return "foo.twig";
}
+
+ public function isTraitable()
+ {
+ return false;
+ }
}
EOF
, $twig);
$body = new Twig_Node(array($import, new Twig_Node_Text('foo', 0)));
$extends = new Twig_Node_Expression_Constant('layout.twig', 0);
- $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $filename);
+ $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, $filename);
$tests[] = array($node, <<<EOF
<?php
{
return "foo.twig";
}
+
+ public function isTraitable()
+ {
+ return false;
+ }
}
EOF
, $twig);
0
);
- $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $filename);
+ $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, $filename);
$tests[] = array($node, <<<EOF
<?php
{
return "foo.twig";
}
+
+ public function isTraitable()
+ {
+ return false;
+ }
}
EOF
, $twig);
$parent = new Twig_Node_Expression_Constant('layout.twig', 0);
$blocks = new Twig_Node();
$macros = new Twig_Node();
+ $traits = new Twig_Node();
$filename = 'foo.twig';
- $node = new Twig_Node_Module($body, $parent, $blocks, $macros, $filename);
+ $node = new Twig_Node_Module($body, $parent, $blocks, $macros, $traits, $filename);
$node = new Twig_Node_SandboxedModule($node, array('for'), array('upper'), array('cycle'));
$this->assertEquals($body, $node->getNode('body'));
$extends = null;
$blocks = new Twig_Node();
$macros = new Twig_Node();
+ $traits = new Twig_Node();
$filename = 'foo.twig';
- $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $filename);
+ $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, $filename);
$node = new Twig_Node_SandboxedModule($node, array('for'), array('upper'), array('cycle'));
$tests[] = array($node, <<<EOF
{
return "foo.twig";
}
+
+ public function isTraitable()
+ {
+ return false;
+ }
}
EOF
, $twig);
$extends = new Twig_Node_Expression_Constant('layout.twig', 0);
$blocks = new Twig_Node();
$macros = new Twig_Node();
+ $traits = new Twig_Node();
$filename = 'foo.twig';
- $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $filename);
+ $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, $filename);
$node = new Twig_Node_SandboxedModule($node, array('for'), array('upper'), array('cycle'));
$tests[] = array($node, <<<EOF
{
return "foo.twig";
}
+
+ public function isTraitable()
+ {
+ return false;
+ }
}
EOF
, $twig);