mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-15 20:06:31 +00:00
merged branch fabpot/loop-errors (PR #927)
This PR was merged into the master branch. Commits -------811dfadadded a syntax error when using a loop variable that is not defined (closes #925)aa15caesimplified code Discussion ---------- added a syntax error when using a loop variable that is not defined refs #925 --------------------------------------------------------------------------- by stof at 2012-12-10T15:18:30Z in the ifexpr, you should also check for ``loop.index`` & co as they are not yet incremented (and so cannot be used properly) --------------------------------------------------------------------------- by fabpot at 2012-12-10T20:07:08Z @stof fixed
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
* 1.12.0 (2012-XX-XX)
|
||||
|
||||
* added a syntax error when using a loop variable that is not defined
|
||||
* added the ability to set default values for macro arguments
|
||||
* added support for named arguments for filters, tests, and functions
|
||||
* moved filters/functions/tests syntax errors to the parser
|
||||
|
||||
@@ -33,25 +33,26 @@ class Twig_TokenParser_For extends Twig_TokenParser
|
||||
public function parse(Twig_Token $token)
|
||||
{
|
||||
$lineno = $token->getLine();
|
||||
$stream = $this->parser->getStream();
|
||||
$targets = $this->parser->getExpressionParser()->parseAssignmentExpression();
|
||||
$this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, 'in');
|
||||
$stream->expect(Twig_Token::OPERATOR_TYPE, 'in');
|
||||
$seq = $this->parser->getExpressionParser()->parseExpression();
|
||||
|
||||
$ifexpr = null;
|
||||
if ($this->parser->getStream()->test(Twig_Token::NAME_TYPE, 'if')) {
|
||||
$this->parser->getStream()->next();
|
||||
if ($stream->test(Twig_Token::NAME_TYPE, 'if')) {
|
||||
$stream->next();
|
||||
$ifexpr = $this->parser->getExpressionParser()->parseExpression();
|
||||
}
|
||||
|
||||
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
|
||||
$stream->expect(Twig_Token::BLOCK_END_TYPE);
|
||||
$body = $this->parser->subparse(array($this, 'decideForFork'));
|
||||
if ($this->parser->getStream()->next()->getValue() == 'else') {
|
||||
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
|
||||
if ($stream->next()->getValue() == 'else') {
|
||||
$stream->expect(Twig_Token::BLOCK_END_TYPE);
|
||||
$else = $this->parser->subparse(array($this, 'decideForEnd'), true);
|
||||
} else {
|
||||
$else = null;
|
||||
}
|
||||
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
|
||||
$stream->expect(Twig_Token::BLOCK_END_TYPE);
|
||||
|
||||
if (count($targets) > 1) {
|
||||
$keyTarget = $targets->getNode(0);
|
||||
@@ -64,6 +65,11 @@ class Twig_TokenParser_For extends Twig_TokenParser
|
||||
$valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine());
|
||||
}
|
||||
|
||||
if ($ifexpr) {
|
||||
$this->checkLoopUsageCondition($stream, $ifexpr);
|
||||
$this->checkLoopUsageBody($stream, $body);
|
||||
}
|
||||
|
||||
return new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, $lineno, $this->getTag());
|
||||
}
|
||||
|
||||
@@ -77,6 +83,47 @@ class Twig_TokenParser_For extends Twig_TokenParser
|
||||
return $token->test('endfor');
|
||||
}
|
||||
|
||||
// the loop variable cannot be used in the condition
|
||||
protected function checkLoopUsageCondition(Twig_TokenStream $stream, Twig_NodeInterface $node)
|
||||
{
|
||||
if ($node instanceof Twig_Node_Expression_GetAttr && 'loop' == $node->getNode('node')->getAttribute('name')) {
|
||||
throw new Twig_Error_Syntax('The "loop" variable cannot be used in a looping condition', $node->getLine(), $stream->getFilename());
|
||||
}
|
||||
|
||||
foreach ($node as $n) {
|
||||
if (!$n) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->checkLoopUsageCondition($stream, $n);
|
||||
}
|
||||
}
|
||||
|
||||
// check usage of non-defined loop-items
|
||||
// it does not catch all problems (for instance when a for is included into another or when the variable is used in an include)
|
||||
protected function checkLoopUsageBody(Twig_TokenStream $stream, Twig_NodeInterface $node)
|
||||
{
|
||||
if ($node instanceof Twig_Node_Expression_GetAttr && 'loop' == $node->getNode('node')->getAttribute('name')) {
|
||||
$attribute = $node->getNode('attribute');
|
||||
if ($attribute instanceof Twig_Node_Expression_Constant && in_array($attribute->getAttribute('value'), array('length', 'revindex0', 'revindex', 'last'))) {
|
||||
throw new Twig_Error_Syntax(sprintf('The "loop.%s" variable is not defined when looping with a condition', $attribute->getAttribute('value')), $node->getLine(), $stream->getFilename());
|
||||
}
|
||||
}
|
||||
|
||||
// should check for parent.loop.XXX usage
|
||||
if ($node instanceof Twig_Node_For) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($node as $n) {
|
||||
if (!$n) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->checkLoopUsageBody($stream, $n);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag name associated with this token parser.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
--TEST--
|
||||
"for" tag
|
||||
--TEMPLATE--
|
||||
{% for i, item in items if i > 0 %}
|
||||
{{ loop.last }}
|
||||
{% endfor %}
|
||||
--DATA--
|
||||
return array('items' => array('a', 'b'))
|
||||
--EXCEPTION--
|
||||
Twig_Error_Syntax: The "loop.last" variable is not defined when looping with a condition in "index.twig" at line 3
|
||||
@@ -0,0 +1,9 @@
|
||||
--TEST--
|
||||
"for" tag
|
||||
--TEMPLATE--
|
||||
{% for i, item in items if loop.last > 0 %}
|
||||
{% endfor %}
|
||||
--DATA--
|
||||
return array('items' => array('a', 'b'))
|
||||
--EXCEPTION--
|
||||
Twig_Error_Syntax: The "loop" variable cannot be used in a looping condition in "index.twig" at line 2
|
||||
Reference in New Issue
Block a user