For.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. <?php
  2. /*
  3. * This file is part of Twig.
  4. *
  5. * (c) 2009 Fabien Potencier
  6. * (c) 2009 Armin Ronacher
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. /**
  12. * Loops over each item of a sequence.
  13. *
  14. * <pre>
  15. * <ul>
  16. * {% for user in users %}
  17. * <li>{{ user.username|e }}</li>
  18. * {% endfor %}
  19. * </ul>
  20. * </pre>
  21. */
  22. class Twig_TokenParser_For extends Twig_TokenParser
  23. {
  24. public function parse(Twig_Token $token)
  25. {
  26. $lineno = $token->getLine();
  27. $stream = $this->parser->getStream();
  28. $targets = $this->parser->getExpressionParser()->parseAssignmentExpression();
  29. $stream->expect(Twig_Token::OPERATOR_TYPE, 'in');
  30. $seq = $this->parser->getExpressionParser()->parseExpression();
  31. $ifexpr = null;
  32. if ($stream->nextIf(Twig_Token::NAME_TYPE, 'if')) {
  33. $ifexpr = $this->parser->getExpressionParser()->parseExpression();
  34. }
  35. $stream->expect(Twig_Token::BLOCK_END_TYPE);
  36. $body = $this->parser->subparse(array($this, 'decideForFork'));
  37. if ($stream->next()->getValue() == 'else') {
  38. $stream->expect(Twig_Token::BLOCK_END_TYPE);
  39. $else = $this->parser->subparse(array($this, 'decideForEnd'), true);
  40. } else {
  41. $else = null;
  42. }
  43. $stream->expect(Twig_Token::BLOCK_END_TYPE);
  44. if (count($targets) > 1) {
  45. $keyTarget = $targets->getNode(0);
  46. $keyTarget = new Twig_Node_Expression_AssignName($keyTarget->getAttribute('name'), $keyTarget->getLine());
  47. $valueTarget = $targets->getNode(1);
  48. $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine());
  49. } else {
  50. $keyTarget = new Twig_Node_Expression_AssignName('_key', $lineno);
  51. $valueTarget = $targets->getNode(0);
  52. $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine());
  53. }
  54. if ($ifexpr) {
  55. $this->checkLoopUsageCondition($stream, $ifexpr);
  56. $this->checkLoopUsageBody($stream, $body);
  57. }
  58. return new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, $lineno, $this->getTag());
  59. }
  60. public function decideForFork(Twig_Token $token)
  61. {
  62. return $token->test(array('else', 'endfor'));
  63. }
  64. public function decideForEnd(Twig_Token $token)
  65. {
  66. return $token->test('endfor');
  67. }
  68. // the loop variable cannot be used in the condition
  69. protected function checkLoopUsageCondition(Twig_TokenStream $stream, Twig_NodeInterface $node)
  70. {
  71. if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) {
  72. throw new Twig_Error_Syntax('The "loop" variable cannot be used in a looping condition.', $node->getLine(), $stream->getFilename());
  73. }
  74. foreach ($node as $n) {
  75. if (!$n) {
  76. continue;
  77. }
  78. $this->checkLoopUsageCondition($stream, $n);
  79. }
  80. }
  81. // check usage of non-defined loop-items
  82. // it does not catch all problems (for instance when a for is included into another or when the variable is used in an include)
  83. protected function checkLoopUsageBody(Twig_TokenStream $stream, Twig_NodeInterface $node)
  84. {
  85. if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) {
  86. $attribute = $node->getNode('attribute');
  87. if ($attribute instanceof Twig_Node_Expression_Constant && in_array($attribute->getAttribute('value'), array('length', 'revindex0', 'revindex', 'last'))) {
  88. 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());
  89. }
  90. }
  91. // should check for parent.loop.XXX usage
  92. if ($node instanceof Twig_Node_For) {
  93. return;
  94. }
  95. foreach ($node as $n) {
  96. if (!$n) {
  97. continue;
  98. }
  99. $this->checkLoopUsageBody($stream, $n);
  100. }
  101. }
  102. public function getTag()
  103. {
  104. return 'for';
  105. }
  106. }