Added variadic filters, tests, and functions

This commit is contained in:
Martin Hasoň
2014-01-16 13:07:08 +01:00
parent dfddd2e0ef
commit c78656a8fb
13 changed files with 215 additions and 16 deletions
+21
View File
@@ -224,6 +224,23 @@ through your filter::
$filter = new Twig_SimpleFilter('somefilter', 'somefilter', array('pre_escape' => 'html', 'is_safe' => array('html')));
Variadic Filters
~~~~~~~~~~~~~~~~
.. versionadded:: 1.19
Support for variadic filters was added in Twig 1.19.
If you want to pass a variable number of positional or named arguments to the filter,
set the ``is_variadic`` option to ``true``; Twig will pass the array of arbitrary arguments
as the last argument to the filter call that is defined as an array with an empty default value::
$filter = new Twig_SimpleFilter('thumbnail', function ($file, array $options = array()) {
...
}, array('is_variadic' => true));
The named arguments passed to the variadic filter cannot be checked if they are valid or not
as if they are not valid, they will end up in the option array.
Dynamic Filters
~~~~~~~~~~~~~~~
@@ -331,6 +348,10 @@ The ``node`` sub-node will contain an expression of ``my_value``. Node-based
tests also have access to the ``arguments`` node. This node will contain the
various other arguments that have been provided to your test.
If you want to pass a variable number of positional or named arguments to the test,
set the ``is_variadic`` option to ``true``. Tests also support dynamic name feature
as filters and functions.
Tags
----
+16 -1
View File
@@ -93,7 +93,7 @@ access the variable attribute:
don't put the braces around them.
If a variable or attribute does not exist, you will receive a ``null`` value
when the ``strict_variables`` option is set to ``false``; alternatively, if ``strict_variables``
when the ``strict_variables`` option is set to ``false``; alternatively, if ``strict_variables``
is set, Twig will throw an error (see :ref:`environment options<environment_options>`).
.. sidebar:: Implementation
@@ -238,6 +238,21 @@ case positional arguments must always come before named arguments:
Each function and filter documentation page has a section where the names
of all arguments are listed when supported.
Variadic Arguments
------------------
.. versionadded:: 1.19
Support for variadic arguments was added in Twig 1.19.
The variadic filter, function or test can accept any arbitrary positional or named arguments:
.. code-block:: jinja
{{ "path/to/image.png"|thumbnail(size=[32, 32], mode=outbound, quality=90, format="jpg") }}
The named arguments passed to the variadic filter, function or test cannot be checked if they are valid or not
as if they are not valid, they will end up in the option array.
Control Structure
-----------------
+42 -2
View File
@@ -106,12 +106,19 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
$parameters[$name] = $node;
}
if (!$named) {
$isVariadic = $this->hasAttribute('is_variadic') && $this->getAttribute('is_variadic');
if (!$named && !$isVariadic) {
return $parameters;
}
if (!$callable) {
throw new LogicException(sprintf('Named arguments are not supported for %s "%s".', $callType, $callName));
if ($named) {
$message = sprintf('Named arguments are not supported for %s "%s".', $callType, $callName);
} else {
$message = sprintf('Arbitrary positional arguments are not supported for %s "%s".', $callType, $callName);
}
throw new LogicException($message);
}
// manage named arguments
@@ -141,6 +148,22 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
array_shift($definition);
}
}
if ($isVariadic) {
$argument = end($definition);
if ($argument && $argument->isArray() && $argument->isDefaultValueAvailable() && array() === $argument->getDefaultValue()) {
array_pop($definition);
} else {
$callableName = $r->name;
if ($r->getDeclaringClass()) {
$callableName = $r->getDeclaringClass()->name.'::'.$callableName;
}
throw new LogicException(sprintf(
'The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = array()".',
$callableName, $callType, $callName
));
}
}
$arguments = array();
$names = array();
@@ -185,6 +208,23 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
}
}
if ($isVariadic) {
$arbitraryArguments = new Twig_Node_Expression_Array(array(), -1);
foreach ($parameters as $key => $value) {
if (is_int($key)) {
$arbitraryArguments->addElement($value);
} else {
$arbitraryArguments->addElement($value, new Twig_Node_Expression_Constant($key, -1));
}
unset($parameters[$key]);
}
if ($arbitraryArguments->count()) {
$arguments = array_merge($arguments, $optionalArguments);
$arguments[] = $arbitraryArguments;
}
}
if (!empty($parameters)) {
$unknownParameter = null;
foreach ($parameters as $parameter) {
+3
View File
@@ -30,6 +30,9 @@ class Twig_Node_Expression_Filter extends Twig_Node_Expression_Call
if ($filter instanceof Twig_FilterCallableInterface || $filter instanceof Twig_SimpleFilter) {
$this->setAttribute('callable', $filter->getCallable());
}
if ($filter instanceof Twig_SimpleFilter) {
$this->setAttribute('is_variadic', $filter->isVariadic());
}
$this->compileCallable($compiler);
}
+3
View File
@@ -29,6 +29,9 @@ class Twig_Node_Expression_Function extends Twig_Node_Expression_Call
if ($function instanceof Twig_FunctionCallableInterface || $function instanceof Twig_SimpleFunction) {
$this->setAttribute('callable', $function->getCallable());
}
if ($function instanceof Twig_SimpleFunction) {
$this->setAttribute('is_variadic', $function->isVariadic());
}
$this->compileCallable($compiler);
}
+3
View File
@@ -26,6 +26,9 @@ class Twig_Node_Expression_Test extends Twig_Node_Expression_Call
if ($test instanceof Twig_TestCallableInterface || $test instanceof Twig_SimpleTest) {
$this->setAttribute('callable', $test->getCallable());
}
if ($test instanceof Twig_SimpleTest) {
$this->setAttribute('is_variadic', $test->isVariadic());
}
$this->compileCallable($compiler);
}
+12 -6
View File
@@ -27,12 +27,13 @@ class Twig_SimpleFilter
$this->callable = $callable;
$this->options = array_merge(array(
'needs_environment' => false,
'needs_context' => false,
'is_safe' => null,
'is_safe_callback' => null,
'pre_escape' => null,
'preserves_safety' => null,
'node_class' => 'Twig_Node_Expression_Filter',
'needs_context' => false,
'is_variadic' => false,
'is_safe' => null,
'is_safe_callback' => null,
'pre_escape' => null,
'preserves_safety' => null,
'node_class' => 'Twig_Node_Expression_Filter',
), $options);
}
@@ -91,4 +92,9 @@ class Twig_SimpleFilter
{
return $this->options['pre_escape'];
}
public function isVariadic()
{
return $this->options['is_variadic'];
}
}
+10 -4
View File
@@ -27,10 +27,11 @@ class Twig_SimpleFunction
$this->callable = $callable;
$this->options = array_merge(array(
'needs_environment' => false,
'needs_context' => false,
'is_safe' => null,
'is_safe_callback' => null,
'node_class' => 'Twig_Node_Expression_Function',
'needs_context' => false,
'is_variadic' => false,
'is_safe' => null,
'is_safe_callback' => null,
'node_class' => 'Twig_Node_Expression_Function',
), $options);
}
@@ -81,4 +82,9 @@ class Twig_SimpleFunction
return array();
}
public function isVariadic()
{
return $this->options['is_variadic'];
}
}
+6
View File
@@ -25,6 +25,7 @@ class Twig_SimpleTest
$this->name = $name;
$this->callable = $callable;
$this->options = array_merge(array(
'is_variadic' => false,
'node_class' => 'Twig_Node_Expression_Test',
), $options);
}
@@ -43,4 +44,9 @@ class Twig_SimpleTest
{
return $this->options['node_class'];
}
public function isVariadic()
{
return $this->options['is_variadic'];
}
}
+15 -1
View File
@@ -73,7 +73,7 @@ class Twig_Tests_Node_Expression_CallTest extends PHPUnit_Framework_TestCase
public function testResolveArgumentsOnlyNecessaryArgumentsForCustomFunction()
{
$node = new Twig_Tests_Node_Expression_Call(array(), array('type' => 'function', 'name' => 'custom_function'));
$node = new Twig_Tests_Node_Expression_Call(array(), array('type' => 'function', 'name' => 'custom_function'));
$this->assertEquals(array('arg1'), $node->getArguments(array($this, 'customFunction'), array('arg1' => 'arg1')));
}
@@ -84,6 +84,16 @@ class Twig_Tests_Node_Expression_CallTest extends PHPUnit_Framework_TestCase
$this->assertEquals(array('arg1'), $node->getArguments(__CLASS__.'::customStaticFunction', array('arg1' => 'arg1')));
}
/**
* @expectedException LogicException
* @expectedExceptionMessage The last parameter of "Twig_Tests_Node_Expression_CallTest::customFunctionWithArbitraryArguments" for function "foo" must be an array with default value, eg. "array $arg = array()".
*/
public function testResolveArgumentsWithMissingParameterForArbitraryArguments()
{
$node = new Twig_Tests_Node_Expression_Call(array(), array('type' => 'function', 'name' => 'foo', 'is_variadic' => true));
$node->getArguments(array($this, 'customFunctionWithArbitraryArguments'), array());
}
public static function customStaticFunction($arg1, $arg2 = 'default', $arg3 = array())
{
}
@@ -91,6 +101,10 @@ class Twig_Tests_Node_Expression_CallTest extends PHPUnit_Framework_TestCase
public function customFunction($arg1, $arg2 = 'default', $arg3 = array())
{
}
public function customFunctionWithArbitraryArguments()
{
}
}
class Twig_Tests_Node_Expression_Call extends Twig_Node_Expression_Call
+34 -1
View File
@@ -25,6 +25,10 @@ class Twig_Tests_Node_Expression_FilterTest extends Twig_Test_NodeTestCase
public function getTests()
{
$environment = new Twig_Environment();
$environment->addFilter(new Twig_SimpleFilter('bar', 'bar', array('needs_environment' => true)));
$environment->addFilter(new Twig_SimpleFilter('barbar', 'twig_tests_filter_barbar', array('needs_context' => true, 'is_variadic' => true)));
$tests = array();
$expr = new Twig_Node_Expression_Constant('foo', 1);
@@ -41,7 +45,7 @@ class Twig_Tests_Node_Expression_FilterTest extends Twig_Test_NodeTestCase
$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),
'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")');
@@ -69,6 +73,31 @@ class Twig_Tests_Node_Expression_FilterTest extends Twig_Test_NodeTestCase
$tests[] = array($node, 'call_user_func_array($this->env->getFilter(\'anonymous\')->getCallable(), array("foo"))');
}
// needs environment
$node = $this->createFilter($string, 'bar');
$tests[] = array($node, 'bar($this->env, "abc")', $environment);
$node = $this->createFilter($string, 'bar', array(new Twig_Node_Expression_Constant('bar', 1)));
$tests[] = array($node, 'bar($this->env, "abc", "bar")', $environment);
// arbitrary named arguments
$node = $this->createFilter($string, 'barbar');
$tests[] = array($node, 'twig_tests_filter_barbar($context, "abc")', $environment);
$node = $this->createFilter($string, 'barbar', array('foo' => new Twig_Node_Expression_Constant('bar', 1)));
$tests[] = array($node, 'twig_tests_filter_barbar($context, "abc", null, null, array("foo" => "bar"))', $environment);
$node = $this->createFilter($string, 'barbar', array('arg2' => new Twig_Node_Expression_Constant('bar', 1)));
$tests[] = array($node, 'twig_tests_filter_barbar($context, "abc", null, "bar")', $environment);
$node = $this->createFilter($string, 'barbar', array(
new Twig_Node_Expression_Constant('1', 1),
new Twig_Node_Expression_Constant('2', 1),
new Twig_Node_Expression_Constant('3', 1),
'foo' => new Twig_Node_Expression_Constant('bar', 1),
));
$tests[] = array($node, 'twig_tests_filter_barbar($context, "abc", "1", "2", array(0 => "3", "foo" => "bar"))', $environment);
return $tests;
}
@@ -119,3 +148,7 @@ class Twig_Tests_Node_Expression_FilterTest extends Twig_Test_NodeTestCase
return parent::getEnvironment();
}
}
function twig_tests_filter_barbar($context, $string, $arg1 = null, $arg2 = null, array $args = array())
{
}
@@ -28,6 +28,7 @@ class Twig_Tests_Node_Expression_FunctionTest extends Twig_Test_NodeTestCase
$environment->addFunction(new Twig_SimpleFunction('bar', 'bar', array('needs_environment' => true)));
$environment->addFunction(new Twig_SimpleFunction('foofoo', 'foofoo', array('needs_context' => true)));
$environment->addFunction(new Twig_SimpleFunction('foobar', 'foobar', array('needs_environment' => true, 'needs_context' => true)));
$environment->addFunction(new Twig_SimpleFunction('barbar', 'twig_tests_function_barbar', array('is_variadic' => true)));
$tests = array();
@@ -58,10 +59,28 @@ class Twig_Tests_Node_Expression_FunctionTest extends Twig_Test_NodeTestCase
// named arguments
$node = $this->createFunction('date', array(
'timezone' => new Twig_Node_Expression_Constant('America/Chicago', 1),
'date' => new Twig_Node_Expression_Constant(0, 1),
'date' => new Twig_Node_Expression_Constant(0, 1),
));
$tests[] = array($node, 'twig_date_converter($this->env, 0, "America/Chicago")');
// arbitrary named arguments
$node = $this->createFunction('barbar');
$tests[] = array($node, 'twig_tests_function_barbar()', $environment);
$node = $this->createFunction('barbar', array('foo' => new Twig_Node_Expression_Constant('bar', 1)));
$tests[] = array($node, 'twig_tests_function_barbar(null, null, array("foo" => "bar"))', $environment);
$node = $this->createFunction('barbar', array('arg2' => new Twig_Node_Expression_Constant('bar', 1)));
$tests[] = array($node, 'twig_tests_function_barbar(null, "bar")', $environment);
$node = $this->createFunction('barbar', array(
new Twig_Node_Expression_Constant('1', 1),
new Twig_Node_Expression_Constant('2', 1),
new Twig_Node_Expression_Constant('3', 1),
'foo' => new Twig_Node_Expression_Constant('bar', 1),
));
$tests[] = array($node, 'twig_tests_function_barbar("1", "2", array(0 => "3", "foo" => "bar"))', $environment);
// function as an anonymous function
if (PHP_VERSION_ID >= 50300) {
$node = $this->createFunction('anonymous', array(new Twig_Node_Expression_Constant('foo', 1)));
@@ -85,3 +104,7 @@ class Twig_Tests_Node_Expression_FunctionTest extends Twig_Test_NodeTestCase
return parent::getEnvironment();
}
}
function twig_tests_function_barbar($arg1 = null, $arg2 = null, array $args = array())
{
}
@@ -25,6 +25,9 @@ class Twig_Tests_Node_Expression_TestTest extends Twig_Test_NodeTestCase
public function getTests()
{
$environment = new Twig_Environment();
$environment->addTest(new Twig_SimpleTest('barbar', 'twig_tests_test_barbar', array('is_variadic' => true, 'need_context' => true)));
$tests = array();
$expr = new Twig_Node_Expression_Constant('foo', 1);
@@ -37,6 +40,25 @@ class Twig_Tests_Node_Expression_TestTest extends Twig_Test_NodeTestCase
$tests[] = array($node, 'call_user_func_array($this->env->getTest(\'anonymous\')->getCallable(), array("foo", "foo"))');
}
// arbitrary named arguments
$string = new Twig_Node_Expression_Constant('abc', 1);
$node = $this->createTest($string, 'barbar');
$tests[] = array($node, 'twig_tests_test_barbar("abc")', $environment);
$node = $this->createTest($string, 'barbar', array('foo' => new Twig_Node_Expression_Constant('bar', 1)));
$tests[] = array($node, 'twig_tests_test_barbar("abc", null, null, array("foo" => "bar"))', $environment);
$node = $this->createTest($string, 'barbar', array('arg2' => new Twig_Node_Expression_Constant('bar', 1)));
$tests[] = array($node, 'twig_tests_test_barbar("abc", null, "bar")', $environment);
$node = $this->createTest($string, 'barbar', array(
new Twig_Node_Expression_Constant('1', 1),
new Twig_Node_Expression_Constant('2', 1),
new Twig_Node_Expression_Constant('3', 1),
'foo' => new Twig_Node_Expression_Constant('bar', 1),
));
$tests[] = array($node, 'twig_tests_test_barbar("abc", "1", "2", array(0 => "3", "foo" => "bar"))', $environment);
return $tests;
}
@@ -54,3 +76,7 @@ class Twig_Tests_Node_Expression_TestTest extends Twig_Test_NodeTestCase
return parent::getEnvironment();
}
}
function twig_tests_test_barbar($string, $arg1 = null, $arg2 = null, array $args = array())
{
}