* 0.9.5-DEV
+ * added the .. operator (as a syntactic sugar for the range filter when the step is 1)
* added the in operator (as a syntactic sugar for the in filter)
* added the following filters in the Core extension: in, range
* added support for arrays (same behavior as in PHP, a mix between lists and dictionaries, arrays and hashes)
>A sequence can be either an array or an object implementing the `Iterator`
>interface.
-If you do need to iterate over a sequence of numbers, you can use the `range`
-filter:
+If you do need to iterate over a sequence of numbers, you can use the `..`
+operator:
[twig]
- {% for i in 0|range(10) %}
+ {% for i in 0..10 %}
* {{ i }}
{% endfor %}
It can be also useful with letters:
[twig]
- {% for letter in 'a'|range('z') %}
+ {% for letter in 'a'..'z' %}
+ * {{ letter }}
+ {% endfor %}
+
+The `..` operator can take any expression at both sides:
+
+ [twig]
+ {% for letter in 'a'|upper..'z'|upper %}
* {{ letter }}
{% endfor %}
+If you need a step different from 1, you can use the `range` filter instead:
+
+ [twig]
+ {% for i in 0|range(10, 2) %}
+ * {{ i }}
+ {% endfor %}
+
Inside of a `for` loop block you can access some special variables:
| Variable | Description
`true`. To perform a negative test, the whole expression should be prefixed
with `not` ({{ not 1 in [1, 2, 3] }} would return `false`).
+ * `..`: Creates a sequence based on the operand before and after the operator
+ (see the `for` tag for some usage examples).
+
* `|`: Applies a filter.
* `~`: Converts all operands into strings and concatenates them. `{{ "Hello "
>**TIP**
>The `range` filter works as the native PHP `range` function.
+The `..` operator (see above) is a syntactic sugar for the `range` filter
+(with a step of 1):
+
+ [twig]
+ {% for i in 0|range(10) %}
+ * {{ i }}
+ {% endfor %}
+
+ {# is equivalent to #}
+
+ {% for i in 0..10 %}
+ * {{ i }}
+ {% endfor %}
+
### `default`
The `default` filter returns the passed default value if the value is
{
switch ($this->parser->getCurrentToken()->getValue())
{
+ case '..':
+ $node = $this->parseRangeExpression($node);
+ break;
+
case '.':
case '[':
$node = $this->parseSubscriptExpression($node);
return $node;
}
+ public function parseRangeExpression($node)
+ {
+ $token = $this->parser->getStream()->next();
+ $lineno = $token->getLine();
+
+ $end = $this->parseExpression();
+
+ return new Twig_Node_Expression_Filter($node, array(array('range', array($end))), $lineno);
+ }
+
public function parseSubscriptExpression($node)
{
$token = $this->parser->getStream()->next();
const REGEX_NAME = '/[A-Za-z_][A-Za-z0-9_]*/A';
const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?/A';
const REGEX_STRING = '/(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')/Asm';
- const REGEX_OPERATOR = '/<=? | >=? | [!=]= | [(){}.,%*\/+~|-] | \[ | \] | \? | \:/Ax';
+ const REGEX_OPERATOR = '/<=? | >=? | [!=]= | \.\. | [(){}.,%*\/+~|-] | \[ | \] | \? | \:/Ax';
public function __construct(Twig_Environment $env = null, array $options = array())
{
--- /dev/null
+--TEST--
+Twig supports the .. operator
+--TEMPLATE--
+{% for i in 0..10 %}{{ i }} {% endfor %}
+
+{% for letter in 'a'..'z' %}{{ letter }} {% endfor %}
+
+{% for letter in 'a'|upper..'z'|upper %}{{ letter }} {% endfor %}
+
+{% for i in foo[0]..foo[1] %}{{ i }} {% endfor %}
+--DATA--
+return array('foo' => array(1, 10))
+--EXPECT--
+0 1 2 3 4 5 6 7 8 9 10
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
+1 2 3 4 5 6 7 8 9 10
}
}
-$t = new LimeTest(54);
+$t = new LimeTest(55);
$fixturesDir = realpath(dirname(__FILE__).'/../fixtures/');
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($fixturesDir), RecursiveIteratorIterator::LEAVES_ONLY) as $file)