mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-14 19:36:43 +00:00
added support for named arguments for filters, tests, and functions
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
* 1.12.0 (2012-XX-XX)
|
||||
|
||||
* added support for named arguments for filters, tests, and functions
|
||||
* moved filters/functions/tests syntax errors to the parser
|
||||
* added support for extended ternary operator syntaxes
|
||||
|
||||
|
||||
@@ -18,5 +18,11 @@ is the input charset:
|
||||
them must be installed. In case both are installed, `mbstring`_ is used by
|
||||
default (Twig before 1.8.1 uses `iconv`_ by default).
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``from``: The input charset
|
||||
* ``to``: The output charset
|
||||
|
||||
.. _`iconv`: http://php.net/iconv
|
||||
.. _`mbstring`: http://php.net/mbstring
|
||||
|
||||
@@ -77,6 +77,12 @@ The default timezone can also be set globally by calling ``setTimezone()``:
|
||||
$twig = new Twig_Environment($loader);
|
||||
$twig->getExtension('core')->setTimezone('Europe/Paris');
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``format``: The date format
|
||||
* ``timezone``: The date timezone
|
||||
|
||||
.. _`strtotime`: http://www.php.net/strtotime
|
||||
.. _`DateTime`: http://www.php.net/DateTime
|
||||
.. _`DateInterval`: http://www.php.net/DateInterval
|
||||
|
||||
@@ -14,5 +14,10 @@ The ``date_modify`` filter accepts strings (it must be in a format supported
|
||||
by the `strtotime`_ function) or `DateTime`_ instances. You can easily combine
|
||||
it with the :doc:`date<date>` filter for formatting.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``modifier``: The modifier
|
||||
|
||||
.. _`strtotime`: http://www.php.net/strtotime
|
||||
.. _`DateTime`: http://www.php.net/DateTime
|
||||
|
||||
@@ -26,3 +26,8 @@ undefined:
|
||||
|
||||
Read the documentation for the :doc:`defined<../tests/defined>` and
|
||||
:doc:`empty<../tests/empty>` tests to learn more about their semantics.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``default``: The default value
|
||||
|
||||
@@ -84,4 +84,10 @@ The ``escape`` filter supports the following escaping strategies:
|
||||
{{ var|escape(strategy)|raw }} {# won't be double-escaped #}
|
||||
{% endautoescape %}
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``strategy``: The escaping strategy
|
||||
* ``charset``: The string charset
|
||||
|
||||
.. _`htmlspecialchars`: http://php.net/htmlspecialchars
|
||||
|
||||
@@ -16,3 +16,8 @@ define it with the optional first parameter:
|
||||
|
||||
{{ [1, 2, 3]|join('|') }}
|
||||
{# returns 1|2|3 #}
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``glue``: The separator
|
||||
|
||||
@@ -11,4 +11,9 @@ The ``json_encode`` filter returns the JSON representation of a string:
|
||||
|
||||
Internally, Twig uses the PHP `json_encode`_ function.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``options``: The options
|
||||
|
||||
.. _`json_encode`: http://php.net/json_encode
|
||||
|
||||
@@ -35,4 +35,11 @@ These defaults can be easily changed through the core extension:
|
||||
The defaults set for ``number_format`` can be over-ridden upon each call using the
|
||||
additional parameters.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``decimal``: The number of decimal points to display
|
||||
* ``decimal_point``: The character(s) to use for the decimal point
|
||||
* ``decimal_sep``: The character(s) to use for the thousands separator
|
||||
|
||||
.. _`number_format`: http://php.net/number_format
|
||||
|
||||
@@ -11,4 +11,9 @@ The ``replace`` filter formats a given string by replacing the placeholders
|
||||
{# returns I like foo and bar
|
||||
if the foo parameter equals to the foo string. #}
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``replace_pairs``: The placeholder values
|
||||
|
||||
.. seealso:: :doc:`format<format>`
|
||||
|
||||
@@ -52,6 +52,13 @@ up until the end of the variable.
|
||||
|
||||
It also works with objects implementing the `Traversable`_ interface.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``start``: The start of the slice
|
||||
* ``length``: The size of the slice
|
||||
* ``preserve_keys``: Whether to preserve key or not (when the input is an array)
|
||||
|
||||
.. _`Traversable`: http://php.net/manual/en/class.traversable.php
|
||||
.. _`array_slice`: http://php.net/array_slice
|
||||
.. _`substr`: http://php.net/substr
|
||||
|
||||
@@ -43,5 +43,11 @@ chunks. Length is set by the ``limit`` argument (one character by default).
|
||||
Internally, Twig uses the PHP `explode`_ or `str_split`_ (if delimiter is
|
||||
empty) functions for string splitting.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``delimiter``: The delimiter
|
||||
* ``limit``: The limit argument
|
||||
|
||||
.. _`explode`: http://php.net/explode
|
||||
.. _`str_split`: http://php.net/str_split
|
||||
|
||||
@@ -21,4 +21,9 @@ and end of a string:
|
||||
|
||||
Internally, Twig uses the PHP `trim`_ function.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``character_mask``: The characters to strip
|
||||
|
||||
.. _`trim`: http://php.net/trim
|
||||
|
||||
@@ -18,3 +18,8 @@ The array can contain any number of values:
|
||||
{% for i in 0..10 %}
|
||||
{{ cycle(fruits, i) }}
|
||||
{% endfor %}
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``position``: The cycle position
|
||||
|
||||
@@ -43,4 +43,10 @@ If no argument is passed, the function returns the current date:
|
||||
$twig = new Twig_Environment($loader);
|
||||
$twig->getExtension('core')->setTimezone('Europe/Paris');
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``date``: The date
|
||||
* ``timezone``: The timezone
|
||||
|
||||
.. _`date`: http://www.php.net/date
|
||||
|
||||
@@ -60,5 +60,10 @@ dumped:
|
||||
|
||||
Internally, Twig uses the PHP `var_dump`_ function.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``context``: The context to dump
|
||||
|
||||
.. _`XDebug`: http://xdebug.org/docs/display
|
||||
.. _`var_dump`: http://php.net/var_dump
|
||||
|
||||
@@ -21,4 +21,9 @@ parameter type:
|
||||
{{ random() }} {# example output: 15386094 (works as native PHP `mt_rand`_ function) #}
|
||||
{{ random(5) }} {# example output: 3 #}
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``values``: The values
|
||||
|
||||
.. _`mt_rand`: http://php.net/mt_rand
|
||||
|
||||
@@ -35,4 +35,11 @@ function (with a step of 1):
|
||||
|
||||
The ``range`` function works as the native PHP `range`_ function.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``low``: The first value of the sequence.
|
||||
* ``high``: The highest possible value of the sequence.
|
||||
* ``step``: The increment between elements of the sequence.
|
||||
|
||||
.. _`range`: http://php.net/range
|
||||
|
||||
@@ -25,3 +25,8 @@ The ``template_from_string`` function loads a template from a string:
|
||||
Even if you will probably always use the ``template_from_string`` function
|
||||
with the ``include`` tag, you can use it with any tag or function that
|
||||
takes a template as an argument (like the ``embed`` or ``extends`` tags).
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``template``: The template
|
||||
|
||||
@@ -190,6 +190,56 @@ progression of integers:
|
||||
Go to the :doc:`functions<functions/index>` page to learn more about the
|
||||
built-in functions.
|
||||
|
||||
Named Arguments
|
||||
---------------
|
||||
|
||||
.. versionadded:: 1.12
|
||||
Support for named arguments was added in Twig 1.12.
|
||||
|
||||
Arguments for filters and functions can also be passed as *named arguments*:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% for i in range(low=1, high=10, step=2) %}
|
||||
{{ i }},
|
||||
{% endfor %}
|
||||
|
||||
Using named arguments makes your templates more explicit about the meaning of
|
||||
the values you pass as arguments:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{{ data|convert_encoding('UTF-8', 'iso-2022-jp') }}
|
||||
|
||||
{# versus #}
|
||||
|
||||
{{ data|convert_encoding(from='iso-2022-jp', to='UTF-8') }}
|
||||
|
||||
Named arguments also allow you to skip some arguments for which you don't want
|
||||
to change the default value::
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{# the first argument is the date format, which defaults to the global date format if null is passed #}
|
||||
{{ "now"|date(null, "Europe/Paris") }}
|
||||
|
||||
{# or skip the format value by using a named argument for the timezone #}
|
||||
{{ "now"|date(timezone="Europe/Paris") }}
|
||||
|
||||
You can also use both positional and named arguments in one call, which is not
|
||||
recommended as it can be confusing:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{# both work #}
|
||||
{{ "now"|date('d/m/Y H:i', timezone="Europe/Paris") }}
|
||||
{{ "now"|date(timezone="Europe/Paris", 'd/m/Y H:i') }}
|
||||
|
||||
.. tip::
|
||||
|
||||
Each function and filter documentation page has a section where the names
|
||||
of all arguments are listed when supported.
|
||||
|
||||
Control Structure
|
||||
-----------------
|
||||
|
||||
|
||||
@@ -297,9 +297,9 @@ class Twig_ExpressionParser
|
||||
|
||||
public function getFunctionNode($name, $line)
|
||||
{
|
||||
$args = $this->parseArguments();
|
||||
switch ($name) {
|
||||
case 'parent':
|
||||
$args = $this->parseArguments();
|
||||
if (!count($this->parser->getBlockStack())) {
|
||||
throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden', $line, $this->parser->getFilename());
|
||||
}
|
||||
@@ -310,8 +310,9 @@ class Twig_ExpressionParser
|
||||
|
||||
return new Twig_Node_Expression_Parent($this->parser->peekBlockStack(), $line);
|
||||
case 'block':
|
||||
return new Twig_Node_Expression_BlockReference($args->getNode(0), false, $line);
|
||||
return new Twig_Node_Expression_BlockReference($this->parseArguments()->getNode(0), false, $line);
|
||||
case 'attribute':
|
||||
$args = $this->parseArguments();
|
||||
if (count($args) < 2) {
|
||||
throw new Twig_Error_Syntax('The "attribute" function takes at least two arguments (the variable and the attributes)', $line, $this->parser->getFilename());
|
||||
}
|
||||
@@ -320,7 +321,7 @@ class Twig_ExpressionParser
|
||||
default:
|
||||
if (null !== $alias = $this->parser->getImportedSymbol('function', $name)) {
|
||||
$arguments = new Twig_Node_Expression_Array(array(), $line);
|
||||
foreach ($args as $n) {
|
||||
foreach ($this->parseArguments() as $n) {
|
||||
$arguments->addElement($n);
|
||||
}
|
||||
|
||||
@@ -330,6 +331,7 @@ class Twig_ExpressionParser
|
||||
return $node;
|
||||
}
|
||||
|
||||
$args = $this->parseArguments(true);
|
||||
$class = $this->getFunctionNodeClass($name, $line);
|
||||
|
||||
return new $class($name, $args, $line);
|
||||
@@ -416,7 +418,7 @@ class Twig_ExpressionParser
|
||||
if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
|
||||
$arguments = new Twig_Node();
|
||||
} else {
|
||||
$arguments = $this->parseArguments();
|
||||
$arguments = $this->parseArguments(true);
|
||||
}
|
||||
|
||||
$class = $this->getFilterNodeClass($name->getAttribute('value'), $token->getLine());
|
||||
@@ -433,17 +435,40 @@ class Twig_ExpressionParser
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function parseArguments()
|
||||
/**
|
||||
* Parses arguments.
|
||||
*
|
||||
* @param Boolean $namedArguments Whether to allow named arguments or not
|
||||
* @param Boolean $definition Whether we are parsing arguments for a function definition
|
||||
*/
|
||||
public function parseArguments($namedArguments = false, $definition = false)
|
||||
{
|
||||
$args = array();
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, '(', 'A list of arguments must be opened by a parenthesis');
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, '(', 'A list of arguments must begin with an opening parenthesis');
|
||||
while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ')')) {
|
||||
if (!empty($args)) {
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma');
|
||||
}
|
||||
$args[] = $this->parseExpression();
|
||||
|
||||
$value = $this->parseExpression();
|
||||
|
||||
$name = null;
|
||||
if ($namedArguments && $stream->test(Twig_Token::OPERATOR_TYPE, '=')) {
|
||||
$token = $stream->next();
|
||||
if (!$value instanceof Twig_Node_Expression_Name) {
|
||||
throw new Twig_Error_Syntax(sprintf('A parameter name must be a string, "%s" given', get_class($value)), $token->getLine());
|
||||
}
|
||||
$name = $value->getAttribute('name');
|
||||
$value = $definition ? $this->parsePrimaryExpression() : $this->parseExpression();
|
||||
}
|
||||
|
||||
if (null === $name) {
|
||||
$args[] = $value;
|
||||
} else {
|
||||
$args[$name] = $value;
|
||||
}
|
||||
}
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis');
|
||||
|
||||
|
||||
@@ -267,7 +267,7 @@ class Twig_Extension_Core extends Twig_Extension
|
||||
$name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
|
||||
$arguments = null;
|
||||
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
|
||||
$arguments = $parser->getExpressionParser()->parseArguments();
|
||||
$arguments = $parser->getExpressionParser()->parseArguments(true);
|
||||
}
|
||||
|
||||
$class = $this->getTestNodeClass($parser, $name, $node->getLine());
|
||||
@@ -305,18 +305,18 @@ class Twig_Extension_Core extends Twig_Extension
|
||||
/**
|
||||
* Cycles over a value.
|
||||
*
|
||||
* @param ArrayAccess|array $values An array or an ArrayAccess instance
|
||||
* @param integer $i The cycle value
|
||||
* @param ArrayAccess|array $values An array or an ArrayAccess instance
|
||||
* @param integer $position The cycle position
|
||||
*
|
||||
* @return string The next value in the cycle
|
||||
*/
|
||||
function twig_cycle($values, $i)
|
||||
function twig_cycle($values, $position)
|
||||
{
|
||||
if (!is_array($values) && !$values instanceof ArrayAccess) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
return $values[$i % count($values)];
|
||||
return $values[$position % count($values)];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -410,7 +410,7 @@ function twig_date_format_filter(Twig_Environment $env, $date, $format = null, $
|
||||
* Returns a new date object modified
|
||||
*
|
||||
* <pre>
|
||||
* {{ post.published_at|modify("-1day")|date("m/d/Y") }}
|
||||
* {{ post.published_at|date_modify("-1day")|date("m/d/Y") }}
|
||||
* </pre>
|
||||
*
|
||||
* @param Twig_Environment $env A Twig_Environment instance
|
||||
|
||||
+7
-1
@@ -15,7 +15,7 @@
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
abstract class Twig_Filter implements Twig_FilterInterface
|
||||
abstract class Twig_Filter implements Twig_FilterInterface, Twig_FilterCallableInterface
|
||||
{
|
||||
protected $options;
|
||||
protected $arguments = array();
|
||||
@@ -27,6 +27,7 @@ abstract class Twig_Filter implements Twig_FilterInterface
|
||||
'needs_context' => false,
|
||||
'pre_escape' => null,
|
||||
'preserves_safety' => null,
|
||||
'callable' => null,
|
||||
), $options);
|
||||
}
|
||||
|
||||
@@ -72,4 +73,9 @@ abstract class Twig_Filter implements Twig_FilterInterface
|
||||
{
|
||||
return $this->options['pre_escape'];
|
||||
}
|
||||
|
||||
public function getCallable()
|
||||
{
|
||||
return $this->options['callable'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ class Twig_Filter_Function extends Twig_Filter
|
||||
|
||||
public function __construct($function, array $options = array())
|
||||
{
|
||||
$options['callable'] = $function;
|
||||
|
||||
parent::__construct($options);
|
||||
|
||||
$this->function = $function;
|
||||
|
||||
@@ -22,6 +22,8 @@ class Twig_Filter_Method extends Twig_Filter
|
||||
|
||||
public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array())
|
||||
{
|
||||
$options['callable'] = array($extension, $method);
|
||||
|
||||
parent::__construct($options);
|
||||
|
||||
$this->extension = $extension;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2012 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a callable template filter.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface Twig_FilterCallableInterface
|
||||
{
|
||||
public function getCallable();
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
abstract class Twig_Function implements Twig_FunctionInterface
|
||||
abstract class Twig_Function implements Twig_FunctionInterface, Twig_FunctionCallableInterface
|
||||
{
|
||||
protected $options;
|
||||
protected $arguments = array();
|
||||
@@ -25,6 +25,7 @@ abstract class Twig_Function implements Twig_FunctionInterface
|
||||
$this->options = array_merge(array(
|
||||
'needs_environment' => false,
|
||||
'needs_context' => false,
|
||||
'callable' => null,
|
||||
), $options);
|
||||
}
|
||||
|
||||
@@ -60,4 +61,9 @@ abstract class Twig_Function implements Twig_FunctionInterface
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getCallable()
|
||||
{
|
||||
return $this->options['callable'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ class Twig_Function_Function extends Twig_Function
|
||||
|
||||
public function __construct($function, array $options = array())
|
||||
{
|
||||
$options['callable'] = $function;
|
||||
|
||||
parent::__construct($options);
|
||||
|
||||
$this->function = $function;
|
||||
|
||||
@@ -23,6 +23,8 @@ class Twig_Function_Method extends Twig_Function
|
||||
|
||||
public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array())
|
||||
{
|
||||
$options['callable'] = array($extension, $method);
|
||||
|
||||
parent::__construct($options);
|
||||
|
||||
$this->extension = $extension;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2012 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a callable template function.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface Twig_FunctionCallableInterface
|
||||
{
|
||||
public function getCallable();
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
*/
|
||||
abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
|
||||
{
|
||||
public function compileArguments(Twig_Compiler $compiler)
|
||||
protected function compileArguments(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler->raw('(');
|
||||
|
||||
@@ -48,7 +48,11 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
|
||||
}
|
||||
|
||||
if ($this->hasNode('arguments') && null !== $this->getNode('arguments')) {
|
||||
foreach ($this->getNode('arguments') as $node) {
|
||||
$callable = $this->hasAttribute('callable') ? $this->getAttribute('callable') : null;
|
||||
|
||||
$arguments = $this->getArguments($callable, $this->getNode('arguments'));
|
||||
|
||||
foreach ($arguments as $node) {
|
||||
if (!$first) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
@@ -59,4 +63,83 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
|
||||
|
||||
$compiler->raw(')');
|
||||
}
|
||||
|
||||
protected function getArguments($callable, $arguments)
|
||||
{
|
||||
$parameters = array();
|
||||
$named = false;
|
||||
foreach ($arguments as $name => $node) {
|
||||
if (!is_int($name)) {
|
||||
$named = true;
|
||||
$name = $this->normalizeName($name);
|
||||
}
|
||||
$parameters[$name] = $node;
|
||||
}
|
||||
|
||||
if (!$named) {
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
if (!$callable) {
|
||||
throw new LogicException(sprintf('Named arguments are not supported for %s "%s".', $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
}
|
||||
|
||||
// manage named arguments
|
||||
if (is_array($callable)) {
|
||||
$r = new \ReflectionMethod($callable[0], $callable[1]);
|
||||
} elseif (is_object($callable) && !$callable instanceof \Closure) {
|
||||
$r = new \ReflectionObject($callable);
|
||||
$r = $r->getMethod('__invoke');
|
||||
} else {
|
||||
$r = new \ReflectionFunction($callable);
|
||||
}
|
||||
|
||||
$definition = $r->getParameters();
|
||||
if ($this->hasNode('node')) {
|
||||
array_shift($definition);
|
||||
}
|
||||
if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
|
||||
array_shift($definition);
|
||||
}
|
||||
if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
|
||||
array_shift($definition);
|
||||
}
|
||||
if ($this->hasAttribute('arguments') && null !== $this->getAttribute('arguments')) {
|
||||
foreach ($this->getAttribute('arguments') as $argument) {
|
||||
array_shift($definition);
|
||||
}
|
||||
}
|
||||
|
||||
$arguments = array();
|
||||
$pos = 0;
|
||||
foreach ($definition as $param) {
|
||||
$name = $this->normalizeName($param->name);
|
||||
|
||||
if (array_key_exists($name, $parameters)) {
|
||||
$arguments[] = $parameters[$name];
|
||||
unset($parameters[$name]);
|
||||
} elseif (array_key_exists($pos, $parameters)) {
|
||||
$arguments[] = $parameters[$pos];
|
||||
unset($parameters[$pos]);
|
||||
++$pos;
|
||||
} elseif ($param->isDefaultValueAvailable()) {
|
||||
$arguments[] = new Twig_Node_Expression_Constant($param->getDefaultValue(), -1);
|
||||
} elseif ($param->isOptional()) {
|
||||
break;
|
||||
} else {
|
||||
throw new Twig_Error_Syntax(sprintf('Value for argument "%s" is required for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (array_keys($parameters) as $name) {
|
||||
throw new Twig_Error_Syntax(sprintf('Unknown argument "%s" for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
protected function normalizeName($name)
|
||||
{
|
||||
return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), $name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,19 @@ class Twig_Node_Expression_Filter extends Twig_Node_Expression_Call
|
||||
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$filter = $compiler->getEnvironment()->getFilter($this->getNode('filter')->getAttribute('value'));
|
||||
$name = $this->getNode('filter')->getAttribute('value');
|
||||
$filter = $compiler->getEnvironment()->getFilter($name);
|
||||
|
||||
$compiler->raw($filter->compile());
|
||||
|
||||
$this->setAttribute('name', $name);
|
||||
$this->setAttribute('type', 'filter');
|
||||
$this->setAttribute('needs_environment', $filter->needsEnvironment());
|
||||
$this->setAttribute('needs_context', $filter->needsContext());
|
||||
$this->setAttribute('arguments', $filter->getArguments());
|
||||
if ($filter instanceof Twig_FilterCallableInterface) {
|
||||
$this->setAttribute('callable', $filter->getCallable());
|
||||
}
|
||||
|
||||
$this->compileArguments($compiler);
|
||||
}
|
||||
|
||||
@@ -17,13 +17,19 @@ class Twig_Node_Expression_Function extends Twig_Node_Expression_Call
|
||||
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$function = $compiler->getEnvironment()->getFunction($this->getAttribute('name'));
|
||||
$name = $this->getAttribute('name');
|
||||
$function = $compiler->getEnvironment()->getFunction($name);
|
||||
|
||||
$compiler->raw($function->compile());
|
||||
|
||||
$this->setAttribute('name', $name);
|
||||
$this->setAttribute('type', 'function');
|
||||
$this->setAttribute('needs_environment', $function->needsEnvironment());
|
||||
$this->setAttribute('needs_context', $function->needsContext());
|
||||
$this->setAttribute('arguments', $function->getArguments());
|
||||
if ($function instanceof Twig_FunctionCallableInterface) {
|
||||
$this->setAttribute('callable', $function->getCallable());
|
||||
}
|
||||
|
||||
$this->compileArguments($compiler);
|
||||
}
|
||||
|
||||
@@ -19,8 +19,15 @@ class Twig_Node_Expression_Test extends Twig_Node_Expression_Call
|
||||
{
|
||||
$name = $this->getAttribute('name');
|
||||
$testMap = $compiler->getEnvironment()->getTests();
|
||||
$test = $testMap[$name];
|
||||
|
||||
$compiler->raw($testMap[$name]->compile());
|
||||
$this->setAttribute('name', $name);
|
||||
$this->setAttribute('type', 'test');
|
||||
if ($test instanceof Twig_TestCallableInterface) {
|
||||
$this->setAttribute('callable', $test->getCallable());
|
||||
}
|
||||
|
||||
$compiler->raw($test->compile());
|
||||
|
||||
$this->compileArguments($compiler);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2012 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a template test.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
abstract class Twig_Test implements Twig_TestInterface, Twig_TestCallableInterface
|
||||
{
|
||||
protected $options;
|
||||
protected $arguments = array();
|
||||
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
$this->options = array_merge(array(
|
||||
'callable' => null,
|
||||
), $options);
|
||||
}
|
||||
|
||||
public function getCallable()
|
||||
{
|
||||
return $this->options['callable'];
|
||||
}
|
||||
}
|
||||
@@ -15,12 +15,16 @@
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Test_Function implements Twig_TestInterface
|
||||
class Twig_Test_Function extends Twig_Test
|
||||
{
|
||||
protected $function;
|
||||
|
||||
public function __construct($function)
|
||||
public function __construct($function, array $options = array())
|
||||
{
|
||||
$options['callable'] = $function;
|
||||
|
||||
parent::__construct($options);
|
||||
|
||||
$this->function = $function;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,13 +15,17 @@
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Test_Method implements Twig_TestInterface
|
||||
class Twig_Test_Method extends Twig_Test
|
||||
{
|
||||
protected $extension;
|
||||
protected $method;
|
||||
|
||||
public function __construct(Twig_ExtensionInterface $extension, $method)
|
||||
public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array())
|
||||
{
|
||||
$options['callable'] = array($extension, $method);
|
||||
|
||||
parent::__construct($options);
|
||||
|
||||
$this->extension = $extension;
|
||||
$this->method = $method;
|
||||
}
|
||||
|
||||
@@ -15,12 +15,14 @@
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Test_Node implements Twig_TestInterface
|
||||
class Twig_Test_Node extends Twig_Test
|
||||
{
|
||||
protected $class;
|
||||
|
||||
public function __construct($class)
|
||||
public function __construct($class, array $options = array())
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
$this->class = $class;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2012 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a callable template test.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface Twig_TestCallableInterface
|
||||
{
|
||||
public function getCallable();
|
||||
}
|
||||
@@ -215,6 +215,28 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Twig_Error_Syntax
|
||||
*/
|
||||
public function testAttributeCallDoesNotSupportNamedArguments()
|
||||
{
|
||||
$env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
|
||||
$parser = new Twig_Parser($env);
|
||||
|
||||
$parser->parse($env->tokenize('{{ foo.bar(name="Foo") }}', 'index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Twig_Error_Syntax
|
||||
*/
|
||||
public function testMacroCallDoesNotSupportNamedArguments()
|
||||
{
|
||||
$env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
|
||||
$parser = new Twig_Parser($env);
|
||||
|
||||
$parser->parse($env->tokenize('{% from _self import foo %}{% macro foo() %}{% endmacro %}{{ foo(name="Foo") }}', 'index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Twig_Error_Syntax
|
||||
* @expectedExceptionMessage The function "cycl" does not exist. Did you mean "cycle" in "index" at line 1
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
--TEST--
|
||||
"date" filter
|
||||
--TEMPLATE--
|
||||
{{ date|date(format='d/m/Y H:i:s P', timezone='America/Chicago') }}
|
||||
{{ date|date(timezone='America/Chicago', format='d/m/Y H:i:s P') }}
|
||||
{{ date|date(timezone='America/Chicago', 'd/m/Y H:i:s P') }}
|
||||
{{ date|date('d/m/Y H:i:s P', timezone='America/Chicago') }}
|
||||
--DATA--
|
||||
date_default_timezone_set('UTC');
|
||||
return array('date' => mktime(13, 45, 0, 10, 4, 2010))
|
||||
--EXPECT--
|
||||
04/10/2010 08:45:00 -05:00
|
||||
04/10/2010 08:45:00 -05:00
|
||||
04/10/2010 08:45:00 -05:00
|
||||
04/10/2010 08:45:00 -05:00
|
||||
@@ -4,9 +4,15 @@
|
||||
{{ [1, 2, 3, 4]|reverse|join('') }}
|
||||
{{ '1234évènement'|reverse }}
|
||||
{{ arr|reverse|join('') }}
|
||||
{{ {'a': 'c', 'b': 'a'}|reverse()|join(',') }}
|
||||
{{ {'a': 'c', 'b': 'a'}|reverse(preserveKeys=true)|join(glue=',') }}
|
||||
{{ {'a': 'c', 'b': 'a'}|reverse(preserve_keys=true)|join(glue=',') }}
|
||||
--DATA--
|
||||
return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
|
||||
--EXPECT--
|
||||
4321
|
||||
tnemenèvé4321
|
||||
4321
|
||||
a,c
|
||||
a,c
|
||||
a,c
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
--TEST--
|
||||
"date" function
|
||||
--TEMPLATE--
|
||||
{{ date(date, "America/New_York")|date('d/m/Y H:i:s P', false) }}
|
||||
{{ date(timezone="America/New_York", date=date)|date('d/m/Y H:i:s P', false) }}
|
||||
--DATA--
|
||||
date_default_timezone_set('UTC');
|
||||
return array('date' => mktime(13, 45, 0, 10, 4, 2010))
|
||||
--EXPECT--
|
||||
04/10/2010 09:45:00 -04:00
|
||||
04/10/2010 09:45:00 -04:00
|
||||
@@ -0,0 +1,8 @@
|
||||
--TEST--
|
||||
"range" function
|
||||
--TEMPLATE--
|
||||
{{ range(low=0+1, high=10+0, step=2)|join(',') }}
|
||||
--DATA--
|
||||
return array()
|
||||
--EXPECT--
|
||||
1,3,5,7,9
|
||||
@@ -49,9 +49,65 @@ class Twig_Tests_Node_Expression_FilterTest extends Twig_Test_NodeTestCase
|
||||
$tests[] = array($node, 'twig_number_format_filter($this->env, strtoupper("foo"), 2, ".", ",")');
|
||||
}
|
||||
|
||||
// named arguments
|
||||
$date = new Twig_Node_Expression_Constant(0, 1);
|
||||
$node = $this->createFilter($date, 'date', array(
|
||||
'timezone' => new Twig_Node_Expression_Constant('America/Chicago', 1),
|
||||
'format' => new Twig_Node_Expression_Constant('d/m/Y H:i:s P', 1),
|
||||
));
|
||||
$tests[] = array($node, 'twig_date_format_filter($this->env, 0, "d/m/Y H:i:s P", "America/Chicago")');
|
||||
|
||||
// skip an optional argument
|
||||
$date = new Twig_Node_Expression_Constant(0, 1);
|
||||
$node = $this->createFilter($date, 'date', array(
|
||||
'timezone' => new Twig_Node_Expression_Constant('America/Chicago', 1),
|
||||
));
|
||||
$tests[] = array($node, 'twig_date_format_filter($this->env, 0, null, "America/Chicago")');
|
||||
|
||||
// underscores vs camelCase for named arguments
|
||||
$string = new Twig_Node_Expression_Constant('abc', 1);
|
||||
$node = $this->createFilter($string, 'reverse', array(
|
||||
'preserve_keys' => new Twig_Node_Expression_Constant(true, 1),
|
||||
));
|
||||
$tests[] = array($node, 'twig_reverse_filter($this->env, "abc", true)');
|
||||
$node = $this->createFilter($string, 'reverse', array(
|
||||
'preserveKeys' => new Twig_Node_Expression_Constant(true, 1),
|
||||
));
|
||||
$tests[] = array($node, 'twig_reverse_filter($this->env, "abc", true)');
|
||||
|
||||
return $tests;
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Twig_Error_Syntax
|
||||
* @expectedExceptionMessage Unknown argument "foobar" for filter "date".
|
||||
*/
|
||||
public function testCompileWithWrongNamedArgumentName()
|
||||
{
|
||||
$date = new Twig_Node_Expression_Constant(0, 1);
|
||||
$node = $this->createFilter($date, 'date', array(
|
||||
'foobar' => new Twig_Node_Expression_Constant('America/Chicago', 1),
|
||||
));
|
||||
|
||||
$compiler = $this->getCompiler();
|
||||
$compiler->compile($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Twig_Error_Syntax
|
||||
* @expectedExceptionMessage Value for argument "from" is required for filter "replace".
|
||||
*/
|
||||
public function testCompileWithMissingNamedArgument()
|
||||
{
|
||||
$value = new Twig_Node_Expression_Constant(0, 1);
|
||||
$node = $this->createFilter($value, 'replace', array(
|
||||
'to' => new Twig_Node_Expression_Constant('foo', 1),
|
||||
));
|
||||
|
||||
$compiler = $this->getCompiler();
|
||||
$compiler->compile($node);
|
||||
}
|
||||
|
||||
protected function createFilter($node, $name, array $arguments = array())
|
||||
{
|
||||
$name = new Twig_Node_Expression_Constant($name, 1);
|
||||
|
||||
@@ -67,6 +67,13 @@ class Twig_Tests_Node_Expression_FunctionTest extends Twig_Test_NodeTestCase
|
||||
$node = $this->createFunction('foobar', array(new Twig_Node_Expression_Constant('bar', 1)));
|
||||
$tests[] = array($node, 'foobar($this->env, $context, "bar")', $environment);
|
||||
|
||||
// named arguments
|
||||
$node = $this->createFunction('date', array(
|
||||
'timezone' => new Twig_Node_Expression_Constant('America/Chicago', 1),
|
||||
'date' => new Twig_Node_Expression_Constant(0, 1),
|
||||
));
|
||||
$tests[] = array($node, 'twig_date_converter($this->env, 0, "America/Chicago")');
|
||||
|
||||
return $tests;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user