From: Fabien Potencier Date: Tue, 1 Jun 2010 17:56:15 +0000 (+0200) Subject: made a big refactoring of Twig internals (and added a bunch of unit tests and phpdoc... X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=56318ee4a82eee38bda4bd8f409cff52889fc413;p=web%2Fkonrad%2Ftwig.git made a big refactoring of Twig internals (and added a bunch of unit tests and phpdoc, fixes #53) --- diff --git a/CHANGELOG b/CHANGELOG index f0a58e7..033521c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,15 @@ * 0.9.7-DEV Backward incompatibilities: - * The short notation of the `block` tag changed. - + * added a 'as' string to the block tag short notation ({% block title "Title" %} must now be {% block title as "Title" %}) + * removed the sandboxed attribute of the include tag (use the new sandbox tag instead) + * refactored the Node system (if you have custom nodes, you will have to update them to use the new API) + + * removed the Twig_Resource::resolveMissingFilter() method + * fixed the filter tag which did not apply filtering to included files + * added a bunch of unit tests + * added a bunch of phpdoc + * added a sandbox tag in the sandbox extension * fixed iterator_to_array() usage * changed the date filter to support any date format supported by DateTime * added strict_variable setting to throw an exception when an invalid variable is used in a template (disabled by default when debug is false) @@ -12,7 +19,6 @@ Backward incompatibilities: * added three interfaces: Twig_NodeInterface, Twig_TokenParserInterface, and Twig_FilterInterface * changed the generated code to match the new coding standards * fixed sandbox mode (__toString() method check was not enforced if called implicitly from a simple statement like {{ article }}) - * added a 'as' string to the block tag short notation ({% block title "Title" %} must now be {% block title as "Title" %}) * added an exception when a child template has a non-empty body (as it is always ignored when rendering) * 0.9.6 (2010-05-12) diff --git a/doc/02-Twig-for-Template-Designers.markdown b/doc/02-Twig-for-Template-Designers.markdown index eff8116..8d98dbf 100644 --- a/doc/02-Twig-for-Template-Designers.markdown +++ b/doc/02-Twig-for-Template-Designers.markdown @@ -641,12 +641,6 @@ rendered contents of that file into the current namespace: Included templates have access to the variables of the active context. -An included file can be evaluated in the sandbox environment by appending -`sandboxed` at the end if the `escaper` extension has been enabled: - - [twig] - {% include 'user.html' sandboxed %} - You can also restrict the variables passed to the template by explicitly pass them as an array: @@ -656,16 +650,15 @@ them as an array: {% set vars as ['foo': 'bar'] %} {% include 'foo' with vars %} -The most secure way to include a template is to use both the `sandboxed` mode, -and to pass the minimum amount of variables needed for the template to be -rendered correctly: - - [twig] - {% include 'foo' sandboxed with vars %} - >**NOTE** >The `with` keyword is supported as of Twig 0.9.5. +- + +>**TIP** +>When including a template created by an end user, you should consider +>sandboxing it. More information in the "Twig for Developers" chapter. + ### Import Twig supports putting often used code into macros. These macros can go into diff --git a/doc/03-Twig-for-Developers.markdown b/doc/03-Twig-for-Developers.markdown index 5dfbceb..bcb81bb 100644 --- a/doc/03-Twig-for-Developers.markdown +++ b/doc/03-Twig-for-Developers.markdown @@ -389,10 +389,12 @@ The policy object is the first argument of the sandbox constructor: $twig->addExtension($sandbox); By default, the sandbox mode is disabled and should be enabled when including -untrusted templates: +untrusted template code by using the `sandbox` tag: - [php] - {% include "user.html" sandboxed %} + [twig] + {% sandbox %} + {% include 'user.html' %} + {% endsandbox %} You can sandbox all templates by passing `true` as the second argument of the extension constructor: diff --git a/doc/04-Extending-Twig.markdown b/doc/04-Extending-Twig.markdown index 669c51c..bb4a287 100644 --- a/doc/04-Extending-Twig.markdown +++ b/doc/04-Extending-Twig.markdown @@ -481,22 +481,16 @@ The `Project_Set_Node` class itself is rather simple: [php] class Project_Set_Node extends Twig_Node { - protected $name; - protected $value; - public function __construct($name, Twig_Node_Expression $value, $lineno) { - parent::__construct($lineno); - - $this->name = $name; - $this->value = $value; + parent::__construct(array('value' => $value), array('name' => $name), $lineno); } public function compile($compiler) { $compiler ->addDebugInfo($this) - ->write('$context[\''.$this->name.'\'] = ') + ->write('$context[\''.$this['name'].'\'] = ') ->subcompile($this->value) ->raw(";\n") ; @@ -533,7 +527,7 @@ developer generate beautiful and readable PHP code: * `outdent()`: Outdents the generated code (see `Twig_Node_Block` for a usage example). -Creating a Node Transformer ---------------------------- +Creating a Node Visitor +----------------------- To be written... diff --git a/lib/Twig/Autoloader.php b/lib/Twig/Autoloader.php index ec0ffac..f144890 100644 --- a/lib/Twig/Autoloader.php +++ b/lib/Twig/Autoloader.php @@ -37,11 +37,9 @@ class Twig_Autoloader static public function autoload($class) { if (0 !== strpos($class, 'Twig')) { - return false; + return; } require dirname(__FILE__).'/../'.str_replace('_', '/', $class).'.php'; - - return true; } } diff --git a/lib/Twig/Compiler.php b/lib/Twig/Compiler.php index 989f190..1be505a 100644 --- a/lib/Twig/Compiler.php +++ b/lib/Twig/Compiler.php @@ -42,6 +42,16 @@ class Twig_Compiler implements Twig_CompilerInterface } /** + * Returns the environment instance related to this compiler. + * + * @return Twig_Environment The environment instance + */ + public function getEnvironment() + { + return $this->env; + } + + /** * Gets the current PHP code after compilation. * * @return string The PHP code @@ -211,19 +221,4 @@ class Twig_Compiler implements Twig_CompilerInterface return $this; } - - /** - * Returns the environment instance related to this compiler. - * - * @return Twig_Environment The environment instance - */ - public function getEnvironment() - { - return $this->env; - } - - public function getTemplateClass($name) - { - return $this->getEnvironment()->getTemplateClass($name); - } } diff --git a/lib/Twig/Environment.php b/lib/Twig/Environment.php index 4a72e60..c10c5ab 100644 --- a/lib/Twig/Environment.php +++ b/lib/Twig/Environment.php @@ -178,13 +178,14 @@ class Twig_Environment /** * Loads a template by name. * - * @param string $name The template name + * @param string $name The template name + * @param Boolean $macro Whether to return the macro object if any, or the template one * * @return Twig_TemplateInterface A template instance representing the given template name */ - public function loadTemplate($name) + public function loadTemplate($name, $macro = false) { - $cls = $this->getTemplateClass($name); + $cls = $this->getTemplateClass($name).($macro ? '_Macro' : ''); if (isset($this->loadedTemplates[$cls])) { return $this->loadedTemplates[$cls]; @@ -309,6 +310,10 @@ class Twig_Environment public function getExtension($name) { + if (!isset($this->extensions[$name])) { + throw new LogicException(sprintf('The "%s" extension is not enabled.', $name)); + } + return $this->extensions[$name]; } diff --git a/lib/Twig/ExpressionParser.php b/lib/Twig/ExpressionParser.php index c77f534..53c49fd 100644 --- a/lib/Twig/ExpressionParser.php +++ b/lib/Twig/ExpressionParser.php @@ -86,14 +86,15 @@ class Twig_ExpressionParser || $this->parser->getStream()->test(Twig_Token::NAME_TYPE, 'in') ) { - $ops[] = array($this->parser->getStream()->next()->getValue(), $this->parseAddExpression()); + $ops[] = new Twig_Node_Expression_Constant($this->parser->getStream()->next()->getValue(), $lineno); + $ops[] = $this->parseAddExpression(); } if (empty($ops)) { return $expr; } - return new Twig_Node_Expression_Compare($expr, $ops, $lineno); + return new Twig_Node_Expression_Compare($expr, new Twig_Node($ops), $lineno); } public function parseAddExpression() @@ -279,6 +280,7 @@ class Twig_ExpressionParser throw new Twig_SyntaxError(sprintf('Unexpected token "%s" of value "%s"', Twig_Token::getTypeAsString($token->getType()), $token->getValue()), $token->getLine()); } } + if (!$assignment) { $node = $this->parsePostfixExpression($node); } @@ -358,14 +360,16 @@ class Twig_ExpressionParser $end = $this->parseExpression(); - return new Twig_Node_Expression_Filter($node, array(array('range', array($end))), $lineno); + $filters = new Twig_Node(array(new Twig_Node_Expression_Constant('range', $lineno), new Twig_Node(array($end)))); + + return new Twig_Node_Expression_Filter($node, $filters, $lineno); } public function parseSubscriptExpression($node) { $token = $this->parser->getStream()->next(); $lineno = $token->getLine(); - $arguments = array(); + $arguments = new Twig_Node(); if ($token->getValue() == '.') { $token = $this->parser->getStream()->next(); if ($token->getType() == Twig_Token::NAME_TYPE || $token->getType() == Twig_Token::NUMBER_TYPE) { @@ -398,7 +402,8 @@ class Twig_ExpressionParser while (true) { $token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE); - $filters[] = array($token->getValue(), $this->parseArguments()); + $filters[] = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine()); + $filters[] = $this->parseArguments(); if (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '|')) { break; @@ -407,13 +412,13 @@ class Twig_ExpressionParser $this->parser->getStream()->next(); } - return $filters; + return new Twig_Node($filters); } public function parseArguments() { if (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '(')) { - return array(); + return new Twig_Node(); } $args = array(); @@ -426,14 +431,13 @@ class Twig_ExpressionParser } $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ')'); - return $args; + return new Twig_Node($args); } public function parseAssignmentExpression() { $lineno = $this->parser->getCurrentToken()->getLine(); $targets = array(); - $is_multitarget = false; while (true) { if (!empty($targets)) { $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ','); @@ -449,13 +453,9 @@ class Twig_ExpressionParser if (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, ',')) { break; } - $is_multitarget = true; - } - if (!$is_multitarget && count($targets) == 1) { - return array(false, $targets[0]); } - return array(true, $targets); + return new Twig_Node($targets); } public function parseMultitargetExpression() @@ -479,10 +479,7 @@ class Twig_ExpressionParser } $is_multitarget = true; } - if (!$is_multitarget && count($targets) == 1) { - return array(false, $targets[0]); - } - return array(true, $targets); + return array($is_multitarget, new Twig_Node($targets)); } } diff --git a/lib/Twig/Extension/Core.php b/lib/Twig/Extension/Core.php index 241614e..9564ee2 100644 --- a/lib/Twig/Extension/Core.php +++ b/lib/Twig/Extension/Core.php @@ -34,16 +34,6 @@ class Twig_Extension_Core extends Twig_Extension } /** - * Returns the node visitor instances to add to the existing list. - * - * @return array An array of Twig_NodeVisitorInterface instances - */ - public function getNodeVisitors() - { - return array(new Twig_NodeVisitor_Filter()); - } - - /** * Returns a list of filters to add to the existing list. * * @return array An array of filters diff --git a/lib/Twig/Extension/Sandbox.php b/lib/Twig/Extension/Sandbox.php index c45d9b8..a298749 100644 --- a/lib/Twig/Extension/Sandbox.php +++ b/lib/Twig/Extension/Sandbox.php @@ -21,6 +21,16 @@ class Twig_Extension_Sandbox extends Twig_Extension } /** + * Returns the token parser instance to add to the existing list. + * + * @return array An array of Twig_TokenParser instances + */ + public function getTokenParsers() + { + return array(new Twig_TokenParser_Sandbox()); + } + + /** * Returns the node visitor instances to add to the existing list. * * @return array An array of Twig_NodeVisitorInterface instances diff --git a/lib/Twig/Node.php b/lib/Twig/Node.php index 4762819..f769077 100644 --- a/lib/Twig/Node.php +++ b/lib/Twig/Node.php @@ -17,20 +17,57 @@ * @author Fabien Potencier * @version SVN: $Id$ */ -abstract class Twig_Node implements Twig_NodeInterface +class Twig_Node implements Twig_NodeInterface, ArrayAccess, Countable, Iterator { + protected $nodes; + protected $attributes; protected $lineno; protected $tag; - public function __construct($lineno, $tag = null) + public function __construct(array $nodes = array(), array $attributes = array(), $lineno = 0, $tag = null) { + $this->nodes = array(); + foreach ($nodes as $name => $node) { + $this->$name = $node; + } + $this->attributes = $attributes; $this->lineno = $lineno; $this->tag = $tag; } public function __toString() { - return get_class($this).'()'; + $attributes = array(); + foreach ($this->attributes as $name => $value) { + $attributes[] = sprintf('%s: %s', $name, str_replace("\n", '', var_export($value, true))); + } + + $repr = array(get_class($this).'('.implode(', ', $attributes)); + + if (count($this->nodes)) { + foreach ($this->nodes as $name => $node) { + $len = strlen($name) + 4; + $noderepr = array(); + foreach (explode("\n", (string) $node) as $line) { + $noderepr[] = str_repeat(' ', $len).$line; + } + + $repr[] = sprintf(' %s: %s', $name, ltrim(implode("\n", $noderepr))); + } + + $repr[] = ')'; + } else { + $repr[0] .= ')'; + } + + return implode("\n", $repr); + } + + public function compile($compiler) + { + foreach ($this->nodes as $node) { + $node->compile($compiler); + } } public function getLine() @@ -42,4 +79,132 @@ abstract class Twig_Node implements Twig_NodeInterface { return $this->tag; } + + /** + * Returns true if the attribute is defined. + * + * @param string The attribute name + * + * @return Boolean true if the attribute is defined, false otherwise + */ + public function offsetExists($name) + { + return $this->attributes[$name]; + } + + /** + * Gets an attribute. + * + * @param string The attribute name + * + * @return mixed The attribute value + */ + public function offsetGet($name) + { + if (!array_key_exists($name, $this->attributes)) { + throw new InvalidArgumentException(sprintf('Attribute "%s" does not exist for Node "%s".', $name, get_class($this))); + } + + return $this->attributes[$name]; + } + + /** + * Sets an attribute. + * + * @param string The attribute name + * @param mixed The attribute value + */ + public function offsetSet($name, $value) + { + $this->attributes[$name] = $value; + } + + /** + * Removes an attribute. + * + * @param string The attribute name + */ + public function offsetUnset($name) + { + unset($this->attributes[$name]); + } + + /** + * Returns true if the node with the given identifier exists. + * + * @param string The node name + * + * @return Boolean true if the node with the given name exists, false otherwise + */ + public function __isset($name) + { + return array_key_exists($name, $this->nodes); + } + + /** + * Gets a node by name. + * + * @param string The node name + * + * @return Twig_Node A Twig_Node instance + */ + public function __get($name) + { + if (!array_key_exists($name, $this->nodes)) { + throw new InvalidArgumentException(sprintf('Node "%s" does not exist for Node "%s".', $name, get_class($this))); + } + + return $this->nodes[$name]; + } + + /** + * Sets a node. + * + * @param string The node name + * @param Twig_Node A Twig_Node instance + */ + public function __set($name, $node = null) + { + $this->nodes[$name] = $node; + } + + /** + * Removes a node by name. + * + * @param string The node name + */ + public function __unset($name) + { + unset($this->nodes[$name]); + } + + public function count() + { + return count($this->nodes); + } + + public function rewind() + { + reset($this->nodes); + } + + public function current() + { + return current($this->nodes); + } + + public function key() + { + return key($this->nodes); + } + + public function next() + { + return next($this->nodes); + } + + public function valid() + { + return false !== current($this->nodes); + } } diff --git a/lib/Twig/Node/AutoEscape.php b/lib/Twig/Node/AutoEscape.php index 01b9e0e..6532a80 100644 --- a/lib/Twig/Node/AutoEscape.php +++ b/lib/Twig/Node/AutoEscape.php @@ -22,46 +22,15 @@ * @author Fabien Potencier * @version SVN: $Id$ */ -class Twig_Node_AutoEscape extends Twig_Node implements Twig_NodeListInterface +class Twig_Node_AutoEscape extends Twig_Node { - protected $value; - protected $body; - - public function __construct($value, Twig_NodeList $body, $lineno, $tag = null) - { - parent::__construct($lineno, $tag); - $this->value = $value; - $this->body = $body; - } - - public function __toString() - { - $repr = array(get_class($this).'('.($this->value ? 'on' : 'off')); - foreach (explode("\n", $this->body) as $line) { - $repr[] = ' '.$line; - } - $repr[] = ')'; - - return implode("\n", $repr); - } - - public function getNodes() - { - return $this->body->getNodes(); - } - - public function setNodes(array $nodes) + public function __construct($value, Twig_NodeInterface $body, $lineno, $tag = 'autoescape') { - $this->body = new Twig_NodeList($nodes, $this->lineno); + parent::__construct(array('body' => $body), array('value' => $value), $lineno, $tag); } public function compile($compiler) { $compiler->subcompile($this->body); } - - public function getValue() - { - return $this->value; - } } diff --git a/lib/Twig/Node/Block.php b/lib/Twig/Node/Block.php index 26f1b45..1655f86 100644 --- a/lib/Twig/Node/Block.php +++ b/lib/Twig/Node/Block.php @@ -17,53 +17,18 @@ * @author Fabien Potencier * @version SVN: $Id$ */ -class Twig_Node_Block extends Twig_Node implements Twig_NodeListInterface +class Twig_Node_Block extends Twig_Node { - protected $name; - protected $body; - protected $parent; - - public function __construct($name, Twig_NodeList $body, $lineno, $parent = null, $tag = null) - { - parent::__construct($lineno, $tag); - $this->name = $name; - $this->body = $body; - $this->parent = $parent; - } - - public function __toString() - { - $repr = array(get_class($this).' '.$this->name.'('); - foreach ($this->body->getNodes() as $node) { - foreach (explode("\n", $node->__toString()) as $line) { - $repr[] = ' '.$line; - } - } - $repr[] = ')'; - - return implode("\n", $repr); - } - - public function getNodes() - { - return $this->body->getNodes(); - } - - public function setNodes(array $nodes) + public function __construct($name, Twig_NodeInterface $body, $lineno, $tag = null) { - $this->body = new Twig_NodeList($nodes, $this->lineno); - } - - public function replace($other) - { - $this->body = $other->body; + parent::__construct(array('body' => $body), array('name' => $name), $lineno, $tag); } public function compile($compiler) { $compiler ->addDebugInfo($this) - ->write(sprintf("public function block_%s(\$context)\n", $this->name), "{\n") + ->write(sprintf("public function block_%s(\$context)\n", $this['name']), "{\n") ->indent() ; @@ -73,19 +38,4 @@ class Twig_Node_Block extends Twig_Node implements Twig_NodeListInterface ->write("}\n\n") ; } - - public function getName() - { - return $this->name; - } - - public function getParent() - { - return $this->parent; - } - - public function setParent($parent) - { - $this->parent = $parent; - } } diff --git a/lib/Twig/Node/BlockReference.php b/lib/Twig/Node/BlockReference.php index f950c93..6213404 100644 --- a/lib/Twig/Node/BlockReference.php +++ b/lib/Twig/Node/BlockReference.php @@ -19,29 +19,16 @@ */ class Twig_Node_BlockReference extends Twig_Node { - protected $name; - public function __construct($name, $lineno, $tag = null) { - parent::__construct($lineno, $tag); - $this->name = $name; - } - - public function __toString() - { - return get_class($this).'('.$this->name.')'; + parent::__construct(array(), array('name' => $name), $lineno, $tag); } public function compile($compiler) { $compiler ->addDebugInfo($this) - ->write(sprintf('$this->block_%s($context);'."\n", $this->name)) + ->write(sprintf('$this->block_%s($context);'."\n", $this['name'])) ; } - - public function getName() - { - return $this->name; - } } diff --git a/lib/Twig/Node/Debug.php b/lib/Twig/Node/Debug.php index 074fd06..8c3cbc7 100644 --- a/lib/Twig/Node/Debug.php +++ b/lib/Twig/Node/Debug.php @@ -1,19 +1,26 @@ + * @version SVN: $Id$ + */ class Twig_Node_Debug extends Twig_Node { - protected $expr; - public function __construct(Twig_Node_Expression $expr = null, $lineno, $tag = null) { - parent::__construct($lineno, $tag); - - $this->expr = $expr; - } - - public function __toString() - { - return get_class($this).'('.$this->expr.')'; + parent::__construct(array('expr' => $expr), array(), $lineno, $tag); } public function compile($compiler) diff --git a/lib/Twig/Node/Expression/Array.php b/lib/Twig/Node/Expression/Array.php index 48586ea..d6e71f4 100644 --- a/lib/Twig/Node/Expression/Array.php +++ b/lib/Twig/Node/Expression/Array.php @@ -8,45 +8,18 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -class Twig_Node_Expression_Array extends Twig_Node_Expression implements Twig_NodeListInterface +class Twig_Node_Expression_Array extends Twig_Node_Expression { - protected $elements; - - public function __construct($elements, $lineno) + public function __construct(array $elements, $lineno) { - parent::__construct($lineno); - - $this->elements = $elements; - } - - public function __toString() - { - $repr = array(get_class($this).'('); - foreach ($this->elements as $name => $node) { - foreach (explode("\n", '\''.$name.'\' => '.$node) as $line) { - $repr[] = ' '.$line; - } - } - $repr[] = ')'; - - return implode("\n", $repr); - } - - public function getNodes() - { - return $this->elements; - } - - public function setNodes(array $nodes) - { - $this->elements = $nodes; + parent::__construct($elements, array(), $lineno); } public function compile($compiler) { $compiler->raw('array('); $first = true; - foreach ($this->elements as $name => $node) { + foreach ($this->nodes as $name => $node) { if (!$first) { $compiler->raw(', '); } @@ -60,9 +33,4 @@ class Twig_Node_Expression_Array extends Twig_Node_Expression implements Twig_No } $compiler->raw(')'); } - - public function getElements() - { - return $this->elements; - } } diff --git a/lib/Twig/Node/Expression/AssignName.php b/lib/Twig/Node/Expression/AssignName.php index 0459e8b..4e33005 100644 --- a/lib/Twig/Node/Expression/AssignName.php +++ b/lib/Twig/Node/Expression/AssignName.php @@ -14,6 +14,6 @@ class Twig_Node_Expression_AssignName extends Twig_Node_Expression_Name { public function compile($compiler) { - $compiler->raw(sprintf('$context[\'%s\']', $this->name)); + $compiler->raw(sprintf('$context[\'%s\']', $this['name'])); } } diff --git a/lib/Twig/Node/Expression/Binary.php b/lib/Twig/Node/Expression/Binary.php index 9af1635..cd13f08 100644 --- a/lib/Twig/Node/Expression/Binary.php +++ b/lib/Twig/Node/Expression/Binary.php @@ -11,33 +11,9 @@ */ abstract class Twig_Node_Expression_Binary extends Twig_Node_Expression { - protected $left; - protected $right; - public function __construct(Twig_NodeInterface $left, Twig_NodeInterface $right, $lineno) { - parent::__construct($lineno); - $this->left = $left; - $this->right = $right; - } - - public function __toString() - { - $repr = array(get_class($this).'('); - - foreach (explode("\n", $this->left->__toString()) as $line) { - $repr[] = ' '.$line; - } - - $repr[] = ', '; - - foreach (explode("\n", $this->right->__toString()) as $line) { - $repr[] = ' '.$line; - } - - $repr[] = ')'; - - return implode("\n", $repr); + parent::__construct(array('left' => $left, 'right' => $right), array(), $lineno); } public function compile($compiler) diff --git a/lib/Twig/Node/Expression/Binary/FloorDiv.php b/lib/Twig/Node/Expression/Binary/FloorDiv.php index 6db622c..f689414 100644 --- a/lib/Twig/Node/Expression/Binary/FloorDiv.php +++ b/lib/Twig/Node/Expression/Binary/FloorDiv.php @@ -12,17 +12,13 @@ class Twig_Node_Expression_Binary_FloorDiv extends Twig_Node_Expression_Binary { public function compile($compiler) { - $compiler - ->raw('floor(') - ->subcompile($this->left) - ->raw(' / ') - ->subcompile($this->right) - ->raw(')') - ; + $compiler->raw('floor('); + parent::compile($compiler); + $compiler->raw(')'); } public function operator($compiler) { - return; + return $compiler->raw('/'); } } diff --git a/lib/Twig/Node/Expression/Compare.php b/lib/Twig/Node/Expression/Compare.php index 8d0774f..e1156d2 100644 --- a/lib/Twig/Node/Expression/Compare.php +++ b/lib/Twig/Node/Expression/Compare.php @@ -11,44 +11,39 @@ */ class Twig_Node_Expression_Compare extends Twig_Node_Expression { - protected $expr; - protected $ops; - - public function __construct(Twig_Node_Expression $expr, $ops, $lineno) + public function __construct(Twig_Node_Expression $expr, Twig_NodeInterface $ops, $lineno) { - parent::__construct($lineno); - $this->expr = $expr; - $this->ops = $ops; + parent::__construct(array('expr' => $expr, 'ops' => $ops), array(), $lineno); } public function compile($compiler) { - $nbOps = count($this->ops) > 1; - if ('in' === $this->ops[0][0]) { + if ('in' === $this->ops->{0}['value']) { return $this->compileIn($compiler); } $this->expr->compile($compiler); - $i = 0; - foreach ($this->ops as $op) { - if ($i) { - $compiler->raw(' && ($tmp'.$i); + + $nbOps = count($this->ops); + for ($i = 0; $i < $nbOps; $i += 2) { + if ($i > 0) { + $compiler->raw(' && ($tmp'.($i / 2)); } - list($op, $node) = $op; - $compiler->raw(' '.$op.' '); - if ($nbOps) { + $compiler->raw(' '.$this->ops->{$i}['value'].' '); + + if ($i != $nbOps - 2) { $compiler - ->raw('($tmp'.++$i.' = ') - ->subcompile($node) + ->raw('($tmp'.(($i / 2) + 1).' = ') + ->subcompile($this->ops->{($i + 1)}) ->raw(')') ; } else { - $compiler->subcompile($node); + $compiler->subcompile($this->ops->{($i + 1)}); } } - for ($j = 1; $j < $i; $j++) { + for ($j = 1; $j < $i / 2; $j++) { $compiler->raw(')'); } } @@ -59,7 +54,7 @@ class Twig_Node_Expression_Compare extends Twig_Node_Expression ->raw('twig_in_filter(') ->subcompile($this->expr) ->raw(', ') - ->subcompile($this->ops[0][1]) + ->subcompile($this->ops->{1}) ->raw(')') ; } diff --git a/lib/Twig/Node/Expression/Conditional.php b/lib/Twig/Node/Expression/Conditional.php index c04d111..4440716 100644 --- a/lib/Twig/Node/Expression/Conditional.php +++ b/lib/Twig/Node/Expression/Conditional.php @@ -11,16 +11,9 @@ */ class Twig_Node_Expression_Conditional extends Twig_Node_Expression { - protected $expr1; - protected $expr2; - protected $expr3; - public function __construct(Twig_Node_Expression $expr1, Twig_Node_Expression $expr2, Twig_Node_Expression $expr3, $lineno) { - parent::__construct($lineno); - $this->expr1 = $expr1; - $this->expr2 = $expr2; - $this->expr3 = $expr3; + parent::__construct(array('expr1' => $expr1, 'expr2' => $expr2, 'expr3' => $expr3), array(), $lineno); } public function compile($compiler) diff --git a/lib/Twig/Node/Expression/Constant.php b/lib/Twig/Node/Expression/Constant.php index c2a4d0c..5d8d051 100644 --- a/lib/Twig/Node/Expression/Constant.php +++ b/lib/Twig/Node/Expression/Constant.php @@ -11,26 +11,13 @@ */ class Twig_Node_Expression_Constant extends Twig_Node_Expression { - protected $value; - public function __construct($value, $lineno) { - parent::__construct($lineno); - $this->value = $value; - } - - public function __toString() - { - return get_class($this).'(\''.$this->value.'\')'; + parent::__construct(array(), array('value' => $value), $lineno); } public function compile($compiler) { - $compiler->repr($this->value); - } - - public function getValue() - { - return $this->value; + $compiler->repr($this['value']); } } diff --git a/lib/Twig/Node/Expression/Filter.php b/lib/Twig/Node/Expression/Filter.php index 3131714..3f5eec9 100644 --- a/lib/Twig/Node/Expression/Filter.php +++ b/lib/Twig/Node/Expression/Filter.php @@ -9,45 +9,11 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -class Twig_Node_Expression_Filter extends Twig_Node_Expression implements Twig_NodeListInterface +class Twig_Node_Expression_Filter extends Twig_Node_Expression { - protected $node; - protected $filters; - - public function __construct(Twig_NodeInterface $node, array $filters, $lineno, $tag = null) - { - parent::__construct($lineno, $tag); - - $this->node = $node; - $this->filters = $filters; - } - - public function __toString() - { - $filters = array(); - foreach ($this->filters as $filter) { - $filters[] = $filter[0].'('.implode(', ', $filter[1]).')'; - } - - $repr = array(get_class($this).'('.implode(', ', $filters)); - - foreach (explode("\n", $this->node->__toString()) as $line) { - $repr[] = ' '.$line; - } - - $repr[] = ')'; - - return implode("\n", $repr); - } - - public function getNodes() - { - return array($this->node); - } - - public function setNodes(array $nodes) + public function __construct(Twig_NodeInterface $node, Twig_NodeInterface $filters, $lineno, $tag = null) { - $this->node = $nodes[0]; + parent::__construct(array('node' => $node, 'filters' => $filters), array(), $lineno, $tag); } public function compile($compiler) @@ -55,20 +21,19 @@ class Twig_Node_Expression_Filter extends Twig_Node_Expression implements Twig_N $filterMap = $compiler->getEnvironment()->getFilters(); $postponed = array(); - for ($i = count($this->filters) - 1; $i >= 0; --$i) { - list($name, $attrs) = $this->filters[$i]; + for ($i = count($this->filters) - 1; $i >= 0; $i -= 2) { + $name = $this->filters->{$i - 1}['value']; + $attrs = $this->filters->{$i}; if (!isset($filterMap[$name])) { - $compiler - ->raw('$this->resolveMissingFilter(') - ->repr($name) - ->raw(', ') - ; + throw new Twig_SyntaxError(sprintf('The filter "%s" does not exist', $name), $this->getLine()); } else { $compiler->raw($filterMap[$name]->compile().($filterMap[$name]->needsEnvironment() ? '($this->getEnvironment(), ' : '(')); } $postponed[] = $attrs; } + $this->node->compile($compiler); + foreach (array_reverse($postponed) as $attributes) { foreach ($attributes as $node) { $compiler @@ -80,35 +45,40 @@ class Twig_Node_Expression_Filter extends Twig_Node_Expression implements Twig_N } } - public function getFilters() + public function prependFilter(Twig_Node_Expression_Constant $name, Twig_Node $end) { - return $this->filters; - } + $filters = array($name, $end); + foreach ($this->filters as $node) { + $filters[] = $node; + } - public function setFilters(array $filters) - { - $this->filters = $filters; + $this->filters = new Twig_Node($filters, array(), $this->filters->getLine()); } - public function prependFilter($filter) + public function appendFilter(Twig_Node_Expression_Constant $name, Twig_Node $end) { - $this->filters = array_merge(array($filter), $this->filters); - } + $filters = array(); + foreach ($this->filters as $node) { + $filters[] = $node; + } - public function appendFilter($filter) - { - $this->filters[] = $filter; + $filters[] = $name; + $filters[] = $end; + + $this->filters = new Twig_Node($filters, array(), $this->filters->getLine()); } - public function appendFilters(array $filters) + public function appendFilters(Twig_NodeInterface $filters) { - $this->filters = array_merge($this->filters, $filters); + for ($i = 0; $i < count($filters); $i += 2) { + $this->appendFilter($filters->{$i}, $filters->{$i + 1}); + } } public function hasFilter($name) { - foreach ($this->filters as $filter) { - if ($name == $filter[0]) { + for ($i = 0; $i < count($this->filters); $i += 2) { + if ($name == $this->filters->{$i}['value']) { return true; } } diff --git a/lib/Twig/Node/Expression/GetAttr.php b/lib/Twig/Node/Expression/GetAttr.php index b8989dd..3803849 100644 --- a/lib/Twig/Node/Expression/GetAttr.php +++ b/lib/Twig/Node/Expression/GetAttr.php @@ -9,39 +9,11 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -class Twig_Node_Expression_GetAttr extends Twig_Node_Expression implements Twig_NodeListInterface +class Twig_Node_Expression_GetAttr extends Twig_Node_Expression { - protected $node; - protected $attr; - protected $arguments; - - public function __construct(Twig_NodeInterface $node, $attr, $arguments, $lineno, $token_value) - { - parent::__construct($lineno); - $this->node = $node; - $this->attr = $attr; - $this->arguments = $arguments; - $this->token_value = $token_value; - } - - public function __toString() - { - return get_class($this).'('.$this->node.', '.$this->attr.')'; - } - - public function getNode() - { - return $this->node; - } - - public function getNodes() - { - return array($this->node); - } - - public function setNodes(array $nodes) + public function __construct(Twig_Node_Expression $node, Twig_Node_Expression $attribute, Twig_NodeInterface $arguments, $lineno, $token_value = null) { - $this->node = $nodes[0]; + parent::__construct(array('node' => $node, 'attribute' => $attribute, 'arguments' => $arguments), array('token_value' => $token_value), $lineno); } public function compile($compiler) @@ -50,7 +22,7 @@ class Twig_Node_Expression_GetAttr extends Twig_Node_Expression implements Twig_ ->raw('$this->getAttribute(') ->subcompile($this->node) ->raw(', ') - ->subcompile($this->attr) + ->subcompile($this->attribute) ->raw(', array(') ; @@ -64,7 +36,7 @@ class Twig_Node_Expression_GetAttr extends Twig_Node_Expression implements Twig_ $compiler->raw(')'); // Don't look for functions if they're using foo[bar] - if ('[' == $this->token_value) { + if ('[' == $this['token_value']) { $compiler->raw(', true'); } diff --git a/lib/Twig/Node/Expression/Name.php b/lib/Twig/Node/Expression/Name.php index 00a0160..73b6dab 100644 --- a/lib/Twig/Node/Expression/Name.php +++ b/lib/Twig/Node/Expression/Name.php @@ -11,26 +11,13 @@ */ class Twig_Node_Expression_Name extends Twig_Node_Expression { - protected $name; - public function __construct($name, $lineno) { - parent::__construct($lineno); - $this->name = $name; - } - - public function __toString() - { - return get_class($this).'(\''.$this->name.'\')'; + parent::__construct(array(), array('name' => $name), $lineno); } public function compile($compiler) { - $compiler->raw(sprintf('$this->getContext($context, \'%s\')', $this->name, $this->name)); - } - - public function getName() - { - return $this->name; + $compiler->raw(sprintf('$this->getContext($context, \'%s\')', $this['name'], $this['name'])); } } diff --git a/lib/Twig/Node/Expression/Unary.php b/lib/Twig/Node/Expression/Unary.php index 225f6c7..1042d58 100644 --- a/lib/Twig/Node/Expression/Unary.php +++ b/lib/Twig/Node/Expression/Unary.php @@ -11,25 +11,9 @@ */ abstract class Twig_Node_Expression_Unary extends Twig_Node_Expression { - protected $node; - public function __construct(Twig_NodeInterface $node, $lineno) { - parent::__construct($lineno); - $this->node = $node; - } - - public function __toString() - { - $repr = array(get_class($this).'('); - - foreach (explode("\n", $this->node->__toString()) as $line) { - $repr[] = ' '.$line; - } - - $repr[] = ')'; - - return implode("\n", $repr); + parent::__construct(array('node' => $node), array(), $lineno); } public function compile($compiler) diff --git a/lib/Twig/Node/Filter.php b/lib/Twig/Node/Filter.php deleted file mode 100644 index 20a5b5c..0000000 --- a/lib/Twig/Node/Filter.php +++ /dev/null @@ -1,68 +0,0 @@ - - * @version SVN: $Id$ - */ -class Twig_Node_Filter extends Twig_Node implements Twig_NodeListInterface -{ - protected $filters; - protected $body; - - public function __construct($filters, Twig_NodeList $body, $lineno, $tag = null) - { - parent::__construct($lineno, $tag); - $this->filters = $filters; - $this->body = $body; - } - - public function __toString() - { - $filters = array(); - foreach ($this->filters as $filter) { - $filters[] = $filter[0].'('.implode(', ', $filter[1]).')'; - } - - $repr = array(get_class($this).'('.implode(', ', $filters)); - - foreach (explode("\n", $this->body->__toString()) as $line) { - $repr[] = ' '.$line; - } - - $repr[] = ')'; - - return implode("\n", $repr); - } - - public function getNodes() - { - return $this->body->getNodes(); - } - - public function setNodes(array $nodes) - { - $this->body = new Twig_NodeList($nodes, $this->lineno); - } - - public function compile($compiler) - { - $compiler->subcompile($this->body); - } - - public function getFilters() - { - return $this->filters; - } -} diff --git a/lib/Twig/Node/For.php b/lib/Twig/Node/For.php index 761de92..ace076f 100644 --- a/lib/Twig/Node/For.php +++ b/lib/Twig/Node/For.php @@ -17,35 +17,11 @@ * @author Fabien Potencier * @version SVN: $Id$ */ -class Twig_Node_For extends Twig_Node implements Twig_NodeListInterface +class Twig_Node_For extends Twig_Node { - protected $isMultitarget; - protected $item; - protected $seq; - protected $body; - protected $else; - protected $withLoop; - - public function __construct($isMultitarget, $item, $seq, Twig_NodeList $body, Twig_Node $else = null, $withLoop = false, $lineno, $tag = null) + public function __construct(Twig_Node_Expression_AssignName $keyTarget, Twig_Node_Expression_AssignName $valueTarget, Twig_Node_Expression $seq, Twig_NodeInterface $body, Twig_NodeInterface $else = null, $withLoop = false, $lineno, $tag = null) { - parent::__construct($lineno, $tag); - $this->isMultitarget = $isMultitarget; - $this->item = $item; - $this->seq = $seq; - $this->body = $body; - $this->else = $else; - $this->withLoop = $withLoop; - $this->lineno = $lineno; - } - - public function getNodes() - { - return $this->body->getNodes(); - } - - public function setNodes(array $nodes) - { - $this->body = new Twig_NodeList($nodes, $this->lineno); + parent::__construct(array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'body' => $body, 'else' => $else), array('with_loop' => $withLoop), $lineno, $tag); } public function compile($compiler) @@ -60,19 +36,13 @@ class Twig_Node_For extends Twig_Node implements Twig_NodeListInterface $compiler->write("\$context['_iterated'] = false;\n"); } - if ($this->isMultitarget) { - $loopVars = array($this->item[0]->getName(), $this->item[1]->getName()); - } else { - $loopVars = array('_key', $this->item->getName()); - } - $compiler ->write("\$context['_seq'] = twig_iterator_to_array(") ->subcompile($this->seq) - ->raw(", ".($this->isMultitarget ? 'true' : 'false').");\n") + ->raw(", ".(null !== $this->key_target ? 'true' : 'false').");\n") ; - if ($this->withLoop) { + if ($this['with_loop']) { $compiler ->write("\$length = count(\$context['_seq']);\n") @@ -90,11 +60,11 @@ class Twig_Node_For extends Twig_Node implements Twig_NodeListInterface } $compiler - ->write("foreach (\$context['_seq'] as \$context[") - ->repr($loopVars[0]) - ->raw("] => \$context[") - ->repr($loopVars[1]) - ->raw("]) {\n") + ->write("foreach (\$context['_seq'] as ") + ->subcompile($this->key_target) + ->raw(" => ") + ->subcompile($this->value_target) + ->raw(") {\n") ->indent() ; @@ -104,7 +74,7 @@ class Twig_Node_For extends Twig_Node implements Twig_NodeListInterface $compiler->subcompile($this->body); - if ($this->withLoop) { + if ($this['with_loop']) { $compiler ->write("++\$context['loop']['index0'];\n") ->write("++\$context['loop']['index'];\n") @@ -122,8 +92,7 @@ class Twig_Node_For extends Twig_Node implements Twig_NodeListInterface if (!is_null($this->else)) { $compiler - ->write("if (!\$context['_iterated'])\n") - ->write("{\n") + ->write("if (!\$context['_iterated']) {\n") ->indent() ->subcompile($this->else) ->outdent() @@ -134,14 +103,9 @@ class Twig_Node_For extends Twig_Node implements Twig_NodeListInterface $compiler->write('$_parent = $context[\'_parent\'];'."\n"); // remove some "private" loop variables (needed for nested loops) - $compiler->write('unset($context[\'_seq\'], $context[\'_iterated\'], $context[\''.$loopVars[0].'\'], $context[\''.$loopVars[1].'\'], $context[\'_parent\'], $context[\'loop\']);'."\n"); + $compiler->write('unset($context[\'_seq\'], $context[\'_iterated\'], $context[\''.$this->key_target['name'].'\'], $context[\''.$this->value_target['name'].'\'], $context[\'_parent\'], $context[\'loop\']);'."\n"); /// keep the values set in the inner context for variables defined in the outer context $compiler->write('$context = array_merge($_parent, array_intersect_key($context, $_parent));'."\n"); } - - public function setWithLoop($boolean) - { - $this->withLoop = (Boolean) $boolean; - } } diff --git a/lib/Twig/Node/If.php b/lib/Twig/Node/If.php index 0dc5f60..34dbfa0 100644 --- a/lib/Twig/Node/If.php +++ b/lib/Twig/Node/If.php @@ -17,68 +17,18 @@ * @author Fabien Potencier * @version SVN: $Id$ */ -class Twig_Node_If extends Twig_Node implements Twig_NodeListInterface +class Twig_Node_If extends Twig_Node { - protected $tests; - protected $else; - - public function __construct($tests, Twig_NodeList $else = null, $lineno, $tag = null) - { - parent::__construct($lineno, $tag); - $this->tests = $tests; - $this->else = $else; - } - - public function __toString() - { - $repr = array(get_class($this).'('); - foreach ($this->tests as $test) { - foreach (explode("\n", $test[0].' => '.$test[1]) as $line) { - $repr[] = ' '.$line; - } - } - $repr[] = ')'; - - if ($this->else) { - foreach (explode("\n", $this->else) as $line) { - $repr[] = ' '.$line; - } - } - - return implode("\n", $repr); - } - - public function getNodes() - { - $nodes = array(); - foreach ($this->tests as $test) { - $nodes[] = $test[1]; - } - - if ($this->else) { - $nodes[] = $this->else; - } - - return $nodes; - } - - public function setNodes(array $nodes) + public function __construct(Twig_NodeInterface $tests, Twig_NodeInterface $else = null, $lineno, $tag = null) { - foreach ($this->tests as $i => $test) { - $this->tests[$i][1] = $nodes[$i]; - } - - if ($this->else) { - $nodes = $nodes[count($nodes) - 1]; - } + parent::__construct(array('tests' => $tests, 'else' => $else), array(), $lineno, $tag); } public function compile($compiler) { $compiler->addDebugInfo($this); - $idx = 0; - foreach ($this->tests as $test) { - if ($idx++) { + for ($i = 0; $i < count($this->tests); $i += 2) { + if ($i > 0) { $compiler ->outdent() ->write("} elseif (") @@ -90,13 +40,14 @@ class Twig_Node_If extends Twig_Node implements Twig_NodeListInterface } $compiler - ->subcompile($test[0]) + ->subcompile($this->tests->{$i}) ->raw(") {\n") ->indent() - ->subcompile($test[1]) + ->subcompile($this->tests->{($i + 1)}) ; } - if (!is_null($this->else)) { + + if (isset($this->else) && null !== $this->else) { $compiler ->outdent() ->write("} else {\n") diff --git a/lib/Twig/Node/Import.php b/lib/Twig/Node/Import.php index 1fbde38..984ff31 100644 --- a/lib/Twig/Node/Import.php +++ b/lib/Twig/Node/Import.php @@ -18,48 +18,21 @@ */ class Twig_Node_Import extends Twig_Node { - protected $macro; - protected $var; - - public function __construct($macro, $var, $lineno, $tag = null) + public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression_AssignName $var, $lineno, $tag = null) { - parent::__construct($lineno, $tag); - $this->macro = $macro; - $this->var = $var; - } - - public function __toString() - { - return get_class($this).'('.$this->macro.', '.$this->var.')'; + parent::__construct(array('expr' => $expr, 'var' => $var), array(), $lineno, $tag); } public function compile($compiler) { $compiler ->addDebugInfo($this) - ->write('$this->env->loadTemplate(') - ->string($this->macro) - ->raw(");\n\n") - ->write("if (!class_exists(") - ->string($compiler->getTemplateClass($this->macro).'_Macro') - ->raw(")) {\n") - ->indent() - ->write(sprintf("throw new InvalidArgumentException('There is no defined macros in template \"%s\".');\n", $this->macro)) - ->outdent() - ->write("}\n") - ->write(sprintf("\$context[")) - ->string($this->var) - ->raw(sprintf("] = new %s_Macro(\$this->env);\n", $compiler->getTemplateClass($this->macro))) + ->write('') + ->subcompile($this->var) + ->raw(' = ') + ->raw('$this->env->loadTemplate(') + ->subcompile($this->expr) + ->raw(", true);\n") ; } - - public function getMacro() - { - return $this->macro; - } - - public function getVar() - { - return $this->var; - } } diff --git a/lib/Twig/Node/Include.php b/lib/Twig/Node/Include.php index 4e613d2..b3c9f78 100644 --- a/lib/Twig/Node/Include.php +++ b/lib/Twig/Node/Include.php @@ -17,78 +17,17 @@ * @author Fabien Potencier * @version SVN: $Id$ */ -class Twig_Node_Include extends Twig_Node implements Twig_NodeListInterface +class Twig_Node_Include extends Twig_Node { - protected $expr; - protected $sandboxed; - protected $variables; - - public function __construct(Twig_Node_Expression $expr, $sandboxed, $variables, $lineno, $tag = null) - { - parent::__construct($lineno, $tag); - - $this->expr = $expr; - $this->sandboxed = $sandboxed; - $this->variables = $variables; - } - - public function __toString() + public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression $variables = null, $lineno, $tag = null) { - return get_class($this).'('.$this->expr.($this->sandboxed ? ', sandboxed' : '').($this->variables ? ', '.$this->variables : '').')'; - } - - public function getNodes() - { - if (null === $this->variables) { - return array(new Twig_Node_Text('', -1)); - } else { - return array($this->variables); - } - - return $this->variables->getNodes(); - } - - public function setNodes(array $nodes) - { - if (isset($nodes[0]) && -1 === $nodes[0]->getLine()) { - $this->variables = null; - } else { - $this->variables = $nodes[0]; - } - } - - public function getIncludedFile() - { - return $this->expr; - } - - public function isSandboxed() - { - return $this->sandboxed; - } - - public function getVariables() - { - return $this->variables; + parent::__construct(array('expr' => $expr, 'variables' => $variables), array(), $lineno, $tag); } public function compile($compiler) { - if (!$compiler->getEnvironment()->hasExtension('sandbox') && $this->sandboxed) { - throw new Twig_SyntaxError('Unable to use the sanboxed attribute on an include if the sandbox extension is not enabled.', $this->lineno); - } - - $compiler->addDebugInfo($this); - - if ($this->sandboxed) { - $compiler - ->write("\$sandbox = \$this->env->getExtension('sandbox');\n") - ->write("\$alreadySandboxed = \$sandbox->isSandboxed();\n") - ->write("\$sandbox->enableSandbox();\n") - ; - } - $compiler + ->addDebugInfo($this) ->write('$this->env->loadTemplate(') ->subcompile($this->expr) ->raw(')->display(') @@ -101,15 +40,5 @@ class Twig_Node_Include extends Twig_Node implements Twig_NodeListInterface } $compiler->raw(");\n"); - - if ($this->sandboxed) { - $compiler - ->write("if (!\$alreadySandboxed) {\n") - ->indent() - ->write("\$sandbox->disableSandbox();\n") - ->outdent() - ->write("}\n") - ; - } } } diff --git a/lib/Twig/Node/Macro.php b/lib/Twig/Node/Macro.php index 3f62e3c..03286a8 100644 --- a/lib/Twig/Node/Macro.php +++ b/lib/Twig/Node/Macro.php @@ -16,58 +16,23 @@ * @author Fabien Potencier * @version SVN: $Id$ */ -class Twig_Node_Macro extends Twig_Node implements Twig_NodeListInterface +class Twig_Node_Macro extends Twig_Node { - protected $name; - protected $body; - protected $arguments; - - public function __construct($name, Twig_NodeList $body, $arguments, $lineno, $parent = null, $tag = null) - { - parent::__construct($lineno, $tag); - $this->name = $name; - $this->body = $body; - $this->arguments = $arguments; - } - - public function __toString() - { - $repr = array(get_class($this).' '.$this->name.'('); - foreach ($this->body->getNodes() as $node) { - foreach (explode("\n", $node->__toString()) as $line) { - $repr[] = ' '.$line; - } - } - $repr[] = ')'; - - return implode("\n", $repr); - } - - public function getNodes() - { - return $this->body->getNodes(); - } - - public function setNodes(array $nodes) - { - $this->body = new Twig_NodeList($nodes, $this->lineno); - } - - public function replace($other) + public function __construct($name, Twig_NodeInterface $body, Twig_NodeInterface $arguments, $lineno, $tag = null) { - $this->body = $other->body; + parent::__construct(array('body' => $body, 'arguments' => $arguments), array('name' => $name), $lineno, $tag); } public function compile($compiler) { $arguments = array(); foreach ($this->arguments as $argument) { - $arguments[] = '$'.$argument->getName().' = null'; + $arguments[] = '$'.$argument['name'].' = null'; } $compiler ->addDebugInfo($this) - ->write(sprintf("public function get%s(%s)\n", $this->name, implode(', ', $arguments)), "{\n") + ->write(sprintf("public function get%s(%s)\n", $this['name'], implode(', ', $arguments)), "{\n") ->indent() ->write("\$context = array(\n") ->indent() @@ -76,8 +41,8 @@ class Twig_Node_Macro extends Twig_Node implements Twig_NodeListInterface foreach ($this->arguments as $argument) { $compiler ->write('') - ->string($argument->getName()) - ->raw(' => $'.$argument->getName()) + ->string($argument['name']) + ->raw(' => $'.$argument['name']) ->raw(",\n") ; } diff --git a/lib/Twig/Node/Module.php b/lib/Twig/Node/Module.php index 258f008..8070417 100644 --- a/lib/Twig/Node/Module.php +++ b/lib/Twig/Node/Module.php @@ -17,212 +17,113 @@ * @author Fabien Potencier * @version SVN: $Id$ */ -class Twig_Node_Module extends Twig_Node implements Twig_NodeListInterface +class Twig_Node_Module extends Twig_Node { - protected $body; - protected $extends; - protected $blocks; - protected $macros; - protected $filename; - protected $usedFilters; - protected $usedTags; - - public function __construct(Twig_NodeList $body, $extends, array $blocks, array $macros, $filename) + public function __construct(Twig_NodeInterface $body, $extends, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, $filename) { - parent::__construct(1); - - $this->body = $body; - $this->extends = $extends; - $this->blocks = array_values($blocks); - $this->macros = $macros; - $this->filename = $filename; - $this->usedFilters = array(); - $this->usedTags = array(); + parent::__construct(array('body' => $body, 'blocks' => $blocks, 'macros' => $macros), array('filename' => $filename, 'extends' => $extends), 1); } - public function __toString() + public function compile($compiler) { - $repr = array(get_class($this).'('); - - if ($this->extends) - { - $repr[] = ' extends: '.$this->extends; - } - - $repr[] = ' body:'; - foreach ($this->body->getNodes() as $node) { - foreach (explode("\n", $node->__toString()) as $line) { - $repr[] = ' '.$line; - } - } + $this->compileTemplate($compiler); + $this->compileMacros($compiler); + } - $repr[] = ' blocks: '; - foreach ($this->blocks as $node) { - foreach (explode("\n", $node->__toString()) as $line) { - $repr[] = ' '.$line; - } - } + protected function compileTemplate($compiler) + { + $this->compileClassHeader($compiler); - $repr[] = ' macros: '; - foreach ($this->macros as $node) { - foreach (explode("\n", $node->__toString()) as $line) { - $repr[] = ' '.$line; - } - } + $this->compileDisplayHeader($compiler); - $repr[] = ')'; + $this->compileDisplayBody($compiler); - return implode("\n", $repr); - } + $this->compileDisplayFooter($compiler); - public function getFilename() - { - return $this->filename; - } + $compiler->subcompile($this->blocks); - public function getBody() - { - return $this->body; - } + $this->compileGetName($compiler); - public function getNodes() - { - return array_merge(array($this->body), $this->blocks, $this->macros); + $this->compileClassFooter($compiler); } - public function setNodes(array $nodes) + protected function compileDisplayBody($compiler) { - $this->body = array_shift($nodes); - $this->blocks = array(); - $this->macros = array(); - foreach ($nodes as $node) { - if ($node instanceof Twig_Node_Macro) { - $this->macros[] = $node; - } else { - $this->blocks[] = $node; + if (null !== $this['extends']) { + // remove all but import nodes + foreach ($this->body as $node) { + if ($node instanceof Twig_Node_Import) { + $compiler->subcompile($node); + } } - } - } - public function setUsedFilters(array $filters) - { - $this->usedFilters = $filters; - } - - public function setUsedTags(array $tags) - { - $this->usedTags = $tags; - } - - public function compile($compiler) - { - $this->compileTemplate($compiler); - $this->compileMacros($compiler); + $compiler + ->raw("\n") + ->write("parent::display(\$context);\n") + ; + } else { + $compiler->subcompile($this->body); + } } - protected function compileTemplate($compiler) + protected function compileClassHeader($compiler) { - $sandboxed = $compiler->getEnvironment()->hasExtension('sandbox'); - $compiler->write("extends)) { + if (null !== $this['extends']) { $compiler ->write('$this->loadTemplate(') - ->repr($this->extends) + ->repr($this['extends']) ->raw(");\n\n") ; } $compiler // if the filename contains */, add a blank to avoid a PHP parse error - ->write("/* ".str_replace('*/', '* /', $this->filename)." */\n") - ->write('class '.$compiler->getTemplateClass($this->filename)) + ->write("/* ".str_replace('*/', '* /', $this['filename'])." */\n") + ->write('class '.$compiler->getEnvironment()->getTemplateClass($this['filename'])) ; - $parent = null === $this->extends ? $compiler->getEnvironment()->getBaseTemplateClass() : $compiler->getTemplateClass($this->extends); + $parent = null === $this['extends'] ? $compiler->getEnvironment()->getBaseTemplateClass() : $compiler->getEnvironment()->getTemplateClass($this['extends']); $compiler ->raw(" extends $parent\n") ->write("{\n") ->indent() + ; + } + + protected function compileDisplayHeader($compiler) + { + $compiler ->write("public function display(array \$context)\n", "{\n") ->indent() ; + } - if (null !== $this->extends) { - // remove all but import nodes - $nodes = array(); - foreach ($this->body->getNodes() as $node) { - if ($node instanceof Twig_Node_Import) { - $nodes[] = $node; - } - } - - $compiler - ->subcompile(new Twig_NodeList($nodes)) - ->write("\n") - ->write("parent::display(\$context);\n") - ->outdent() - ->write("}\n\n") - ; - } else { - if ($sandboxed) { - $compiler->write("\$this->checkSecurity();\n"); - } - - $compiler - ->subcompile($this->body) - ->outdent() - ->write("}\n\n") - ; - } - - // blocks - foreach ($this->blocks as $node) { - $compiler->subcompile($node); - } - - if ($sandboxed) { - // sandbox information - $compiler - ->write("protected function checkSecurity()\n", "{\n") - ->indent() - ->write("\$this->env->getExtension('sandbox')->checkSecurity(\n") - ->indent() - ->write(!$this->usedTags ? "array(),\n" : "array('".implode('\', \'', $this->usedTags)."'),\n") - ->write(!$this->usedFilters ? "array()\n" : "array('".implode('\', \'', $this->usedFilters)."')\n") - ->outdent() - ->write(");\n") - ->outdent() - ->write("}\n\n") - ; - } - - // debug information - if ($compiler->getEnvironment()->isDebug()) { - $compiler - ->write("public function __toString()\n", "{\n") - ->indent() - ->write('return ') - ->string($this) - ->raw(";\n") - ->outdent() - ->write("}\n\n") - ; - } - - // original template name + protected function compileGetName($compiler) + { $compiler ->write("public function getName()\n", "{\n") ->indent() ->write('return ') - ->string($this->filename) + ->string($this['filename']) ->raw(";\n") ->outdent() ->write("}\n\n") ; + } + protected function compileDisplayFooter($compiler) + { + $compiler + ->outdent() + ->write("}\n\n") + ; + } + + protected function compileClassFooter($compiler) + { $compiler ->outdent() ->write("}\n") @@ -231,21 +132,15 @@ class Twig_Node_Module extends Twig_Node implements Twig_NodeListInterface protected function compileMacros($compiler) { - if (!$this->macros) { - return; - } - $compiler ->write("\n") - ->write('class '.$compiler->getTemplateClass($this->filename).'_Macro extends Twig_Macro'."\n") + ->write('class '.$compiler->getEnvironment()->getTemplateClass($this['filename']).'_Macro extends Twig_Macro'."\n") ->write("{\n") ->indent() ; // macros - foreach ($this->macros as $node) { - $compiler->subcompile($node); - } + $compiler->subcompile($this->macros); $compiler ->outdent() diff --git a/lib/Twig/Node/Parent.php b/lib/Twig/Node/Parent.php index 076e9a2..fd2f490 100644 --- a/lib/Twig/Node/Parent.php +++ b/lib/Twig/Node/Parent.php @@ -19,24 +19,16 @@ */ class Twig_Node_Parent extends Twig_Node { - protected $blockName; - - public function __construct($blockName, $lineno, $tag = null) - { - parent::__construct($lineno, $tag); - $this->blockName = $blockName; - } - - public function __toString() + public function __construct($name, $lineno, $tag = null) { - return get_class($this).'('.$this->blockName.')'; + parent::__construct(array(), array('name' => $name), $lineno, $tag); } public function compile($compiler) { $compiler ->addDebugInfo($this) - ->write('parent::block_'.$this->blockName.'($context);'."\n") + ->write('parent::block_'.$this['name'].'($context);'."\n") ; } } diff --git a/lib/Twig/Node/Print.php b/lib/Twig/Node/Print.php index 62edd87..6b57f21 100644 --- a/lib/Twig/Node/Print.php +++ b/lib/Twig/Node/Print.php @@ -17,35 +17,11 @@ * @author Fabien Potencier * @version SVN: $Id$ */ -class Twig_Node_Print extends Twig_Node implements Twig_NodeListInterface +class Twig_Node_Print extends Twig_Node { - protected $expr; - public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null) { - parent::__construct($lineno, $tag); - $this->expr = $expr; - } - - public function __toString() - { - $repr = array(get_class($this).'('); - foreach (explode("\n", $this->expr->__toString()) as $line) { - $repr[] = ' '.$line; - } - $repr[] = ')'; - - return implode("\n", $repr); - } - - public function getNodes() - { - return array($this->expr); - } - - public function setNodes(array $nodes) - { - $this->expr = $nodes[0]; + parent::__construct(array('expr' => $expr), array(), $lineno, $tag); } public function compile($compiler) @@ -57,9 +33,4 @@ class Twig_Node_Print extends Twig_Node implements Twig_NodeListInterface ->raw(";\n") ; } - - public function getExpression() - { - return $this->expr; - } } diff --git a/lib/Twig/Node/Sandbox.php b/lib/Twig/Node/Sandbox.php new file mode 100644 index 0000000..38af795 --- /dev/null +++ b/lib/Twig/Node/Sandbox.php @@ -0,0 +1,44 @@ + + * @version SVN: $Id$ + */ +class Twig_Node_Sandbox extends Twig_Node +{ + public function __construct(Twig_NodeInterface $body, $lineno, $tag = null) + { + parent::__construct(array('body' => $body), array(), $lineno, $tag); + } + + public function compile($compiler) + { + $compiler + ->addDebugInfo($this) + ->write("\$sandbox = \$this->env->getExtension('sandbox');\n") + ->write("if (!\$alreadySandboxed = \$sandbox->isSandboxed()) {\n") + ->indent() + ->write("\$sandbox->enableSandbox();\n") + ->outdent() + ->write("}\n") + ->subcompile($this->body) + ->write("if (!\$alreadySandboxed) {\n") + ->indent() + ->write("\$sandbox->disableSandbox();\n") + ->outdent() + ->write("}\n") + ; + } +} diff --git a/lib/Twig/Node/SandboxedModule.php b/lib/Twig/Node/SandboxedModule.php new file mode 100644 index 0000000..1ff09f6 --- /dev/null +++ b/lib/Twig/Node/SandboxedModule.php @@ -0,0 +1,69 @@ + + * @version SVN: $Id$ + */ +class Twig_Node_SandboxedModule extends Twig_Node_Module +{ + protected $usedFilters; + protected $usedTags; + + public function __construct(Twig_Node_Module $node, array $usedFilters, array $usedTags) + { + parent::__construct($node->body, $node['extends'], $node->blocks, $node->macros, $node['filename'], $node->getLine(), $node->getNodeTag()); + + $this->usedFilters = $usedFilters; + $this->usedTags = $usedTags; + } + + protected function compileDisplayBody($compiler) + { + if (null === $this['extends']) { + $compiler->write("\$this->checkSecurity();\n"); + } + + parent::compileDisplayBody($compiler); + } + + protected function compileDisplayFooter($compiler) + { + parent::compileDisplayFooter($compiler); + + $compiler + ->write("protected function checkSecurity() {\n") + ->indent() + ->write("\$this->env->getExtension('sandbox')->checkSecurity(\n") + ->indent() + ->write(!$this->usedTags ? "array(),\n" : "array('".implode('\', \'', $this->usedTags)."'),\n") + ->write(!$this->usedFilters ? "array()\n" : "array('".implode('\', \'', $this->usedFilters)."')\n") + ->outdent() + ->write(");\n") + ; + + if (null !== $this['extends']) { + $compiler + ->raw("\n") + ->write("parent::checkSecurity();\n") + ; + } + + $compiler + ->outdent() + ->write("}\n\n") + ; + } +} diff --git a/lib/Twig/Node/SandboxPrint.php b/lib/Twig/Node/SandboxedPrint.php similarity index 80% rename from lib/Twig/Node/SandboxPrint.php rename to lib/Twig/Node/SandboxedPrint.php index d405c1c..bdc2a41 100644 --- a/lib/Twig/Node/SandboxPrint.php +++ b/lib/Twig/Node/SandboxedPrint.php @@ -10,7 +10,7 @@ */ /** - * Twig_Node_SandboxPrint adds a check for the __toString() method + * Twig_Node_SandboxedPrint adds a check for the __toString() method * when the variable is an object and the sandbox is activated. * * When there is a simple Print statement, like {{ article }}, @@ -21,8 +21,13 @@ * @author Fabien Potencier * @version SVN: $Id$ */ -class Twig_Node_SandboxPrint extends Twig_Node_Print +class Twig_Node_SandboxedPrint extends Twig_Node_Print { + public function __construct(Twig_Node_Print $node) + { + parent::__construct($node->expr, $node->getLine(), $node->getNodeTag()); + } + public function compile($compiler) { $compiler diff --git a/lib/Twig/Node/Set.php b/lib/Twig/Node/Set.php index 07e45cf..c621b70 100644 --- a/lib/Twig/Node/Set.php +++ b/lib/Twig/Node/Set.php @@ -16,55 +16,18 @@ * @author Fabien Potencier * @version SVN: $Id$ */ -class Twig_Node_Set extends Twig_Node implements Twig_NodeListInterface +class Twig_Node_Set extends Twig_Node { - protected $names; - protected $values; - protected $isMultitarget; - protected $capture; - - public function __construct($isMultitarget, $capture, $names, $values, $lineno, $tag = null) - { - parent::__construct($lineno, $tag); - - $this->isMultitarget = $isMultitarget; - $this->capture = $capture; - $this->names = $names; - $this->values = $values; - } - - public function __toString() + public function __construct($capture, Twig_NodeInterface $names, Twig_NodeInterface $values, $lineno, $tag = null) { - $repr = array(get_class($this).'('.($this->isMultitarget ? implode(', ', $this->names) : $this->names).','); - foreach ($this->isMultitarget ? $this->values : array($this->values) as $node) { - foreach (explode("\n", $node->__toString()) as $line) { - $repr[] = ' '.$line; - } - } - $repr[] = ')'; - - return implode("\n", $repr); - } - - public function getNodes() - { - if ($this->isMultitarget) { - return $this->values; - } else { - return array($this->values); - } - } - - public function setNodes(array $nodes) - { - $this->values = $this->isMultitarget ? $nodes : $nodes[0]; + parent::__construct(array('names' => $names, 'values' => $values), array('capture' => $capture), $lineno, $tag); } public function compile($compiler) { $compiler->addDebugInfo($this); - if ($this->isMultitarget) { + if (count($this->names) > 1) { $compiler->write('list('); foreach ($this->names as $idx => $node) { if ($idx) { @@ -75,7 +38,7 @@ class Twig_Node_Set extends Twig_Node implements Twig_NodeListInterface } $compiler->raw(')'); } else { - if ($this->capture) { + if ($this['capture']) { $compiler ->write("ob_start();\n") ->subcompile($this->values) @@ -84,15 +47,15 @@ class Twig_Node_Set extends Twig_Node implements Twig_NodeListInterface $compiler->subcompile($this->names, false); - if ($this->capture) { + if ($this['capture']) { $compiler->raw(" = ob_get_clean()"); } } - if (!$this->capture) { + if (!$this['capture']) { $compiler->raw(' = '); - if ($this->isMultitarget) { + if (count($this->names) > 1) { $compiler->write('array('); foreach ($this->values as $idx => $value) { if ($idx) { @@ -109,9 +72,4 @@ class Twig_Node_Set extends Twig_Node implements Twig_NodeListInterface $compiler->raw(";\n"); } - - public function getNames() - { - return $this->names; - } } diff --git a/lib/Twig/Node/Text.php b/lib/Twig/Node/Text.php index 7f55810..1162053 100644 --- a/lib/Twig/Node/Text.php +++ b/lib/Twig/Node/Text.php @@ -19,17 +19,9 @@ */ class Twig_Node_Text extends Twig_Node { - protected $data; - public function __construct($data, $lineno) { - parent::__construct($lineno); - $this->data = $data; - } - - public function __toString() - { - return get_class($this).'(\''.str_replace("\n", '\n', $this->data).'\')'; + parent::__construct(array(), array('data' => $data), $lineno); } public function compile($compiler) @@ -37,13 +29,8 @@ class Twig_Node_Text extends Twig_Node $compiler ->addDebugInfo($this) ->write('echo ') - ->string($this->data) + ->string($this['data']) ->raw(";\n") ; } - - public function getData() - { - return $this->data; - } } diff --git a/lib/Twig/Node/Trans.php b/lib/Twig/Node/Trans.php index c26ac9b..139c89e 100644 --- a/lib/Twig/Node/Trans.php +++ b/lib/Twig/Node/Trans.php @@ -18,20 +18,9 @@ */ class Twig_Node_Trans extends Twig_Node { - protected $count, $body, $plural; - - public function __construct($count, Twig_NodeList $body, $plural, $lineno, $tag = null) + public function __construct(Twig_Node_Expression $count = null, Twig_NodeInterface $body, Twig_NodeInterface $plural = null, $lineno, $tag = null) { - parent::__construct($lineno, $tag); - - $this->count = $count; - $this->body = $body; - $this->plural = $plural; - } - - public function __toString() - { - return get_class($this).'('.$this->body.', '.$this->count.')'; + parent::__construct(array('count' => $count, 'body' => $body, 'plural' => $plural), array(), $lineno, $tag); } public function compile($compiler) @@ -40,13 +29,13 @@ class Twig_Node_Trans extends Twig_Node list($msg, $vars) = $this->compileString($this->body); - if (false !== $this->plural) { + if (null !== $this->plural) { list($msg1, $vars1) = $this->compileString($this->plural); $vars = array_merge($vars, $vars1); } - $function = false === $this->plural ? 'gettext' : 'ngettext'; + $function = null === $this->plural ? 'gettext' : 'ngettext'; if ($vars) { $compiler @@ -54,7 +43,7 @@ class Twig_Node_Trans extends Twig_Node ->string($msg) ; - if (false !== $this->plural) { + if (null !== $this->plural) { $compiler ->raw(', ') ->string($msg1) @@ -67,7 +56,7 @@ class Twig_Node_Trans extends Twig_Node $compiler->raw('), array('); foreach ($vars as $var) { - if ('count' === $var->getName()) { + if ('count' === $var['name']) { $compiler ->string('%count%') ->raw(' => abs(') @@ -76,7 +65,7 @@ class Twig_Node_Trans extends Twig_Node ; } else { $compiler - ->string('%'.$var->getName().'%') + ->string('%'.$var['name'].'%') ->raw(' => ') ->subcompile($var) ->raw(', ') @@ -94,33 +83,18 @@ class Twig_Node_Trans extends Twig_Node } } - public function getBody() - { - return $this->body; - } - - public function getPlural() - { - return $this->plural; - } - - public function getCount() - { - return $this->count; - } - - protected function compileString(Twig_NodeList $body) + protected function compileString(Twig_NodeInterface $body) { $msg = ''; $vars = array(); - foreach ($body->getNodes() as $i => $node) { + foreach ($body as $i => $node) { if ($node instanceof Twig_Node_Text) { - $msg .= $node->getData(); - } elseif ($node instanceof Twig_Node_Print && $node->getExpression() instanceof Twig_Node_Expression_Name) { - $msg .= sprintf('%%%s%%', $node->getExpression()->getName()); - $vars[] = $node->getExpression(); + $msg .= $node['data']; + } elseif ($node instanceof Twig_Node_Print && $node->expr instanceof Twig_Node_Expression_Name) { + $msg .= sprintf('%%%s%%', $node->expr['name']); + $vars[] = $node->expr; } else { - throw new Twig_SyntaxError(sprintf('The text to be translated with "trans" can only contain references to simple variable'), $this->lineno); + throw new Twig_SyntaxError(sprintf('The text to be translated with "trans" can only contain references to simple variables'), $this->lineno); } } diff --git a/lib/Twig/NodeList.php b/lib/Twig/NodeList.php deleted file mode 100644 index 4f6dec5..0000000 --- a/lib/Twig/NodeList.php +++ /dev/null @@ -1,59 +0,0 @@ - - * @version SVN: $Id$ - */ -class Twig_NodeList extends Twig_Node implements Twig_NodeListInterface -{ - protected $nodes; - - public function __construct(array $nodes, $lineno = 0) - { - parent::__construct($lineno); - - $this->nodes = $nodes; - } - - public function __toString() - { - $repr = array(get_class($this).'('); - foreach ($this->nodes as $node) { - foreach (explode("\n", $node->__toString()) as $line) { - $repr[] = ' '.$line; - } - } - - return implode("\n", $repr); - } - - public function compile($compiler) - { - foreach ($this->nodes as $node) { - $node->compile($compiler); - } - } - - public function getNodes() - { - return $this->nodes; - } - - public function setNodes(array $nodes) - { - $this->nodes = $nodes; - } -} diff --git a/lib/Twig/NodeListInterface.php b/lib/Twig/NodeListInterface.php deleted file mode 100644 index fe3cccd..0000000 --- a/lib/Twig/NodeListInterface.php +++ /dev/null @@ -1,30 +0,0 @@ - - * @version SVN: $Id$ - */ -interface Twig_NodeListInterface -{ - /** - * Returns an array of embedded nodes - */ - public function getNodes(); - - /** - * Sets the array of embedded nodes - */ - public function setNodes(array $nodes); -} diff --git a/lib/Twig/NodeTraverser.php b/lib/Twig/NodeTraverser.php index 49aae44..6450954 100644 --- a/lib/Twig/NodeTraverser.php +++ b/lib/Twig/NodeTraverser.php @@ -8,29 +8,73 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ + +/** + * Twig_NodeTraverser is a node traverser. + * + * It visits all nodes and their children and call the given visitor for each. + * + * @package twig + * @author Fabien Potencier + * @version SVN: $Id$ + */ class Twig_NodeTraverser { protected $env; + protected $visitors; - public function __construct(Twig_Environment $env) + /** + * Constructor. + * + * @param Twig_Environment $env A Twig_Environment instance + * @param array $visitors An array of Twig_NodeVisitorInterface instances + */ + public function __construct(Twig_Environment $env, array $visitors = array()) { $this->env = $env; + $this->visitors = array(); + foreach ($visitors as $visitor) { + $this->addVisitor($visitor); + } + } + + /** + * Adds a visitor. + * + * @param Twig_NodeVisitorInterface $visitor A Twig_NodeVisitorInterface instance + */ + public function addVisitor(Twig_NodeVisitorInterface $visitor) + { + $this->visitors[] = $visitor; } - public function traverse(Twig_NodeInterface $node, Twig_NodeVisitorInterface $visitor) + /** + * Traverses a node and calls the registered visitors. + * + * @param Twig_NodeInterface $node A Twig_NodeInterface instance + */ + public function traverse(Twig_NodeInterface $node = null) { - $node = $visitor->enterNode($node, $this->env); - - if ($node instanceof Twig_NodeListInterface) { - $newNodes = array(); - foreach ($nodes = $node->getNodes() as $k => $n) { - if (null !== $n = $this->traverse($n, $visitor)) { - $newNodes[$k] = $n; - } + if (null === $node) { + return null; + } + + foreach ($this->visitors as $visitor) { + $node = $visitor->enterNode($node, $this->env); + } + + foreach ($node as $k => $n) { + if (false !== $n = $this->traverse($n)) { + $node->$k = $n; + } else { + unset($node->$k); } - $node->setNodes($newNodes); } - return $visitor->leaveNode($node, $this->env); + foreach ($this->visitors as $visitor) { + $node = $visitor->leaveNode($node, $this->env); + } + + return $node; } } diff --git a/lib/Twig/NodeVisitor/Escaper.php b/lib/Twig/NodeVisitor/Escaper.php index be8c3ed..1ceb38b 100644 --- a/lib/Twig/NodeVisitor/Escaper.php +++ b/lib/Twig/NodeVisitor/Escaper.php @@ -8,30 +8,54 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ + +/** + * Twig_NodeVisitor_Escaper implements output escaping. + * + * @package twig + * @author Fabien Potencier + * @version SVN: $Id$ + */ class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface { protected $statusStack = array(); protected $blocks = array(); + /** + * Called before child nodes are visited. + * + * @param Twig_NodeInterface $node The node to visit + * @param Twig_Environment $env The Twig environment instance + * + * @param Twig_NodeInterface The modified node + */ public function enterNode(Twig_NodeInterface $node, Twig_Environment $env) { if ($node instanceof Twig_Node_AutoEscape) { - $this->statusStack[] = $node->getValue(); + $this->statusStack[] = $node['value']; } elseif ($node instanceof Twig_Node_Print) { return $this->escapeNode($node, $env, $this->needEscaping($env)); } elseif ($node instanceof Twig_Node_Block) { - $this->statusStack[] = isset($this->blocks[$node->getName()]) ? $this->blocks[$node->getName()] : $this->needEscaping($env); + $this->statusStack[] = isset($this->blocks[$node['name']]) ? $this->blocks[$node['name']] : $this->needEscaping($env); } return $node; } + /** + * Called after child nodes are visited. + * + * @param Twig_NodeInterface $node The node to visit + * @param Twig_Environment $env The Twig environment instance + * + * @param Twig_NodeInterface The modified node + */ public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env) { if ($node instanceof Twig_Node_AutoEscape || $node instanceof Twig_Node_Block) { array_pop($this->statusStack); } elseif ($node instanceof Twig_Node_BlockReference) { - $this->blocks[$node->getName()] = $this->needEscaping($env); + $this->blocks[$node['name']] = $this->needEscaping($env); } return $node; @@ -43,19 +67,19 @@ class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface return $node; } - $expression = $node instanceof Twig_Node_Print ? $node->getExpression() : $node; + $expression = $node instanceof Twig_Node_Print ? $node->expr : $node; if ($expression instanceof Twig_Node_Expression_Filter) { // don't escape if the primary node of the filter is not a variable - $nodes = $expression->getNodes(); - if (!$nodes[0] instanceof Twig_Node_Expression_Name) { + if (!$expression->node instanceof Twig_Node_Expression_Name) { return $node; } // don't escape if there is already an "escaper" in the filter chain $filterMap = $env->getFilters(); - foreach ($expression->getFilters() as $filter) { - if (isset($filterMap[$filter[0]]) && $filterMap[$filter[0]]->isEscaper()) { + for ($i = 0; $i < count($expression->filters); $i += 2) { + $name = $expression->filters->{$i}['value']; + if (isset($filterMap[$name]) && $filterMap[$name]->isEscaper()) { return $node; } } @@ -67,24 +91,23 @@ class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface // escape if ($expression instanceof Twig_Node_Expression_Filter) { // escape all variables in filters arguments - $filters = $expression->getFilters(); - foreach ($filters as $i => $filter) { - foreach ($filter[1] as $j => $argument) { - $filters[$i][1][$j] = $this->escapeNode($argument, $env, $type); + for ($i = 0; $i < count($expression->filters); $i += 2) { + foreach ($expression->filters->{$i + 1} as $j => $n) { + $expression->filters->{$i + 1}->{$j} = $this->escapeNode($n, $env, $type); } } - $expression->setFilters($filters); - $expression->prependFilter($this->getEscaperFilter($type)); + $filter = $this->getEscaperFilter($type, $expression->getLine()); + $expression->prependFilter($filter[0], $filter[1]); return $node; } elseif ($node instanceof Twig_Node_Print) { return new Twig_Node_Print( - new Twig_Node_Expression_Filter($expression, array($this->getEscaperFilter($type)), $node->getLine()) + new Twig_Node_Expression_Filter($expression, new Twig_Node($this->getEscaperFilter($type, $node->getLine())), $node->getLine()) , $node->getLine() ); } else { - return new Twig_Node_Expression_Filter($node, array($this->getEscaperFilter($type)), $node->getLine()); + return new Twig_Node_Expression_Filter($node, new Twig_Node($this->getEscaperFilter($type, $node->getLine())), $node->getLine()); } } @@ -97,8 +120,8 @@ class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface } } - protected function getEscaperFilter($type) + protected function getEscaperFilter($type, $line) { - return array('escape', array(new Twig_Node_Expression_Constant((string) $type, -1))); + return array(new Twig_Node_Expression_Constant('escape', $line), new Twig_Node(array(new Twig_Node_Expression_Constant((string) $type, $line)))); } } diff --git a/lib/Twig/NodeVisitor/Filter.php b/lib/Twig/NodeVisitor/Filter.php deleted file mode 100644 index e37b673..0000000 --- a/lib/Twig/NodeVisitor/Filter.php +++ /dev/null @@ -1,64 +0,0 @@ -statusStack[] = $node->getFilters(); - } elseif ($node instanceof Twig_Node_Print || $node instanceof Twig_Node_Text) { - return $this->applyFilters($node); - } - - return $node; - } - - public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env) - { - if ($node instanceof Twig_Node_Filter) { - array_pop($this->statusStack); - } - - return $node; - } - - protected function applyFilters(Twig_NodeInterface $node) - { - if (false === $filters = $this->getCurrentFilters()) { - return $node; - } - - if ($node instanceof Twig_Node_Text) { - $expression = new Twig_Node_Expression_Constant($node->getData(), $node->getLine()); - } else { - $expression = $node->getExpression(); - } - - // filters - if ($expression instanceof Twig_Node_Expression_Filter) { - $expression->appendFilters($filters); - - return $node; - } else { - return new Twig_Node_Print( - new Twig_Node_Expression_Filter($expression, $filters, $node->getLine()) - , $node->getLine() - ); - } - } - - protected function getCurrentFilters() - { - return count($this->statusStack) ? $this->statusStack[count($this->statusStack) - 1] : false; - } -} diff --git a/lib/Twig/NodeVisitor/Sandbox.php b/lib/Twig/NodeVisitor/Sandbox.php index d624f25..921760f 100644 --- a/lib/Twig/NodeVisitor/Sandbox.php +++ b/lib/Twig/NodeVisitor/Sandbox.php @@ -8,12 +8,28 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ + +/** + * Twig_NodeVisitor_Sandbox implements sandboxing. + * + * @package twig + * @author Fabien Potencier + * @version SVN: $Id$ + */ class Twig_NodeVisitor_Sandbox implements Twig_NodeVisitorInterface { protected $inAModule = false; protected $tags; protected $filters; + /** + * Called before child nodes are visited. + * + * @param Twig_NodeInterface $node The node to visit + * @param Twig_Environment $env The Twig environment instance + * + * @param Twig_NodeInterface The modified node + */ public function enterNode(Twig_NodeInterface $node, Twig_Environment $env) { if ($node instanceof Twig_Node_Module) { @@ -25,31 +41,39 @@ class Twig_NodeVisitor_Sandbox implements Twig_NodeVisitorInterface } elseif ($this->inAModule) { // look for tags if ($node->getNodeTag()) { - $this->tags[$node->getNodeTag()] = true; + $this->tags[] = $node->getNodeTag(); } // look for filters if ($node instanceof Twig_Node_Expression_Filter) { - foreach ($node->getFilters() as $filter) { - $this->filters[$filter[0]] = true; + for ($i = 0; $i < count($node->filters); $i += 2) { + $this->filters[] = $node->filters->{$i}['value']; } } - // look for simple print statement ({{ article }}) - if ($node instanceof Twig_Node_Print && $node->getExpression() instanceof Twig_Node_Expression_Name) { - return new Twig_Node_SandboxPrint($node->getExpression(), $node->getLine(), $node->getNodeTag()); + // look for simple print statements ({{ article }}) + if ($node instanceof Twig_Node_Print && $node->expr instanceof Twig_Node_Expression_Name) { + return new Twig_Node_SandboxedPrint($node); } } return $node; } + /** + * Called after child nodes are visited. + * + * @param Twig_NodeInterface $node The node to visit + * @param Twig_Environment $env The Twig environment instance + * + * @param Twig_NodeInterface The modified node + */ public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env) { if ($node instanceof Twig_Node_Module) { - $node->setUsedFilters(array_keys($this->filters)); - $node->setUsedTags(array_keys($this->tags)); $this->inAModule = false; + + return new Twig_Node_SandboxedModule($node, array_unique($this->filters), array_unique($this->tags)); } return $node; diff --git a/lib/Twig/NodeVisitorInterface.php b/lib/Twig/NodeVisitorInterface.php index 898e3d1..5edb3fa 100644 --- a/lib/Twig/NodeVisitorInterface.php +++ b/lib/Twig/NodeVisitorInterface.php @@ -1,8 +1,40 @@ + * @version SVN: $Id$ + */ interface Twig_NodeVisitorInterface { + /** + * Called before child nodes are visited. + * + * @param Twig_NodeInterface $node The node to visit + * @param Twig_Environment $env The Twig environment instance + * + * @param Twig_NodeInterface The modified node + */ public function enterNode(Twig_NodeInterface $node, Twig_Environment $env); + /** + * Called after child nodes are visited. + * + * @param Twig_NodeInterface $node The node to visit + * @param Twig_Environment $env The Twig environment instance + * + * @param Twig_NodeInterface The modified node + */ public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env); } diff --git a/lib/Twig/Parser.php b/lib/Twig/Parser.php index 3d07dd9..ccc714e 100644 --- a/lib/Twig/Parser.php +++ b/lib/Twig/Parser.php @@ -77,10 +77,10 @@ class Twig_Parser implements Twig_ParserInterface if (!is_null($this->extends)) { // check that the body only contains block references and empty text nodes - foreach ($body->getNodes() as $node) + foreach ($body as $node) { if ( - ($node instanceof Twig_Node_Text && !preg_match('/^\s*$/s', $node->getData())) + ($node instanceof Twig_Node_Text && !preg_match('/^\s*$/s', $node['data'])) || (!$node instanceof Twig_Node_Text && !$node instanceof Twig_Node_BlockReference) ) { @@ -89,18 +89,15 @@ class Twig_Parser implements Twig_ParserInterface } foreach ($this->blocks as $block) { - $block->setParent($this->extends); + $block['parent'] = $this->extends; } } - $node = new Twig_Node_Module($body, $this->extends, $this->blocks, $this->macros, $this->stream->getFilename()); + $node = new Twig_Node_Module($body, $this->extends, new Twig_Node($this->blocks), new Twig_Node($this->macros), $this->stream->getFilename()); - $t = new Twig_NodeTraverser($this->env); - foreach ($this->visitors as $visitor) { - $node = $t->traverse($node, $visitor); - } + $traverser = new Twig_NodeTraverser($this->env, $this->visitors); - return $node; + return $traverser->traverse($node); } public function subparse($test, $drop_needle = false) @@ -134,7 +131,7 @@ class Twig_Parser implements Twig_ParserInterface $this->stream->next(); } - return new Twig_NodeList($rv, $lineno); + return new Twig_Node($rv, array(), $lineno); } if (!isset($this->handlers[$token->getValue()])) { @@ -155,7 +152,7 @@ class Twig_Parser implements Twig_ParserInterface } } - return new Twig_NodeList($rv, $lineno); + return new Twig_Node($rv, array(), $lineno); } public function addHandler($name, $class) diff --git a/lib/Twig/Resource.php b/lib/Twig/Resource.php index 588e11f..c30a115 100644 --- a/lib/Twig/Resource.php +++ b/lib/Twig/Resource.php @@ -24,11 +24,6 @@ abstract class Twig_Resource return $this->env; } - protected function resolveMissingFilter($name) - { - throw new Twig_RuntimeError(sprintf('The filter "%s" does not exist', $name)); - } - protected function getContext($context, $item) { if (isset($context[$item])) { diff --git a/lib/Twig/TokenParser/Block.php b/lib/Twig/TokenParser/Block.php index 5be60ea..2fa61e5 100644 --- a/lib/Twig/TokenParser/Block.php +++ b/lib/Twig/TokenParser/Block.php @@ -35,7 +35,7 @@ class Twig_TokenParser_Block extends Twig_TokenParser } else { $stream->expect(Twig_Token::NAME_TYPE, 'as'); - $body = new Twig_NodeList(array( + $body = new Twig_Node(array( new Twig_Node_Print($this->parser->getExpressionParser()->parseExpression(), $lineno), )); } diff --git a/lib/Twig/TokenParser/Filter.php b/lib/Twig/TokenParser/Filter.php index 29a5a66..31c9387 100644 --- a/lib/Twig/TokenParser/Filter.php +++ b/lib/Twig/TokenParser/Filter.php @@ -12,14 +12,23 @@ class Twig_TokenParser_Filter extends Twig_TokenParser { public function parse(Twig_Token $token) { - $lineno = $token->getLine(); $filters = $this->parser->getExpressionParser()->parseFilterExpressionRaw(); $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true); $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); - return new Twig_Node_Filter($filters, $body, $lineno, $this->getTag()); + $name = '_tmp'.rand(10000, 99999); + $ref = new Twig_Node_BlockReference($name, $token->getLine(), $this->getTag()); + + $block = new Twig_Node_Block($name, $body, $token->getLine()); + $this->parser->setBlock($name, $block); + + $set = new Twig_Node_Set(true, new Twig_Node(array(new Twig_Node_Expression_AssignName($name, $token->getLine()))), new Twig_Node(array($ref)), $token->getLine(), $this->getTag()); + $filter = new Twig_Node_Expression_Filter(new Twig_Node_Expression_Name($name, $token->getLine()), $filters, $token->getLine(), $this->getTag()); + $filter = new Twig_Node_Print($filter, $token->getLine(), $this->getTag()); + + return new Twig_Node(array($set, $filter)); } public function decideBlockEnd($token) diff --git a/lib/Twig/TokenParser/For.php b/lib/Twig/TokenParser/For.php index 0694089..f18b72f 100644 --- a/lib/Twig/TokenParser/For.php +++ b/lib/Twig/TokenParser/For.php @@ -14,7 +14,7 @@ class Twig_TokenParser_For extends Twig_TokenParser public function parse(Twig_Token $token) { $lineno = $token->getLine(); - list($isMultitarget, $item) = $this->parser->getExpressionParser()->parseAssignmentExpression(); + $targets = $this->parser->getExpressionParser()->parseAssignmentExpression(); $this->parser->getStream()->expect('in'); $seq = $this->parser->getExpressionParser()->parseExpression(); @@ -35,7 +35,15 @@ class Twig_TokenParser_For extends Twig_TokenParser } $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); - return new Twig_Node_For($isMultitarget, $item, $seq, $body, $else, $withLoop, $lineno, $this->getTag()); + if (count($targets) > 1) { + $keyTarget = $targets->{0}; + $valueTarget = $targets->{1}; + } else { + $keyTarget = new Twig_Node_Expression_AssignName('_key', $lineno); + $valueTarget = $targets->{0}; + } + + return new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $withLoop, $lineno, $this->getTag()); } public function decideForFork($token) diff --git a/lib/Twig/TokenParser/If.php b/lib/Twig/TokenParser/If.php index b669b17..14a7429 100644 --- a/lib/Twig/TokenParser/If.php +++ b/lib/Twig/TokenParser/If.php @@ -17,7 +17,7 @@ class Twig_TokenParser_If extends Twig_TokenParser $expr = $this->parser->getExpressionParser()->parseExpression(); $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); $body = $this->parser->subparse(array($this, 'decideIfFork')); - $tests = array(array($expr, $body)); + $tests = array($expr, $body); $else = null; $end = false; @@ -34,7 +34,8 @@ class Twig_TokenParser_If extends Twig_TokenParser $expr = $this->parser->getExpressionParser()->parseExpression(); $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); $body = $this->parser->subparse(array($this, 'decideIfFork')); - $tests[] = array($expr, $body); + $tests[] = $expr; + $tests[] = $body; break; case 'endif': @@ -51,7 +52,7 @@ class Twig_TokenParser_If extends Twig_TokenParser $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); - return new Twig_Node_If($tests, $else, $lineno, $this->getTag()); + return new Twig_Node_If(new Twig_Node($tests), $else, $lineno, $this->getTag()); } public function decideIfFork($token) diff --git a/lib/Twig/TokenParser/Import.php b/lib/Twig/TokenParser/Import.php index 0f464c0..1bc89ce 100644 --- a/lib/Twig/TokenParser/Import.php +++ b/lib/Twig/TokenParser/Import.php @@ -12,9 +12,9 @@ class Twig_TokenParser_Import extends Twig_TokenParser { public function parse(Twig_Token $token) { - $macro = $this->parser->getStream()->expect(Twig_Token::STRING_TYPE)->getValue(); + $macro = $this->parser->getExpressionParser()->parseExpression(); $this->parser->getStream()->expect('as'); - $var = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE)->getValue(); + $var = new Twig_Node_Expression_AssignName($this->parser->getStream()->expect(Twig_Token::NAME_TYPE)->getValue(), $token->getLine()); $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); return new Twig_Node_Import($macro, $var, $token->getLine(), $this->getTag()); diff --git a/lib/Twig/TokenParser/Include.php b/lib/Twig/TokenParser/Include.php index 963c243..bb68eb6 100644 --- a/lib/Twig/TokenParser/Include.php +++ b/lib/Twig/TokenParser/Include.php @@ -15,21 +15,16 @@ class Twig_TokenParser_Include extends Twig_TokenParser { $expr = $this->parser->getExpressionParser()->parseExpression(); - $sandboxed = false; - if ($this->parser->getStream()->test(Twig_Token::NAME_TYPE, 'sandboxed')) { - $this->parser->getStream()->next(); - $sandboxed = true; - } - $variables = null; if ($this->parser->getStream()->test(Twig_Token::NAME_TYPE, 'with')) { $this->parser->getStream()->next(); + $variables = $this->parser->getExpressionParser()->parseExpression(); } $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); - return new Twig_Node_Include($expr, $sandboxed, $variables, $token->getLine(), $this->getTag()); + return new Twig_Node_Include($expr, $variables, $token->getLine(), $this->getTag()); } public function getTag() diff --git a/lib/Twig/TokenParser/Sandbox.php b/lib/Twig/TokenParser/Sandbox.php new file mode 100644 index 0000000..dbca198 --- /dev/null +++ b/lib/Twig/TokenParser/Sandbox.php @@ -0,0 +1,31 @@ +parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true); + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + + return new Twig_Node_Sandbox($body, $token->getLine(), $this->getTag()); + } + + public function decideBlockEnd($token) + { + return $token->test('endsandbox'); + } + + public function getTag() + { + return 'sandbox'; + } +} diff --git a/lib/Twig/TokenParser/Set.php b/lib/Twig/TokenParser/Set.php index f5f7dab..72fb028 100644 --- a/lib/Twig/TokenParser/Set.php +++ b/lib/Twig/TokenParser/Set.php @@ -14,7 +14,7 @@ class Twig_TokenParser_Set extends Twig_TokenParser { $lineno = $token->getLine(); $stream = $this->parser->getStream(); - list($isMultitarget, $names) = $this->parser->getExpressionParser()->parseAssignmentExpression(); + $names = $this->parser->getExpressionParser()->parseAssignmentExpression(); $capture = false; if ($stream->test(Twig_Token::NAME_TYPE, 'as')) { @@ -25,7 +25,7 @@ class Twig_TokenParser_Set extends Twig_TokenParser } else { $capture = true; - if ($isMultitarget) { + if (count($names) > 1) { throw new Twig_SyntaxError("When using set with a block, you cannot have a multi-target.", $lineno); } @@ -39,7 +39,7 @@ class Twig_TokenParser_Set extends Twig_TokenParser throw new Twig_SyntaxError("When using set, you must have the same number of variables and assignements.", $lineno); } - return new Twig_Node_Set($isMultitarget, $capture, $names, $values, $lineno, $this->getTag()); + return new Twig_Node_Set($capture, $names, $values, $lineno, $this->getTag()); } public function decideBlockEnd($token) diff --git a/lib/Twig/TokenParser/Trans.php b/lib/Twig/TokenParser/Trans.php index 615972a..7aa49f4 100644 --- a/lib/Twig/TokenParser/Trans.php +++ b/lib/Twig/TokenParser/Trans.php @@ -14,19 +14,19 @@ class Twig_TokenParser_Trans extends Twig_TokenParser { $lineno = $token->getLine(); $stream = $this->parser->getStream(); - $count = false; + $count = null; if (!$stream->test(Twig_Token::BLOCK_END_TYPE)) { $count = new Twig_Node_Expression_Name($stream->expect(Twig_Token::NAME_TYPE)->getValue(), $lineno); } $stream->expect(Twig_Token::BLOCK_END_TYPE); $body = $this->parser->subparse(array($this, 'decideForFork')); - $plural = false; + $plural = null; if ('plural' === $stream->next()->getValue()) { $stream->expect(Twig_Token::BLOCK_END_TYPE); $plural = $this->parser->subparse(array($this, 'decideForEnd'), true); - if (false === $count) { + if (null === $count) { throw new Twig_SyntaxError('When a plural is used, you must pass the count as an argument to the "trans" tag', $lineno); } } diff --git a/phpunit.xml b/phpunit.xml index 80ccf2b..83dd783 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -6,7 +6,7 @@ convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" - processIsolation="true" + processIsolation="false" stopOnFailure="false" syntaxCheck="false" bootstrap="test/Twig/Tests/bootstrap.php" diff --git a/test/Twig/Tests/AutoloaderTest.php b/test/Twig/Tests/AutoloaderTest.php index e572284..c8b7999 100644 --- a/test/Twig/Tests/AutoloaderTest.php +++ b/test/Twig/Tests/AutoloaderTest.php @@ -16,7 +16,6 @@ class Twig_Tests_AutoloaderTest extends PHPUnit_Framework_TestCase $this->assertFalse(class_exists('FooBarFoo'), '->autoload() does not try to load classes that does not begin with Twig'); $autoloader = new Twig_Autoloader(); - $this->assertTrue($autoloader->autoload('Twig_Parser'), '->autoload() returns true if it is able to load a class'); - $this->assertFalse($autoloader->autoload('Foo'), '->autoload() returns false if it is not able to load a class'); + $this->assertNull($autoloader->autoload('Foo'), '->autoload() returns false if it is not able to load a class'); } } diff --git a/test/Twig/Tests/Extension/SandboxTest.php b/test/Twig/Tests/Extension/SandboxTest.php index eced784..a939ffd 100644 --- a/test/Twig/Tests/Extension/SandboxTest.php +++ b/test/Twig/Tests/Extension/SandboxTest.php @@ -97,7 +97,7 @@ class Twig_Tests_Extension_SandboxTest extends PHPUnit_Framework_TestCase $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( - '3_basic' => '{{ obj.foo }}{% include "3_included" sandboxed %}{{ obj.foo }}', + '3_basic' => '{{ obj.foo }}{% sandbox %}{% include "3_included" %}{% endsandbox %}{{ obj.foo }}', '3_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}', ); diff --git a/test/Twig/Tests/Node/AutoEscapeTest.php b/test/Twig/Tests/Node/AutoEscapeTest.php new file mode 100644 index 0000000..5438390 --- /dev/null +++ b/test/Twig/Tests/Node/AutoEscapeTest.php @@ -0,0 +1,46 @@ +assertEquals($body, $node->body); + $this->assertEquals(true, $node['value']); + } + + /** + * @covers Twig_Node_AutoEscape::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $body = new Twig_Node(array(new Twig_Node_Text('foo', 0))); + $node = new Twig_Node_AutoEscape(true, $body, 0); + + return array( + array($node, 'echo "foo";'), + ); + } +} diff --git a/test/Twig/Tests/Node/BlockReferenceTest.php b/test/Twig/Tests/Node/BlockReferenceTest.php new file mode 100644 index 0000000..cc5e74b --- /dev/null +++ b/test/Twig/Tests/Node/BlockReferenceTest.php @@ -0,0 +1,41 @@ +assertEquals('foo', $node['name']); + } + + /** + * @covers Twig_Node_BlockReference::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + return array( + array(new Twig_Node_BlockReference('foo', 0), '$this->block_foo($context);'), + ); + } +} diff --git a/test/Twig/Tests/Node/BlockTest.php b/test/Twig/Tests/Node/BlockTest.php new file mode 100644 index 0000000..a6470ef --- /dev/null +++ b/test/Twig/Tests/Node/BlockTest.php @@ -0,0 +1,52 @@ +assertEquals($body, $node->body); + $this->assertEquals('foo', $node['name']); + } + + /** + * @covers Twig_Node_Block::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $body = new Twig_Node_Text('foo', 0); + $node = new Twig_Node_Block('foo', $body, 0); + + return array( + array($node, <<assertEquals($expr, $node->expr); + + $node = new Twig_Node_Debug(null, 0); + $this->assertEquals(null, $node->expr); + } + + /** + * @covers Twig_Node_Debug::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $tests = array(); + + $tests[] = array(new Twig_Node_Debug(null, 0), <<env->isDebug()) { + var_export(\$context); +} +EOF + ); + + $expr = new Twig_Node_Expression_Name('foo', 0); + $node = new Twig_Node_Debug($expr, 0); + + $tests[] = array($node, <<env->isDebug()) { + var_export(\$this->getContext(\$context, 'foo')); +} +EOF + ); + + return $tests; + } +} diff --git a/test/Twig/Tests/Node/Expression/ArrayTest.php b/test/Twig/Tests/Node/Expression/ArrayTest.php new file mode 100644 index 0000000..6729225 --- /dev/null +++ b/test/Twig/Tests/Node/Expression/ArrayTest.php @@ -0,0 +1,48 @@ + $foo = new Twig_Node_Expression_Constant('bar', 0)); + $node = new Twig_Node_Expression_Array($elements, 0); + + $this->assertEquals($foo, $node->foo); + } + + /** + * @covers Twig_Node_Expression_Array::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $elements = array( + 'foo' => new Twig_Node_Expression_Constant('bar', 0), + 'bar' => new Twig_Node_Expression_Constant('foo', 0), + ); + $node = new Twig_Node_Expression_Array($elements, 0); + + return array( + array($node, 'array("foo" => "bar", "bar" => "foo")'), + ); + } +} diff --git a/test/Twig/Tests/Node/Expression/AssignNameTest.php b/test/Twig/Tests/Node/Expression/AssignNameTest.php new file mode 100644 index 0000000..5cc9988 --- /dev/null +++ b/test/Twig/Tests/Node/Expression/AssignNameTest.php @@ -0,0 +1,43 @@ +assertEquals('foo', $node['name']); + } + + /** + * @covers Twig_Node_Expression_AssignName::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $node = new Twig_Node_Expression_AssignName('foo', 0); + + return array( + array($node, '$context[\'foo\']'), + ); + } +} diff --git a/test/Twig/Tests/Node/Expression/Binary/AddTest.php b/test/Twig/Tests/Node/Expression/Binary/AddTest.php new file mode 100644 index 0000000..ed49187 --- /dev/null +++ b/test/Twig/Tests/Node/Expression/Binary/AddTest.php @@ -0,0 +1,49 @@ +assertEquals($left, $node->left); + $this->assertEquals($right, $node->right); + } + + /** + * @covers Twig_Node_Expression_Binary_Add::compile + * @covers Twig_Node_Expression_Binary_Add::operator + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $left = new Twig_Node_Expression_Constant(1, 0); + $right = new Twig_Node_Expression_Constant(2, 0); + $node = new Twig_Node_Expression_Binary_Add($left, $right, 0); + + return array( + array($node, '(1) + (2)'), + ); + } +} diff --git a/test/Twig/Tests/Node/Expression/Binary/AndTest.php b/test/Twig/Tests/Node/Expression/Binary/AndTest.php new file mode 100644 index 0000000..0db4cb2 --- /dev/null +++ b/test/Twig/Tests/Node/Expression/Binary/AndTest.php @@ -0,0 +1,49 @@ +assertEquals($left, $node->left); + $this->assertEquals($right, $node->right); + } + + /** + * @covers Twig_Node_Expression_Binary_And::compile + * @covers Twig_Node_Expression_Binary_And::operator + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $left = new Twig_Node_Expression_Constant(1, 0); + $right = new Twig_Node_Expression_Constant(2, 0); + $node = new Twig_Node_Expression_Binary_And($left, $right, 0); + + return array( + array($node, '(1) && (2)'), + ); + } +} diff --git a/test/Twig/Tests/Node/Expression/Binary/ConcatTest.php b/test/Twig/Tests/Node/Expression/Binary/ConcatTest.php new file mode 100644 index 0000000..a24a86a --- /dev/null +++ b/test/Twig/Tests/Node/Expression/Binary/ConcatTest.php @@ -0,0 +1,49 @@ +assertEquals($left, $node->left); + $this->assertEquals($right, $node->right); + } + + /** + * @covers Twig_Node_Expression_Binary_Concat::compile + * @covers Twig_Node_Expression_Binary_Concat::operator + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $left = new Twig_Node_Expression_Constant(1, 0); + $right = new Twig_Node_Expression_Constant(2, 0); + $node = new Twig_Node_Expression_Binary_Concat($left, $right, 0); + + return array( + array($node, '(1) . (2)'), + ); + } +} diff --git a/test/Twig/Tests/Node/Expression/Binary/DivTest.php b/test/Twig/Tests/Node/Expression/Binary/DivTest.php new file mode 100644 index 0000000..3bea350 --- /dev/null +++ b/test/Twig/Tests/Node/Expression/Binary/DivTest.php @@ -0,0 +1,49 @@ +assertEquals($left, $node->left); + $this->assertEquals($right, $node->right); + } + + /** + * @covers Twig_Node_Expression_Binary_Div::compile + * @covers Twig_Node_Expression_Binary_Div::operator + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $left = new Twig_Node_Expression_Constant(1, 0); + $right = new Twig_Node_Expression_Constant(2, 0); + $node = new Twig_Node_Expression_Binary_Div($left, $right, 0); + + return array( + array($node, '(1) / (2)'), + ); + } +} diff --git a/test/Twig/Tests/Node/Expression/Binary/FloorDivTest.php b/test/Twig/Tests/Node/Expression/Binary/FloorDivTest.php new file mode 100644 index 0000000..046e9be --- /dev/null +++ b/test/Twig/Tests/Node/Expression/Binary/FloorDivTest.php @@ -0,0 +1,49 @@ +assertEquals($left, $node->left); + $this->assertEquals($right, $node->right); + } + + /** + * @covers Twig_Node_Expression_Binary_FloorDiv::compile + * @covers Twig_Node_Expression_Binary_FloorDiv::operator + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $left = new Twig_Node_Expression_Constant(1, 0); + $right = new Twig_Node_Expression_Constant(2, 0); + $node = new Twig_Node_Expression_Binary_FloorDiv($left, $right, 0); + + return array( + array($node, 'floor((1) / (2))'), + ); + } +} diff --git a/test/Twig/Tests/Node/Expression/Binary/ModTest.php b/test/Twig/Tests/Node/Expression/Binary/ModTest.php new file mode 100644 index 0000000..c56d39c --- /dev/null +++ b/test/Twig/Tests/Node/Expression/Binary/ModTest.php @@ -0,0 +1,49 @@ +assertEquals($left, $node->left); + $this->assertEquals($right, $node->right); + } + + /** + * @covers Twig_Node_Expression_Binary_Mod::compile + * @covers Twig_Node_Expression_Binary_Mod::operator + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $left = new Twig_Node_Expression_Constant(1, 0); + $right = new Twig_Node_Expression_Constant(2, 0); + $node = new Twig_Node_Expression_Binary_Mod($left, $right, 0); + + return array( + array($node, '(1) % (2)'), + ); + } +} diff --git a/test/Twig/Tests/Node/Expression/Binary/MulTest.php b/test/Twig/Tests/Node/Expression/Binary/MulTest.php new file mode 100644 index 0000000..27be8ac --- /dev/null +++ b/test/Twig/Tests/Node/Expression/Binary/MulTest.php @@ -0,0 +1,49 @@ +assertEquals($left, $node->left); + $this->assertEquals($right, $node->right); + } + + /** + * @covers Twig_Node_Expression_Binary_Mul::compile + * @covers Twig_Node_Expression_Binary_Mul::operator + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $left = new Twig_Node_Expression_Constant(1, 0); + $right = new Twig_Node_Expression_Constant(2, 0); + $node = new Twig_Node_Expression_Binary_Mul($left, $right, 0); + + return array( + array($node, '(1) * (2)'), + ); + } +} diff --git a/test/Twig/Tests/Node/Expression/Binary/OrTest.php b/test/Twig/Tests/Node/Expression/Binary/OrTest.php new file mode 100644 index 0000000..ca05101 --- /dev/null +++ b/test/Twig/Tests/Node/Expression/Binary/OrTest.php @@ -0,0 +1,49 @@ +assertEquals($left, $node->left); + $this->assertEquals($right, $node->right); + } + + /** + * @covers Twig_Node_Expression_Binary_Or::compile + * @covers Twig_Node_Expression_Binary_Or::operator + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $left = new Twig_Node_Expression_Constant(1, 0); + $right = new Twig_Node_Expression_Constant(2, 0); + $node = new Twig_Node_Expression_Binary_Or($left, $right, 0); + + return array( + array($node, '(1) || (2)'), + ); + } +} diff --git a/test/Twig/Tests/Node/Expression/Binary/SubTest.php b/test/Twig/Tests/Node/Expression/Binary/SubTest.php new file mode 100644 index 0000000..ac13130 --- /dev/null +++ b/test/Twig/Tests/Node/Expression/Binary/SubTest.php @@ -0,0 +1,49 @@ +assertEquals($left, $node->left); + $this->assertEquals($right, $node->right); + } + + /** + * @covers Twig_Node_Expression_Binary_Sub::compile + * @covers Twig_Node_Expression_Binary_Sub::operator + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $left = new Twig_Node_Expression_Constant(1, 0); + $right = new Twig_Node_Expression_Constant(2, 0); + $node = new Twig_Node_Expression_Binary_Sub($left, $right, 0); + + return array( + array($node, '(1) - (2)'), + ); + } +} diff --git a/test/Twig/Tests/Node/Expression/CompareTest.php b/test/Twig/Tests/Node/Expression/CompareTest.php new file mode 100644 index 0000000..0c08bf9 --- /dev/null +++ b/test/Twig/Tests/Node/Expression/CompareTest.php @@ -0,0 +1,72 @@ +', 0), + new Twig_Node_Expression_Constant(2, 0), + ), array(), 0); + $node = new Twig_Node_Expression_Compare($expr, $ops, 0); + + $this->assertEquals($expr, $node->expr); + $this->assertEquals($ops, $node->ops); + } + + /** + * @covers Twig_Node_Expression_Compare::compile + * @covers Twig_Node_Expression_Compare::compileIn + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $tests = array(); + + $expr = new Twig_Node_Expression_Constant(1, 0); + $ops = new Twig_Node(array( + new Twig_Node_Expression_Constant('>', 0), + new Twig_Node_Expression_Constant(2, 0), + ), array(), 0); + $node = new Twig_Node_Expression_Compare($expr, $ops, 0); + $tests[] = array($node, '1 > 2'); + + $ops = new Twig_Node(array( + new Twig_Node_Expression_Constant('>', 0), + new Twig_Node_Expression_Constant(2, 0), + new Twig_Node_Expression_Constant('<', 0), + new Twig_Node_Expression_Constant(4, 0), + ), array(), 0); + $node = new Twig_Node_Expression_Compare($expr, $ops, 0); + $tests[] = array($node, '1 > ($tmp1 = 2) && ($tmp1 < 4)'); + + $ops = new Twig_Node(array( + new Twig_Node_Expression_Constant('in', 0), + new Twig_Node_Expression_Constant(2, 0), + ), array(), 0); + $node = new Twig_Node_Expression_Compare($expr, $ops, 0); + $tests[] = array($node, 'twig_in_filter(1, 2)'); + + return $tests; + } +} diff --git a/test/Twig/Tests/Node/Expression/ConditionalTest.php b/test/Twig/Tests/Node/Expression/ConditionalTest.php new file mode 100644 index 0000000..3b22fb0 --- /dev/null +++ b/test/Twig/Tests/Node/Expression/ConditionalTest.php @@ -0,0 +1,52 @@ +assertEquals($expr1, $node->expr1); + $this->assertEquals($expr2, $node->expr2); + $this->assertEquals($expr3, $node->expr3); + } + + /** + * @covers Twig_Node_Expression_Conditional::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $tests = array(); + + $expr1 = new Twig_Node_Expression_Constant(1, 0); + $expr2 = new Twig_Node_Expression_Constant(2, 0); + $expr3 = new Twig_Node_Expression_Constant(3, 0); + $node = new Twig_Node_Expression_Conditional($expr1, $expr2, $expr3, 0); + $tests[] = array($node, '(1) ? (2) : (3)'); + + return $tests; + } +} diff --git a/test/Twig/Tests/Node/Expression/ConstantTest.php b/test/Twig/Tests/Node/Expression/ConstantTest.php new file mode 100644 index 0000000..2824518 --- /dev/null +++ b/test/Twig/Tests/Node/Expression/ConstantTest.php @@ -0,0 +1,44 @@ +assertEquals('foo', $node['value']); + } + + /** + * @covers Twig_Node_Expression_Constant::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $tests = array(); + + $node = new Twig_Node_Expression_Constant('foo', 0); + $tests[] = array($node, '"foo"'); + + return $tests; + } +} diff --git a/test/Twig/Tests/Node/Expression/FilterTest.php b/test/Twig/Tests/Node/Expression/FilterTest.php new file mode 100644 index 0000000..77f4d5f --- /dev/null +++ b/test/Twig/Tests/Node/Expression/FilterTest.php @@ -0,0 +1,170 @@ +assertEquals($expr, $node->node); + $this->assertEquals($filters, $node->filters); + } + + /** + * @covers Twig_Node_Expression_Filter::hasFilter + */ + public function testHasFilter() + { + $expr = new Twig_Node_Expression_Constant('foo', 0); + $filters = new Twig_Node(array( + new Twig_Node_Expression_Constant('upper', 0), + new Twig_Node(), + ), array(), 0); + $node = new Twig_Node_Expression_Filter($expr, $filters, 0); + + $this->assertTrue($node->hasFilter('upper')); + $this->assertFalse($node->hasFilter('lower')); + } + + /** + * @covers Twig_Node_Expression_Filter::prependFilter + */ + public function testPrependFilter() + { + $expr = new Twig_Node_Expression_Constant('foo', 0); + $filters = new Twig_Node(array( + new Twig_Node_Expression_Constant('upper', 0), + new Twig_Node(), + ), array(), 0); + $node = new Twig_Node_Expression_Filter($expr, $filters, 0); + + $a = new Twig_Node_Expression_Constant('lower', 0); + $b = new Twig_Node_Expression_Constant('foobar', 0); + $node->prependFilter($a, $b); + + $filters = new Twig_Node(array( + $a, + $b, + new Twig_Node_Expression_Constant('upper', 0), + new Twig_Node(), + ), array(), 0); + + $this->assertEquals($filters, $node->filters); + } + + /** + * @covers Twig_Node_Expression_Filter::appendFilter + */ + public function testAppendFilter() + { + $expr = new Twig_Node_Expression_Constant('foo', 0); + $filters = new Twig_Node(array( + new Twig_Node_Expression_Constant('upper', 0), + new Twig_Node(), + ), array(), 0); + $node = new Twig_Node_Expression_Filter($expr, $filters, 0); + + $a = new Twig_Node_Expression_Constant('lower', 0); + $b = new Twig_Node_Expression_Constant('foobar', 0); + $node->appendFilter($a, $b); + + $filters = new Twig_Node(array( + new Twig_Node_Expression_Constant('upper', 0), + new Twig_Node(), + $a, + $b, + ), array(), 0); + + $this->assertEquals($filters, $node->filters); + } + + /** + * @covers Twig_Node_Expression_Filter::appendFilters + */ + public function testAppendFilters() + { + $expr = new Twig_Node_Expression_Constant('foo', 0); + $filters = new Twig_Node(array( + new Twig_Node_Expression_Constant('upper', 0), + new Twig_Node(), + ), array(), 0); + $node = new Twig_Node_Expression_Filter($expr, $filters, 0); + + $others = new Twig_Node(array( + $a = new Twig_Node_Expression_Constant('lower', 0), + $b = new Twig_Node_Expression_Constant('foobar', 0), + ), array(), 0); + $node->appendFilters($others); + + $filters = new Twig_Node(array( + new Twig_Node_Expression_Constant('upper', 0), + new Twig_Node(), + $a, + $b, + ), array(), 0); + + $this->assertEquals($filters, $node->filters); + } + + /** + * @covers Twig_Node_Expression_Filter::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + + $expr = new Twig_Node_Expression_Constant('foo', 0); + $filters = new Twig_Node(array( + new Twig_Node_Expression_Constant('foobar', 0), + new Twig_Node(array(new Twig_Node_Expression_Constant('bar', 0), new Twig_Node_Expression_Constant('foobar', 0))), + ), array(), 0); + $node = new Twig_Node_Expression_Filter($expr, $filters, 0); + + $tests[] = array($node, '$this->resolveMissingFilter("foobar", array("foo", "bar", "foobar"))'); + + try { + $node->compile($this->getCompiler()); + $this->fail(); + } catch (Exception $e) { + $this->assertEquals('Twig_SyntaxError', get_class($e)); + } + } + + public function getTests() + { + $tests = array(); + + $expr = new Twig_Node_Expression_Constant('foo', 0); + $filters = new Twig_Node(array( + new Twig_Node_Expression_Constant('upper', 0), + new Twig_Node(), + new Twig_Node_Expression_Constant('lower', 0), + new Twig_Node(array(new Twig_Node_Expression_Constant('bar', 0), new Twig_Node_Expression_Constant('foobar', 0))), + ), array(), 0); + $node = new Twig_Node_Expression_Filter($expr, $filters, 0); + + $tests[] = array($node, 'twig_lower_filter($this->getEnvironment(), twig_upper_filter($this->getEnvironment(), "foo"), "bar", "foobar")'); + + return $tests; + } +} diff --git a/test/Twig/Tests/Node/Expression/GetAttrTest.php b/test/Twig/Tests/Node/Expression/GetAttrTest.php new file mode 100644 index 0000000..b7f4393 --- /dev/null +++ b/test/Twig/Tests/Node/Expression/GetAttrTest.php @@ -0,0 +1,66 @@ +assertEquals($expr, $node->node); + $this->assertEquals($attr, $node->attribute); + $this->assertEquals($args, $node->arguments); + $this->assertEquals('[', $node['token_value']); + } + + /** + * @covers Twig_Node_Expression_GetAttr::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $tests = array(); + + $expr = new Twig_Node_Expression_Name('foo', 0); + $attr = new Twig_Node_Expression_Constant('bar', 0); + $args = new Twig_Node(); + $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, 0); + $tests[] = array($node, '$this->getAttribute($this->getContext($context, \'foo\'), "bar", array())'); + + $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, 0, '['); + $tests[] = array($node, '$this->getAttribute($this->getContext($context, \'foo\'), "bar", array(), true)'); + + $args = new Twig_Node(array( + new Twig_Node_Expression_Name('foo', 0), + new Twig_Node_Expression_Constant('bar', 0), + )); + $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, 0); + $tests[] = array($node, '$this->getAttribute($this->getContext($context, \'foo\'), "bar", array($this->getContext($context, \'foo\'), "bar", ))'); + + return $tests; + } +} diff --git a/test/Twig/Tests/Node/Expression/NameTest.php b/test/Twig/Tests/Node/Expression/NameTest.php new file mode 100644 index 0000000..9f52019 --- /dev/null +++ b/test/Twig/Tests/Node/Expression/NameTest.php @@ -0,0 +1,43 @@ +assertEquals('foo', $node['name']); + } + + /** + * @covers Twig_Node_Expression_Name::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $node = new Twig_Node_Expression_Name('foo', 0); + + return array( + array($node, '$this->getContext($context, \'foo\')'), + ); + } +} diff --git a/test/Twig/Tests/Node/Expression/Unary/NegTest.php b/test/Twig/Tests/Node/Expression/Unary/NegTest.php new file mode 100644 index 0000000..b53e59a --- /dev/null +++ b/test/Twig/Tests/Node/Expression/Unary/NegTest.php @@ -0,0 +1,46 @@ +assertEquals($expr, $node->node); + } + + /** + * @covers Twig_Node_Expression_Unary_Neg::compile + * @covers Twig_Node_Expression_Unary_Neg::operator + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $node = new Twig_Node_Expression_Constant(1, 0); + $node = new Twig_Node_Expression_Unary_Neg($node, 0); + + return array( + array($node, '(-1)'), + ); + } +} diff --git a/test/Twig/Tests/Node/Expression/Unary/NotTest.php b/test/Twig/Tests/Node/Expression/Unary/NotTest.php new file mode 100644 index 0000000..e5bcb54 --- /dev/null +++ b/test/Twig/Tests/Node/Expression/Unary/NotTest.php @@ -0,0 +1,46 @@ +assertEquals($expr, $node->node); + } + + /** + * @covers Twig_Node_Expression_Unary_Not::compile + * @covers Twig_Node_Expression_Unary_Not::operator + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $node = new Twig_Node_Expression_Constant(1, 0); + $node = new Twig_Node_Expression_Unary_Not($node, 0); + + return array( + array($node, '(!1)'), + ); + } +} diff --git a/test/Twig/Tests/Node/Expression/Unary/PosTest.php b/test/Twig/Tests/Node/Expression/Unary/PosTest.php new file mode 100644 index 0000000..bb4815c --- /dev/null +++ b/test/Twig/Tests/Node/Expression/Unary/PosTest.php @@ -0,0 +1,46 @@ +assertEquals($expr, $node->node); + } + + /** + * @covers Twig_Node_Expression_Unary_Pos::compile + * @covers Twig_Node_Expression_Unary_Pos::operator + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $node = new Twig_Node_Expression_Constant(1, 0); + $node = new Twig_Node_Expression_Unary_Pos($node, 0); + + return array( + array($node, '(+1)'), + ); + } +} diff --git a/test/Twig/Tests/Node/ForTest.php b/test/Twig/Tests/Node/ForTest.php new file mode 100644 index 0000000..9517972 --- /dev/null +++ b/test/Twig/Tests/Node/ForTest.php @@ -0,0 +1,156 @@ +assertEquals($keyTarget, $node->key_target); + $this->assertEquals($valueTarget, $node->value_target); + $this->assertEquals($seq, $node->seq); + $this->assertEquals($body, $node->body); + $this->assertEquals(null, $node->else); + + $this->assertEquals($withLoop, $node['with_loop']); + + $else = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0); + $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $withLoop, 0); + $this->assertEquals($else, $node->else); + } + + /** + * @covers Twig_Node_For::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $tests = array(); + + $keyTarget = new Twig_Node_Expression_AssignName('key', 0); + $valueTarget = new Twig_Node_Expression_AssignName('item', 0); + $seq = new Twig_Node_Expression_Name('items', 0); + $body = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0); + $else = null; + $withLoop = false; + $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $withLoop, 0); + + $tests[] = array($node, <<getContext(\$context, 'items'), true); +foreach (\$context['_seq'] as \$context['key'] => \$context['item']) { + echo \$this->getContext(\$context, 'foo'); +} +\$_parent = \$context['_parent']; +unset(\$context['_seq'], \$context['_iterated'], \$context['key'], \$context['item'], \$context['_parent'], \$context['loop']); +\$context = array_merge(\$_parent, array_intersect_key(\$context, \$_parent)); +EOF + ); + + $keyTarget = new Twig_Node_Expression_AssignName('k', 0); + $valueTarget = new Twig_Node_Expression_AssignName('v', 0); + $seq = new Twig_Node_Expression_Name('values', 0); + $body = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0); + $else = null; + $withLoop = true; + $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $withLoop, 0); + + $tests[] = array($node, <<getContext(\$context, 'values'), true); +\$length = count(\$context['_seq']); +\$context['loop'] = array( + 'parent' => \$context['_parent'], + 'length' => \$length, + 'index0' => 0, + 'index' => 1, + 'revindex0' => \$length - 1, + 'revindex' => \$length, + 'first' => true, + 'last' => 1 === \$length, +); +foreach (\$context['_seq'] as \$context['k'] => \$context['v']) { + echo \$this->getContext(\$context, 'foo'); + ++\$context['loop']['index0']; + ++\$context['loop']['index']; + --\$context['loop']['revindex0']; + --\$context['loop']['revindex']; + \$context['loop']['first'] = false; + \$context['loop']['last'] = 0 === \$context['loop']['revindex0']; +} +\$_parent = \$context['_parent']; +unset(\$context['_seq'], \$context['_iterated'], \$context['k'], \$context['v'], \$context['_parent'], \$context['loop']); +\$context = array_merge(\$_parent, array_intersect_key(\$context, \$_parent)); +EOF + ); + + $keyTarget = new Twig_Node_Expression_AssignName('k', 0); + $valueTarget = new Twig_Node_Expression_AssignName('v', 0); + $seq = new Twig_Node_Expression_Name('values', 0); + $body = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0); + $else = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0); + $withLoop = true; + $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $withLoop, 0); + + $tests[] = array($node, <<getContext(\$context, 'values'), true); +\$length = count(\$context['_seq']); +\$context['loop'] = array( + 'parent' => \$context['_parent'], + 'length' => \$length, + 'index0' => 0, + 'index' => 1, + 'revindex0' => \$length - 1, + 'revindex' => \$length, + 'first' => true, + 'last' => 1 === \$length, +); +foreach (\$context['_seq'] as \$context['k'] => \$context['v']) { + \$context['_iterated'] = true; + echo \$this->getContext(\$context, 'foo'); + ++\$context['loop']['index0']; + ++\$context['loop']['index']; + --\$context['loop']['revindex0']; + --\$context['loop']['revindex']; + \$context['loop']['first'] = false; + \$context['loop']['last'] = 0 === \$context['loop']['revindex0']; +} +if (!\$context['_iterated']) { + echo \$this->getContext(\$context, 'foo'); +} +\$_parent = \$context['_parent']; +unset(\$context['_seq'], \$context['_iterated'], \$context['k'], \$context['v'], \$context['_parent'], \$context['loop']); +\$context = array_merge(\$_parent, array_intersect_key(\$context, \$_parent)); +EOF + ); + + return $tests; + } +} diff --git a/test/Twig/Tests/Node/IfTest.php b/test/Twig/Tests/Node/IfTest.php new file mode 100644 index 0000000..41b5422 --- /dev/null +++ b/test/Twig/Tests/Node/IfTest.php @@ -0,0 +1,99 @@ +assertEquals($t, $node->tests); + $this->assertEquals(null, $node->else); + + $else = new Twig_Node_Print(new Twig_Node_Expression_Name('bar', 0), 0); + $node = new Twig_Node_If($t, $else, 0); + $this->assertEquals($else, $node->else); + } + + /** + * @covers Twig_Node_If::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $tests = array(); + + $t = new Twig_Node(array( + new Twig_Node_Expression_Constant(true, 0), + new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0), + ), array(), 0); + $else = null; + $node = new Twig_Node_If($t, $else, 0); + + $tests[] = array($node, <<getContext(\$context, 'foo'); +} +EOF + ); + + $t = new Twig_Node(array( + new Twig_Node_Expression_Constant(true, 0), + new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0), + new Twig_Node_Expression_Constant(false, 0), + new Twig_Node_Print(new Twig_Node_Expression_Name('bar', 0), 0), + ), array(), 0); + $else = null; + $node = new Twig_Node_If($t, $else, 0); + + $tests[] = array($node, <<getContext(\$context, 'foo'); +} elseif (false) { + echo \$this->getContext(\$context, 'bar'); +} +EOF + ); + + $t = new Twig_Node(array( + new Twig_Node_Expression_Constant(true, 0), + new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0), + ), array(), 0); + $else = new Twig_Node_Print(new Twig_Node_Expression_Name('bar', 0), 0); + $node = new Twig_Node_If($t, $else, 0); + + $tests[] = array($node, <<getContext(\$context, 'foo'); +} else { + echo \$this->getContext(\$context, 'bar'); +} +EOF + ); + + return $tests; + } +} diff --git a/test/Twig/Tests/Node/ImportTest.php b/test/Twig/Tests/Node/ImportTest.php new file mode 100644 index 0000000..5d4b200 --- /dev/null +++ b/test/Twig/Tests/Node/ImportTest.php @@ -0,0 +1,50 @@ +assertEquals($macro, $node->expr); + $this->assertEquals($var, $node->var); + } + + /** + * @covers Twig_Node_Import::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $tests = array(); + + $macro = new Twig_Node_Expression_Constant('foo.twig', 0); + $var = new Twig_Node_Expression_AssignName('macro', 0); + $node = new Twig_Node_Import($macro, $var, 0); + + $tests[] = array($node, '$context[\'macro\'] = $this->env->loadTemplate("foo.twig", true);'); + + return $tests; + } +} diff --git a/test/Twig/Tests/Node/IncludeTest.php b/test/Twig/Tests/Node/IncludeTest.php new file mode 100644 index 0000000..439c69c --- /dev/null +++ b/test/Twig/Tests/Node/IncludeTest.php @@ -0,0 +1,56 @@ +assertEquals(null, $node->variables); + $this->assertEquals($expr, $node->expr); + + $vars = new Twig_Node_Expression_Array(array('foo' => new Twig_Node_Expression_Constant(true, 0)), 0); + $node = new Twig_Node_Include($expr, $vars, 0); + $this->assertEquals($vars, $node->variables); + } + + /** + * @covers Twig_Node_Include::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $tests = array(); + + $expr = new Twig_Node_Expression_Constant('foo.twig', 0); + $node = new Twig_Node_Include($expr, null, 0); + $tests[] = array($node, '$this->env->loadTemplate("foo.twig")->display($context);'); + + $expr = new Twig_Node_Expression_Constant('foo.twig', 0); + $vars = new Twig_Node_Expression_Array(array('foo' => new Twig_Node_Expression_Constant(true, 0)), 0); + $node = new Twig_Node_Include($expr, $vars, 0); + $tests[] = array($node, '$this->env->loadTemplate("foo.twig")->display(array("foo" => true));'); + + return $tests; + } +} diff --git a/test/Twig/Tests/Node/MacroTest.php b/test/Twig/Tests/Node/MacroTest.php new file mode 100644 index 0000000..959d016 --- /dev/null +++ b/test/Twig/Tests/Node/MacroTest.php @@ -0,0 +1,59 @@ +assertEquals($body, $node->body); + $this->assertEquals($arguments, $node->arguments); + $this->assertEquals('foo', $node['name']); + } + + /** + * @covers Twig_Node_Macro::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $body = new Twig_Node_Text('foo', 0); + $arguments = new Twig_Node(array(new Twig_Node_Expression_Name('foo', 0)), array(), 0); + $node = new Twig_Node_Macro('foo', $body, $arguments, 0); + + return array( + array($node, << \$foo, + ); + + echo "foo"; +} +EOF + ), + ); + } +} diff --git a/test/Twig/Tests/Node/ModuleTest.php b/test/Twig/Tests/Node/ModuleTest.php new file mode 100644 index 0000000..cea65ac --- /dev/null +++ b/test/Twig/Tests/Node/ModuleTest.php @@ -0,0 +1,128 @@ +assertEquals($body, $node->body); + $this->assertEquals($blocks, $node->blocks); + $this->assertEquals($macros, $node->macros); + $this->assertEquals($filename, $node['filename']); + $this->assertEquals($extends, $node['extends']); + } + + /** + * @covers Twig_Node_Module::compile + * @covers Twig_Node_Module::compileTemplate + * @covers Twig_Node_Module::compileMacros + * @covers Twig_Node_Module::compileClassHeader + * @covers Twig_Node_Module::compileDisplayHeader + * @covers Twig_Node_Module::compileDisplayBody + * @covers Twig_Node_Module::compileDisplayFooter + * @covers Twig_Node_Module::compileGetName + * @covers Twig_Node_Module::compileClassFooter + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $twig = new Twig_Environment(new Twig_Loader_String()); + + $tests = array(); + + $body = new Twig_Node_Text('foo', 0); + $extends = null; + $blocks = new Twig_Node(); + $macros = new Twig_Node(); + $filename = 'foo.twig'; + + $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $filename); + $tests[] = array($node, <<loadTemplate("layout.twig"); + +/* foo.twig */ +class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends __TwigTemplate_d8fb9d03f55738ff78518e1bc2741faf +{ + public function display(array \$context) + { + \$context['macro'] = \$this->env->loadTemplate("foo.twig", true); + + parent::display(\$context); + } + + public function getName() + { + return "foo.twig"; + } + +} + +class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34_Macro extends Twig_Macro +{ +} +EOF + , $twig); + + return $tests; + } +} diff --git a/test/Twig/Tests/Node/ParentTest.php b/test/Twig/Tests/Node/ParentTest.php new file mode 100644 index 0000000..51f5419 --- /dev/null +++ b/test/Twig/Tests/Node/ParentTest.php @@ -0,0 +1,42 @@ +assertEquals('foo', $node['name']); + } + + /** + * @covers Twig_Node_Parent::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $tests = array(); + $tests[] = array(new Twig_Node_Parent('foo', 0), 'parent::block_foo($context);'); + + return $tests; + } +} diff --git a/test/Twig/Tests/Node/PrintTest.php b/test/Twig/Tests/Node/PrintTest.php new file mode 100644 index 0000000..53ef15e --- /dev/null +++ b/test/Twig/Tests/Node/PrintTest.php @@ -0,0 +1,43 @@ +assertEquals($expr, $node->expr); + } + + /** + * @covers Twig_Node_Print::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $tests = array(); + $tests[] = array(new Twig_Node_Print(new Twig_Node_Expression_Constant('foo', 0), 0), 'echo "foo";'); + + return $tests; + } +} diff --git a/test/Twig/Tests/Node/SandboxTest.php b/test/Twig/Tests/Node/SandboxTest.php new file mode 100644 index 0000000..d3598a9 --- /dev/null +++ b/test/Twig/Tests/Node/SandboxTest.php @@ -0,0 +1,57 @@ +assertEquals($body, $node->body); + } + + /** + * @covers Twig_Node_Sandbox::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $tests = array(); + + $body = new Twig_Node_Text('foo', 0); + $node = new Twig_Node_Sandbox($body, 0); + + $tests[] = array($node, <<env->getExtension('sandbox'); +if (!\$alreadySandboxed = \$sandbox->isSandboxed()) { + \$sandbox->enableSandbox(); +} +echo "foo"; +if (!\$alreadySandboxed) { + \$sandbox->disableSandbox(); +} +EOF + ); + + return $tests; + } +} diff --git a/test/Twig/Tests/Node/SandboxedModuleTest.php b/test/Twig/Tests/Node/SandboxedModuleTest.php new file mode 100644 index 0000000..3ef84c1 --- /dev/null +++ b/test/Twig/Tests/Node/SandboxedModuleTest.php @@ -0,0 +1,141 @@ +assertEquals($body, $node->body); + $this->assertEquals($blocks, $node->blocks); + $this->assertEquals($macros, $node->macros); + $this->assertEquals($filename, $node['filename']); + $this->assertEquals($extends, $node['extends']); + } + + /** + * @covers Twig_Node_SandboxedModule::compile + * @covers Twig_Node_SandboxedModule::compileDisplayBody + * @covers Twig_Node_SandboxedModule::compileDisplayFooter + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $twig = new Twig_Environment(new Twig_Loader_String()); + + $tests = array(); + + $body = new Twig_Node_Text('foo', 0); + $extends = null; + $blocks = new Twig_Node(); + $macros = new Twig_Node(); + $filename = 'foo.twig'; + + $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $filename); + $node = new Twig_Node_SandboxedModule($node, array('for'), array('upper')); + + $tests[] = array($node, <<checkSecurity(); + echo "foo"; + } + + protected function checkSecurity() { + \$this->env->getExtension('sandbox')->checkSecurity( + array('upper'), + array('for') + ); + } + + public function getName() + { + return "foo.twig"; + } + +} + +class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34_Macro extends Twig_Macro +{ +} +EOF + , $twig); + + $body = new Twig_Node_Text('foo', 0); + $extends = 'layout.twig'; + $blocks = new Twig_Node(); + $macros = new Twig_Node(); + $filename = 'foo.twig'; + + $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $filename); + $node = new Twig_Node_SandboxedModule($node, array('for'), array('upper')); + + $tests[] = array($node, <<loadTemplate("layout.twig"); + +/* foo.twig */ +class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends __TwigTemplate_d8fb9d03f55738ff78518e1bc2741faf +{ + public function display(array \$context) + { + + parent::display(\$context); + } + + protected function checkSecurity() { + \$this->env->getExtension('sandbox')->checkSecurity( + array('upper'), + array('for') + ); + + parent::checkSecurity(); + } + + public function getName() + { + return "foo.twig"; + } + +} + +class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34_Macro extends Twig_Macro +{ +} +EOF + , $twig); + + return $tests; + } +} diff --git a/test/Twig/Tests/Node/SandboxedPrintTest.php b/test/Twig/Tests/Node/SandboxedPrintTest.php new file mode 100644 index 0000000..ee205a6 --- /dev/null +++ b/test/Twig/Tests/Node/SandboxedPrintTest.php @@ -0,0 +1,52 @@ +assertEquals($expr, $node->expr); + } + + /** + * @covers Twig_Node_SandboxedPrint::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $tests = array(); + + $node = new Twig_Node_Print(new Twig_Node_Expression_Constant('foo', 0), 0); + $tests[] = array(new Twig_Node_SandboxedPrint($node), <<env->hasExtension('sandbox') && is_object("foo")) { + \$this->env->getExtension('sandbox')->checkMethodAllowed("foo", '__toString'); +} +echo "foo"; +EOF + ); + + return $tests; + } +} diff --git a/test/Twig/Tests/Node/SetTest.php b/test/Twig/Tests/Node/SetTest.php new file mode 100644 index 0000000..dff9bde --- /dev/null +++ b/test/Twig/Tests/Node/SetTest.php @@ -0,0 +1,68 @@ +assertEquals($names, $node->names); + $this->assertEquals($values, $node->values); + $this->assertEquals(false, $node['capture']); + } + + /** + * @covers Twig_Node_Set::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $tests = array(); + + $names = new Twig_Node(array(new Twig_Node_Expression_AssignName('foo', 0)), array(), 0); + $values = new Twig_Node(array(new Twig_Node_Expression_Constant('foo', 0)), array(), 0); + $node = new Twig_Node_Set(false, $names, $values, 0); + $tests[] = array($node, '$context[\'foo\'] = "foo";'); + + $names = new Twig_Node(array(new Twig_Node_Expression_AssignName('foo', 0)), array(), 0); + $values = new Twig_Node(array(new Twig_Node_Print(new Twig_Node_Expression_Constant('foo', 0), 0)), array(), 0); + $node = new Twig_Node_Set(true, $names, $values, 0); + $tests[] = array($node, <<getContext(\$context, 'bar')); +EOF + ); + + return $tests; + } +} diff --git a/test/Twig/Tests/Node/TestCase.php b/test/Twig/Tests/Node/TestCase.php new file mode 100644 index 0000000..c5b974f --- /dev/null +++ b/test/Twig/Tests/Node/TestCase.php @@ -0,0 +1,40 @@ +assertNodeCompilation($source, $node, $environment); + } + + public function assertNodeCompilation($source, Twig_Node $node, Twig_Environment $environment = null) + { + $compiler = $this->getCompiler($environment); + $compiler->compile($node); + + $this->assertEquals($source, trim($compiler->getSource())); + } + + protected function getCompiler(Twig_Environment $environment = null) + { + return new Twig_Compiler(null === $environment ? $this->getEnvironment() : $environment); + } + + protected function getEnvironment() + { + return new Twig_Environment(); + } +} diff --git a/test/Twig/Tests/Node/TextTest.php b/test/Twig/Tests/Node/TextTest.php new file mode 100644 index 0000000..8550d6f --- /dev/null +++ b/test/Twig/Tests/Node/TextTest.php @@ -0,0 +1,42 @@ +assertEquals('foo', $node['data']); + } + + /** + * @covers Twig_Node_Text::compile + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + } + + public function getTests() + { + $tests = array(); + $tests[] = array(new Twig_Node_Text('foo', 0), 'echo "foo";'); + + return $tests; + } +} diff --git a/test/Twig/Tests/Node/TransTest.php b/test/Twig/Tests/Node/TransTest.php new file mode 100644 index 0000000..3182fd9 --- /dev/null +++ b/test/Twig/Tests/Node/TransTest.php @@ -0,0 +1,97 @@ +assertEquals($body, $node->body); + $this->assertEquals($count, $node->count); + $this->assertEquals($plural, $node->plural); + } + + /** + * @covers Twig_Node_Trans::compile + * @covers Twig_Node_Trans::compileString + * @dataProvider getTests + */ + public function testCompile($node, $source, $environment = null) + { + parent::testCompile($node, $source, $environment); + + $body = new Twig_Node(array( + new Twig_Node_Expression_Constant('Hello', 0), + ), array(), 0); + $node = new Twig_Node_Trans(null, $body, null, 0); + + try { + $node->compile($this->getCompiler()); + $this->fail(); + } catch (Exception $e) { + $this->assertEquals('Twig_SyntaxError', get_class($e)); + } + } + + public function getTests() + { + $tests = array(); + + $body = new Twig_Node(array( + new Twig_Node_Text('Hello', 0), + ), array(), 0); + $node = new Twig_Node_Trans(null, $body, null, 0); + $tests[] = array($node, 'echo gettext("Hello");'); + + $body = new Twig_Node(array( + new Twig_Node_Text('J\'ai ', 0), + new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0), + new Twig_Node_Text(' pommes', 0), + ), array(), 0); + $node = new Twig_Node_Trans(null, $body, null, 0); + $tests[] = array($node, 'echo strtr(gettext("J\'ai %foo% pommes"), array("%foo%" => $this->getContext($context, \'foo\'), ));'); + + $count = new Twig_Node_Expression_Constant(12, 0); + $body = new Twig_Node(array( + new Twig_Node_Text('Hey ', 0), + new Twig_Node_Print(new Twig_Node_Expression_Name('name', 0), 0), + new Twig_Node_Text(', I have one apple', 0), + ), array(), 0); + $plural = new Twig_Node(array( + new Twig_Node_Text('Hey ', 0), + new Twig_Node_Print(new Twig_Node_Expression_Name('name', 0), 0), + new Twig_Node_Text(', I have ', 0), + new Twig_Node_Print(new Twig_Node_Expression_Name('count', 0), 0), + new Twig_Node_Text(' apples', 0), + ), array(), 0); + $node = new Twig_Node_Trans($count, $body, $plural, 0); + $tests[] = array($node, 'echo strtr(ngettext("Hey %name%, I have one apple", "Hey %name%, I have %count% apples", abs(12)), array("%name%" => $this->getContext($context, \'name\'), "%name%" => $this->getContext($context, \'name\'), "%count%" => abs(12), ));'); + + return $tests; + } +} diff --git a/test/Twig/Tests/integrationTest.php b/test/Twig/Tests/integrationTest.php index 091c9e5..0c9c07b 100644 --- a/test/Twig/Tests/integrationTest.php +++ b/test/Twig/Tests/integrationTest.php @@ -11,16 +11,12 @@ class Twig_Tests_IntegrationTest extends PHPUnit_Framework_TestCase { - static protected $fixturesDir; - - public function setUp() + public function getTests() { - self::$fixturesDir = realpath(dirname(__FILE__).'/../../fixtures/'); - } + $fixturesDir = realpath(dirname(__FILE__).'/../../fixtures/'); + $tests = array(); - public function testIntegration() - { - foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator(self::$fixturesDir), RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($fixturesDir), RecursiveIteratorIterator::LEAVES_ONLY) as $file) { if (!preg_match('/\.test$/', $file)) { continue; } @@ -28,7 +24,7 @@ class Twig_Tests_IntegrationTest extends PHPUnit_Framework_TestCase $test = file_get_contents($file->getRealpath()); if (!preg_match('/--TEST--\s*(.*?)\s*((?:--TEMPLATE(?:\(.*?\))?--(?:.*?))+)--DATA--.*?--EXPECT--.*/s', $test, $match)) { - throw new InvalidArgumentException(sprintf('Test "%s" is not valid.', str_replace(self::$fixturesDir.'/', '', $file))); + throw new InvalidArgumentException(sprintf('Test "%s" is not valid.', str_replace($fixturesDir.'/', '', $file))); } $message = $match[1]; @@ -38,34 +34,44 @@ class Twig_Tests_IntegrationTest extends PHPUnit_Framework_TestCase $templates[($match[1] ? $match[1] : 'index.twig')] = $match[2]; } - $loader = new Twig_Loader_Array($templates); - $twig = new Twig_Environment($loader, array('trim_blocks' => true, 'cache' => false)); - $twig->addExtension(new Twig_Extension_Escaper()); - $twig->addExtension(new TestExtension()); + $tests[] = array(str_replace($fixturesDir.'/', '', $file), $test, $message, $templates); + } - try { - $template = $twig->loadTemplate('index.twig'); - } catch (Twig_SyntaxError $e) { - $e->setFilename(str_replace(self::$fixturesDir.'/', '', $file)); + return $tests; + } - throw $e; - } catch (Exception $e) { - throw new Twig_Error($e->getMessage().' (in '.str_replace(self::$fixturesDir, '', $file).')'); - } + /** + * @dataProvider getTests + */ + public function testIntegration($file, $test, $message, $templates) + { + $loader = new Twig_Loader_Array($templates); + $twig = new Twig_Environment($loader, array('trim_blocks' => true, 'cache' => false)); + $twig->addExtension(new Twig_Extension_Escaper()); + $twig->addExtension(new TestExtension()); + + try { + $template = $twig->loadTemplate('index.twig'); + } catch (Twig_SyntaxError $e) { + $e->setFilename($file); + + throw $e; + } catch (Exception $e) { + throw new Twig_Error($e->getMessage().' (in '.$file.')'); + } - preg_match_all('/--DATA--(.*?)--EXPECT--(.*?)(?=\-\-DATA\-\-|$)/s', $test, $matches, PREG_SET_ORDER); - foreach ($matches as $match) { - $output = trim($template->render(eval($match[1].';')), "\n "); - $expected = trim($match[2], "\n "); + preg_match_all('/--DATA--(.*?)--EXPECT--(.*?)(?=\-\-DATA\-\-|$)/s', $test, $matches, PREG_SET_ORDER); + foreach ($matches as $match) { + $output = trim($template->render(eval($match[1].';')), "\n "); + $expected = trim($match[2], "\n "); - $this->assertEquals($expected, $output, $message.' (in '.str_replace(self::$fixturesDir, '', $file).')'); - if ($output != $expected) { - echo 'Compiled template that failed:'; + $this->assertEquals($expected, $output, $message.' (in '.$file.')'); + if ($output != $expected) { + echo 'Compiled template that failed:'; - foreach (array_keys($templates) as $name) { - $source = $loader->getSource($name); - echo $twig->compile($twig->parse($twig->tokenize($source, $name))); - } + foreach (array_keys($templates) as $name) { + $source = $loader->getSource($name); + echo $twig->compile($twig->parse($twig->tokenize($source, $name))); } } } diff --git a/test/fixtures/tags/filter/multiple.test b/test/fixtures/tags/filter/multiple.test index c7a5790..75512ef 100644 --- a/test/fixtures/tags/filter/multiple.test +++ b/test/fixtures/tags/filter/multiple.test @@ -1,7 +1,7 @@ --TEST-- "filter" tags accept multiple chained filters --TEMPLATE-- -{% filter lower|capitalize %} +{% filter lower|title %} {{ var }} {% endfilter %} --DATA-- diff --git a/test/fixtures/tags/filter/nested.test b/test/fixtures/tags/filter/nested.test index d26bf6e..7e4e4eb 100644 --- a/test/fixtures/tags/filter/nested.test +++ b/test/fixtures/tags/filter/nested.test @@ -1,7 +1,7 @@ --TEST-- "filter" tags can be nested at will --TEMPLATE-- -{% filter capitalize %} +{% filter lower|title %} {{ var }} {% filter upper %} {{ var }} @@ -12,5 +12,5 @@ return array('var' => 'var') --EXPECT-- Var - VAR + Var Var