mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-13 19:06:40 +00:00
added the ability to set default values for macro arguments (closes #447)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
* 1.12.0 (2012-XX-XX)
|
||||
|
||||
* added the ability to set default values for macro arguments
|
||||
* 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
|
||||
|
||||
@@ -493,6 +493,9 @@ For bigger sections it makes sense to mark a block :doc:`raw<tags/raw>`.
|
||||
Macros
|
||||
------
|
||||
|
||||
.. versionadded:: 1.12
|
||||
Support for default argument values was added in Twig 1.12.
|
||||
|
||||
Macros are comparable with functions in regular programming languages. They
|
||||
are useful to reuse often used HTML fragments to not repeat yourself.
|
||||
|
||||
@@ -528,6 +531,15 @@ current namespace via the :doc:`from<tags/from>` tag and optionally alias them:
|
||||
<dd>{{ input_field('password', '', 'password') }}</dd>
|
||||
</dl>
|
||||
|
||||
A default value can also be defined for macro arguments when not provided in a
|
||||
macro call:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% macro input(name, value = "", type = "text", size = 20) %}
|
||||
<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}" />
|
||||
{% endmacro %}
|
||||
|
||||
Expressions
|
||||
-----------
|
||||
|
||||
|
||||
@@ -452,22 +452,44 @@ class Twig_ExpressionParser
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma');
|
||||
}
|
||||
|
||||
$value = $this->parseExpression();
|
||||
if ($definition) {
|
||||
$token = $stream->expect(Twig_Token::NAME_TYPE, null, 'An argument must be a name');
|
||||
$value = new Twig_Node_Expression_Name($token->getValue(), $this->parser->getCurrentToken()->getLine());
|
||||
} else {
|
||||
$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());
|
||||
throw new Twig_Error_Syntax(sprintf('A parameter name must be a string, "%s" given', get_class($value)), $token->getLine(), $this->parser->getFilename());
|
||||
}
|
||||
$name = $value->getAttribute('name');
|
||||
$value = $definition ? $this->parsePrimaryExpression() : $this->parseExpression();
|
||||
|
||||
if ($definition) {
|
||||
$value = $this->parsePrimaryExpression();
|
||||
|
||||
if (!$this->checkConstantExpression($value)) {
|
||||
throw new Twig_Error_Syntax(sprintf('A default value for an argument must be a constant (a boolean, a string, a number, or an array).'), $token->getLine(), $this->parser->getFilename());
|
||||
}
|
||||
} else {
|
||||
$value = $this->parseExpression();
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $name) {
|
||||
$args[] = $value;
|
||||
} else {
|
||||
if ($definition) {
|
||||
if (null === $name) {
|
||||
$name = $value->getAttribute('name');
|
||||
$value = new Twig_Node_Expression_Constant(null, $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
$args[$name] = $value;
|
||||
} else {
|
||||
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');
|
||||
@@ -539,4 +561,20 @@ class Twig_ExpressionParser
|
||||
|
||||
return $filter instanceof Twig_Filter_Node ? $filter->getClass() : 'Twig_Node_Expression_Filter';
|
||||
}
|
||||
|
||||
// checks that the node only contains "constant" elements
|
||||
protected function checkConstantExpression(Twig_NodeInterface $node)
|
||||
{
|
||||
if (!($node instanceof Twig_Node_Expression_Constant || $node instanceof Twig_Node_Expression_Array)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($node as $n) {
|
||||
if (!$this->checkConstantExpression($n)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+21
-8
@@ -29,14 +29,27 @@ class Twig_Node_Macro extends Twig_Node
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$arguments = array();
|
||||
foreach ($this->getNode('arguments') as $argument) {
|
||||
$arguments[] = '$_'.$argument->getAttribute('name').' = null';
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write(sprintf("public function get%s(", $this->getAttribute('name')))
|
||||
;
|
||||
|
||||
$count = count($this->getNode('arguments'));
|
||||
$pos = 0;
|
||||
foreach ($this->getNode('arguments') as $name => $default) {
|
||||
$compiler
|
||||
->raw('$_'.$name.' = ')
|
||||
->subcompile($default)
|
||||
;
|
||||
|
||||
if (++$pos < $count) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
}
|
||||
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write(sprintf("public function get%s(%s)\n", $this->getAttribute('name'), implode(', ', $arguments)), "{\n")
|
||||
->raw(")\n")
|
||||
->write("{\n")
|
||||
->indent()
|
||||
;
|
||||
|
||||
@@ -48,11 +61,11 @@ class Twig_Node_Macro extends Twig_Node
|
||||
->indent()
|
||||
;
|
||||
|
||||
foreach ($this->getNode('arguments') as $argument) {
|
||||
foreach ($this->getNode('arguments') as $name => $default) {
|
||||
$compiler
|
||||
->write('')
|
||||
->string($argument->getAttribute('name'))
|
||||
->raw(' => $_'.$argument->getAttribute('name'))
|
||||
->string($name)
|
||||
->raw(' => $_'.$name)
|
||||
->raw(",\n")
|
||||
;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class Twig_TokenParser_Macro extends Twig_TokenParser
|
||||
$stream = $this->parser->getStream();
|
||||
$name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
|
||||
|
||||
$arguments = $this->parser->getExpressionParser()->parseArguments();
|
||||
$arguments = $this->parser->getExpressionParser()->parseArguments(true, true);
|
||||
|
||||
$stream->expect(Twig_Token::BLOCK_END_TYPE);
|
||||
$this->parser->pushLocalScope();
|
||||
|
||||
@@ -237,6 +237,63 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
|
||||
$parser->parse($env->tokenize('{% from _self import foo %}{% macro foo() %}{% endmacro %}{{ foo(name="Foo") }}', 'index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Twig_Error_Syntax
|
||||
* @expectedExceptionMessage An argument must be a name. Unexpected token "string" of value "a" ("name" expected) in "index" at line 1
|
||||
*/
|
||||
public function testMacroDefinitionDoesNotSupportNonNameVariableName()
|
||||
{
|
||||
$env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
|
||||
$parser = new Twig_Parser($env);
|
||||
|
||||
$parser->parse($env->tokenize('{% macro foo("a") %}{% endmacro %}', 'index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Twig_Error_Syntax
|
||||
* @expectedExceptionMessage A default value for an argument must be a constant (a boolean, a string, a number, or an array) in "index" at line 1
|
||||
* @dataProvider getMacroDefinitionDoesNotSupportNonConstantDefaultValues
|
||||
*/
|
||||
public function testMacroDefinitionDoesNotSupportNonConstantDefaultValues($template)
|
||||
{
|
||||
$env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
|
||||
$parser = new Twig_Parser($env);
|
||||
|
||||
$parser->parse($env->tokenize($template, 'index'));
|
||||
}
|
||||
|
||||
public function getMacroDefinitionDoesNotSupportNonConstantDefaultValues()
|
||||
{
|
||||
return array(
|
||||
array('{% macro foo(name = "a #{foo} a") %}{% endmacro %}'),
|
||||
array('{% macro foo(name = [["b", "a #{foo} a"]]) %}{% endmacro %}'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getMacroDefinitionSupportsConstantDefaultValues
|
||||
*/
|
||||
public function testMacroDefinitionSupportsConstantDefaultValues($template)
|
||||
{
|
||||
$env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
|
||||
$parser = new Twig_Parser($env);
|
||||
|
||||
$parser->parse($env->tokenize($template, 'index'));
|
||||
}
|
||||
|
||||
public function getMacroDefinitionSupportsConstantDefaultValues()
|
||||
{
|
||||
return array(
|
||||
array('{% macro foo(name = "aa") %}{% endmacro %}'),
|
||||
array('{% macro foo(name = 12) %}{% endmacro %}'),
|
||||
array('{% macro foo(name = true) %}{% endmacro %}'),
|
||||
array('{% macro foo(name = ["a"]) %}{% endmacro %}'),
|
||||
array('{% macro foo(name = [["a"]]) %}{% endmacro %}'),
|
||||
array('{% macro foo(name = {a: "a"}) %}{% endmacro %}'),
|
||||
array('{% macro foo(name = {a: {b: "a"}}) %}{% endmacro %}'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Twig_Error_Syntax
|
||||
* @expectedExceptionMessage The function "cycl" does not exist. Did you mean "cycle" in "index" at line 1
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
--TEST--
|
||||
macro
|
||||
--TEMPLATE--
|
||||
{% from _self import test %}
|
||||
|
||||
{% macro test(a, b = 'bar') -%}
|
||||
{{ a }}{{ b }}
|
||||
{%- endmacro %}
|
||||
|
||||
{{ test('foo') }}
|
||||
{{ test('bar', 'foo') }}
|
||||
--DATA--
|
||||
return array();
|
||||
--EXPECT--
|
||||
foobar
|
||||
barfoo
|
||||
@@ -37,16 +37,20 @@ class Twig_Tests_Node_MacroTest extends Twig_Test_NodeTestCase
|
||||
public function getTests()
|
||||
{
|
||||
$body = new Twig_Node_Text('foo', 1);
|
||||
$arguments = new Twig_Node(array(new Twig_Node_Expression_Name('foo', 1)), array(), 1);
|
||||
$arguments = new Twig_Node(array(
|
||||
'foo' => new Twig_Node_Expression_Constant(null, 1),
|
||||
'bar' => new Twig_Node_Expression_Constant('Foo', 1),
|
||||
), array(), 1);
|
||||
$node = new Twig_Node_Macro('foo', $body, $arguments, 1);
|
||||
|
||||
return array(
|
||||
array($node, <<<EOF
|
||||
// line 1
|
||||
public function getfoo(\$_foo = null)
|
||||
public function getfoo(\$_foo = null, \$_bar = "Foo")
|
||||
{
|
||||
\$context = \$this->env->mergeGlobals(array(
|
||||
"foo" => \$_foo,
|
||||
"bar" => \$_bar,
|
||||
));
|
||||
|
||||
\$blocks = array();
|
||||
|
||||
Reference in New Issue
Block a user