* 1.6.0-DEV
+ * added slice notation support for the [] operator (syntactic sugar for the slice operator)
+ * added a slice filter
* added string support for the reverse filter
* fixed the empty test and the length filter for Twig_Markup instances
* added a date function to ease date comparison
--- /dev/null
+``slice``
+===========
+
+.. versionadded:: 1.6
+ The slice filter was added in Twig 1.6.
+
+The ``slice`` filter extracts a slice of a sequence, a mapping, or a string:
+
+.. code-block:: jinja
+
+ {% for i in [1, 2, 3, 4]|slice(1, 2) %}
+ {# will iterate over 2 and 3 #}
+ {% endfor %}
+
+ {{ '1234'|slice(1, 2) }}
+
+ {# outputs 23 #}
+
+You can use any valid expression for both the start and the length:
+
+.. code-block:: jinja
+
+ {% for i in [1, 2, 3, 4]|slice(start, length) %}
+ {# ... #}
+ {% endfor %}
+
+As syntactic sugar, you can also use the ``[]`` notation:
+
+.. code-block:: jinja
+
+ {% for i in [1, 2, 3, 4][start:length] %}
+ {# ... #}
+ {% endfor %}
+
+ {{ '1234'[1:2] }}
+
+The ``slice`` filter works as the `array_slice`_ PHP function for arrays and
+`substr`_ for strings.
+
+If the start is non-negative, the sequence will start at that start in the
+variable. If start is negative, the sequence will start that far from the end
+of the variable.
+
+If length is given and is positive, then the sequence will have up to that
+many elements in it. If the variable is shorter than the length, then only the
+available variable elements will be present. If length is given and is
+negative then the sequence will stop that many elements from the end of the
+variable. If it is omitted, then the sequence will have everything from offset
+up until the end of the variable.
+
+.. note::
+
+ It also works with objects implementing the `Traversable`_ interface.
+
+.. _`Traversable`: http://php.net/manual/en/class.traversable.php
+.. _`array_slice`: http://php.net/array_slice
+.. _`substr`: http://php.net/substr
public function parseSubscriptExpression($node)
{
- $token = $this->parser->getStream()->next();
+ $stream = $this->parser->getStream();
+ $token = $stream->next();
$lineno = $token->getLine();
$arguments = new Twig_Node();
$type = Twig_TemplateInterface::ANY_CALL;
if ($token->getValue() == '.') {
- $token = $this->parser->getStream()->next();
+ $token = $stream->next();
if (
$token->getType() == Twig_Token::NAME_TYPE
||
) {
$arg = new Twig_Node_Expression_Constant($token->getValue(), $lineno);
- if ($this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
+ if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
$type = Twig_TemplateInterface::METHOD_CALL;
$arguments = $this->parseArguments();
} else {
$type = Twig_TemplateInterface::ARRAY_CALL;
$arg = $this->parseExpression();
- $this->parser->getStream()->expect(Twig_Token::PUNCTUATION_TYPE, ']');
+
+ // slice?
+ if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
+ $stream->next();
+
+ $class = $this->getFilterNodeClass('slice');
+ $arguments = new Twig_Node(array($arg, $this->parseExpression()));
+ $filter = new $class($node, new Twig_Node_Expression_Constant('slice', $token->getLine()), $arguments, $token->getLine());
+
+ $stream->expect(Twig_Token::PUNCTUATION_TYPE, ']');
+
+ return $filter;
+ }
+
+ $stream->expect(Twig_Token::PUNCTUATION_TYPE, ']');
}
return new Twig_Node_Expression_GetAttr($node, $arg, $arguments, $type, $lineno);
// string/array filters
'reverse' => new Twig_Filter_Function('twig_reverse_filter', array('needs_environment' => true)),
'length' => new Twig_Filter_Function('twig_length_filter', array('needs_environment' => true)),
+ 'slice' => new Twig_Filter_Function('twig_slice', array('needs_environment' => true)),
// iteration and runtime
'default' => new Twig_Filter_Node('Twig_Node_Expression_Filter_Default'),
}
/**
+ * Slices a variable.
+ *
+ * @param Twig_Environment $env A Twig_Environment instance
+ * @param mixed $item A variable
+ * @param integer $start Start of the slice
+ * @param integer $length Size of the slice
+ *
+ * @return mixed The sliced variable
+ */
+function twig_slice(Twig_Environment $env, $item, $start, $length = null)
+{
+ if ($item instanceof Traversable) {
+ $item = iterator_to_array($item, false);
+ }
+
+ if (is_array($item)) {
+ return array_slice($item, $start, $length);
+ }
+
+ $item = (string) $item;
+
+ if (function_exists('mb_get_info') && null !== $charset = $env->getCharset()) {
+ return mb_substr($item, $start, $length, $charset);
+ }
+
+ return substr($item, $start, $length);
+}
+
+/**
* Joins the values to a string.
*
* The separator between elements is an empty string per default, you can define it with the optional parameter.
--- /dev/null
+--TEST--
+"slice" filter
+--TEMPLATE--
+{{ [1, 2, 3, 4][1:2]|join('')}}
+{{ {a: 1, b: 2, c: 3, d: 4}[1:2]|join('') }}
+{{ [1, 2, 3, 4][start:length]|join('') }}
+{{ [1, 2, 3, 4]|slice(1, 2)|join('') }}
+{{ {a: 1, b: 2, c: 3, d: 4}|slice(1, 2)|join('') }}
+{{ '1234'|slice(1, 2) }}
+{{ '1234'[1:2] }}
+{{ arr|slice(1, 2)|join('') }}
+{{ arr[1:2]|join('') }}
+--DATA--
+return array('start' => 1, 'length' => 2, 'arr' => new ArrayObject(array(1, 2, 3, 4)))
+--EXPECT--
+23
+23
+23
+23
+23
+23
+23
+23
+23