Skip to content

Commit

Permalink
feature #1659 Added support for unconsumed macro arguments (hason)
Browse files Browse the repository at this point in the history
This PR was merged into the 1.x branch.

Discussion
----------

Added support for unconsumed macro arguments

Fixes #1159 and supplements #1699

Commits
-------

7f00cb9 Added support for unconsumed macro arguments
  • Loading branch information
fabpot committed Jul 5, 2015
2 parents 82636dc + 7f00cb9 commit 5020747
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 17 deletions.
4 changes: 4 additions & 0 deletions doc/tags/macro.rst
Expand Up @@ -20,6 +20,10 @@ Macros differs from native PHP functions in a few ways:

* Arguments of a macro are always optional.

* If more positional arguments are passed to the macro than accepted by the macro,
they end up in the special ``varargs`` variable as a list of values. This variable
is always available in the macro body.

But as with PHP functions, macros don't have access to the current template
variables.

Expand Down
6 changes: 5 additions & 1 deletion doc/templates.rst
Expand Up @@ -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
Expand Down Expand Up @@ -541,6 +541,10 @@ macro call:
<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}" />
{% endmacro %}
Inside macros, you have access to special variable ``varargs``. If more positional arguments
are passed to the macro than accepted by the macro, they end up in the special ``varargs`` variable
as a list of values.

.. _twig-expressions:

Expressions
Expand Down
60 changes: 45 additions & 15 deletions lib/Twig/Node/Macro.php
Expand Up @@ -16,8 +16,19 @@
*/
class Twig_Node_Macro extends Twig_Node
{
const VARARGS_NAME = 'varargs';

public function __construct($name, Twig_NodeInterface $body, Twig_NodeInterface $arguments, $lineno, $tag = null)
{
foreach ($arguments as $argumentName => $argument) {
if (self::VARARGS_NAME === $argumentName) {
throw new Twig_Error_Syntax(sprintf(
'The argument "%s" in macro "%s" cannot be defined because the variable "%s" is reserved for arbitrary arguments',
self::VARARGS_NAME, $name, self::VARARGS_NAME
), $argument->getLine());
}
}

parent::__construct(array('body' => $body, 'arguments' => $arguments), array('name' => $name), $lineno, $tag);
}

Expand Down Expand Up @@ -46,36 +57,55 @@ public function compile(Twig_Compiler $compiler)
}
}

if (PHP_VERSION_ID >= 50600) {
if ($count) {
$compiler->raw(', ');
}

$compiler->raw('...$__varargs__');
}

$compiler
->raw(")\n")
->write("{\n")
->indent()
;

if (!count($this->getNode('arguments'))) {
$compiler->write("\$context = \$this->env->getGlobals();\n\n");
} else {
$compiler
->write("\$context = \$this->env->mergeGlobals(array(\n")
->indent()
;

foreach ($this->getNode('arguments') as $name => $default) {
$compiler
->write("\$context = \$this->env->mergeGlobals(array(\n")
->indent()
->addIndentation()
->string($name)
->raw(' => $__'.$name.'__')
->raw(",\n")
;
}

foreach ($this->getNode('arguments') as $name => $default) {
$compiler
->write('')
->string($name)
->raw(' => $__'.$name.'__')
->raw(",\n")
;
}
$compiler
->addIndentation()
->string(self::VARARGS_NAME)
->raw(' => ')
;

if (PHP_VERSION_ID >= 50600) {
$compiler->raw("\$__varargs__,\n");
} else {
$compiler
->outdent()
->write("));\n\n")
->raw('func_num_args() > ')
->repr($count)
->raw(' ? array_slice(func_get_args(), ')
->repr($count)
->raw(") : array(),\n")
;
}

$compiler
->outdent()
->write("));\n\n")
->write("\$blocks = array();\n\n")
->write("ob_start();\n")
->write("try {\n")
Expand Down
21 changes: 21 additions & 0 deletions test/Twig/Tests/Fixtures/macros/varargs.test
@@ -0,0 +1,21 @@
--TEST--
macro with arbitrary arguments
--TEMPLATE--
{% from _self import test1, test2 %}

{% macro test1(var) %}
{{- var }}: {{ varargs|join(", ") }}
{% endmacro %}

{% macro test2() %}
{{- varargs|join(", ") }}
{% endmacro %}

{{ test1("foo", "bar", "foobar") }}
{{ test2("foo", "bar", "foobar") }}
--DATA--
return array();
--EXPECT--
foo: bar, foobar

foo, bar, foobar
8 changes: 8 additions & 0 deletions test/Twig/Tests/Fixtures/macros/varargs_argument.test
@@ -0,0 +1,8 @@
--TEST--
macro with varargs argument
--TEMPLATE--
{% macro test(varargs) %}
{% endmacro %}
--EXCEPTION--
Twig_Error_Syntax: The argument "varargs" in macro "test" cannot be defined because the variable "varargs" is reserved for arbitrary arguments in "index.twig" at line 2

11 changes: 10 additions & 1 deletion test/Twig/Tests/Node/MacroTest.php
Expand Up @@ -31,14 +31,23 @@ public function getTests()
), array(), 1);
$node = new Twig_Node_Macro('foo', $body, $arguments, 1);

if (PHP_VERSION_ID >= 50600) {
$declaration = ', ...$__varargs__';
$varargs = '$__varargs__';
} else {
$declaration = '';
$varargs = 'func_num_args() > 2 ? array_slice(func_get_args(), 2) : array()';
}

return array(
array($node, <<<EOF
// line 1
public function getfoo(\$__foo__ = null, \$__bar__ = "Foo")
public function getfoo(\$__foo__ = null, \$__bar__ = "Foo"$declaration)
{
\$context = \$this->env->mergeGlobals(array(
"foo" => \$__foo__,
"bar" => \$__bar__,
"varargs" => $varargs,
));
\$blocks = array();
Expand Down

0 comments on commit 5020747

Please sign in to comment.