2011-10-05 06:22:53 +02:00
< ? php
/*
* This file is part of Twig .
*
* ( c ) 2009 Fabien Potencier
* ( c ) 2009 Armin Ronacher
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
/**
* Loops over each item of a sequence .
*
* < pre >
* < ul >
* { % for user in users % }
* < li > {{ user . username | e }} </ li >
* { % endfor % }
* </ ul >
* </ pre >
*/
class Twig_TokenParser_For extends Twig_TokenParser
{
/**
* Parses a token and returns a node .
*
* @ param Twig_Token $token A Twig_Token instance
*
* @ return Twig_NodeInterface A Twig_NodeInterface instance
*/
public function parse ( Twig_Token $token )
{
$lineno = $token -> getLine ();
2013-08-01 21:20:12 +02:00
$stream = $this -> parser -> getStream ();
2011-10-05 06:22:53 +02:00
$targets = $this -> parser -> getExpressionParser () -> parseAssignmentExpression ();
2013-08-01 21:20:12 +02:00
$stream -> expect ( Twig_Token :: OPERATOR_TYPE , 'in' );
2011-10-05 06:22:53 +02:00
$seq = $this -> parser -> getExpressionParser () -> parseExpression ();
$ifexpr = null ;
2015-03-11 00:16:45 +01:00
if ( $stream -> nextIf ( Twig_Token :: NAME_TYPE , 'if' )) {
2011-10-05 06:22:53 +02:00
$ifexpr = $this -> parser -> getExpressionParser () -> parseExpression ();
}
2013-08-01 21:20:12 +02:00
$stream -> expect ( Twig_Token :: BLOCK_END_TYPE );
2011-10-05 06:22:53 +02:00
$body = $this -> parser -> subparse ( array ( $this , 'decideForFork' ));
2013-08-01 21:20:12 +02:00
if ( $stream -> next () -> getValue () == 'else' ) {
$stream -> expect ( Twig_Token :: BLOCK_END_TYPE );
2011-10-05 06:22:53 +02:00
$else = $this -> parser -> subparse ( array ( $this , 'decideForEnd' ), true );
} else {
$else = null ;
}
2013-08-01 21:20:12 +02:00
$stream -> expect ( Twig_Token :: BLOCK_END_TYPE );
2011-10-05 06:22:53 +02:00
if ( count ( $targets ) > 1 ) {
$keyTarget = $targets -> getNode ( 0 );
2013-08-01 21:20:12 +02:00
$keyTarget = new Twig_Node_Expression_AssignName ( $keyTarget -> getAttribute ( 'name' ), $keyTarget -> getLine ());
2011-10-05 06:22:53 +02:00
$valueTarget = $targets -> getNode ( 1 );
2013-08-01 21:20:12 +02:00
$valueTarget = new Twig_Node_Expression_AssignName ( $valueTarget -> getAttribute ( 'name' ), $valueTarget -> getLine ());
2011-10-05 06:22:53 +02:00
} else {
$keyTarget = new Twig_Node_Expression_AssignName ( '_key' , $lineno );
$valueTarget = $targets -> getNode ( 0 );
2013-08-01 21:20:12 +02:00
$valueTarget = new Twig_Node_Expression_AssignName ( $valueTarget -> getAttribute ( 'name' ), $valueTarget -> getLine ());
}
if ( $ifexpr ) {
$this -> checkLoopUsageCondition ( $stream , $ifexpr );
$this -> checkLoopUsageBody ( $stream , $body );
2011-10-05 06:22:53 +02:00
}
return new Twig_Node_For ( $keyTarget , $valueTarget , $seq , $ifexpr , $body , $else , $lineno , $this -> getTag ());
}
public function decideForFork ( Twig_Token $token )
{
return $token -> test ( array ( 'else' , 'endfor' ));
}
public function decideForEnd ( Twig_Token $token )
{
return $token -> test ( 'endfor' );
}
2013-08-01 21:20:12 +02:00
// 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 && $node -> getNode ( 'node' ) instanceof Twig_Node_Expression_Name && '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 && $node -> getNode ( 'node' ) instanceof Twig_Node_Expression_Name && '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 );
}
}
2011-10-05 06:22:53 +02:00
/**
* Gets the tag name associated with this token parser .
*
2013-08-01 21:20:12 +02:00
* @ return string The tag name
2011-10-05 06:22:53 +02:00
*/
public function getTag ()
{
return 'for' ;
}
}