Merge branch '3.x' into 4.x

* 3.x:
  Fix typos in CHANGELOG
  Fix typo
  Fix some minor issues
  Add support for named arguments on special functions
This commit is contained in:
Fabien Potencier
2024-08-17 18:03:35 +02:00
10 changed files with 52 additions and 18 deletions
+2 -2
View File
@@ -7,7 +7,7 @@ The ``markdown_to_html`` filter converts a block of Markdown to HTML:
{% apply markdown_to_html %}
Title
======
=====
Hello!
{% endapply %}
@@ -19,7 +19,7 @@ removed consistently before conversion:
{% apply markdown_to_html %}
Title
======
=====
Hello!
{% endapply %}
+7
View File
@@ -21,3 +21,10 @@ attribute:
The resolution algorithm is the same as the one used for the ``.``
notation, except that the item can be any valid expression.
Arguments
---------
* ``variable``: The variable
* ``attribute``: The attribute name
* ``arguments``: An array of arguments to pass to the call
+6
View File
@@ -32,6 +32,12 @@ current template:
...
{% endif %}
Arguments
---------
* ``name``: The block name
* ``template``: The template where to look for the block
.. seealso::
:doc:`extends<../tags/extends>`, :doc:`parent<../functions/parent>`
+13 -11
View File
@@ -34,6 +34,7 @@ use Twig\Node\Expression\Unary\NegUnary;
use Twig\Node\Expression\Unary\NotUnary;
use Twig\Node\Expression\Unary\PosUnary;
use Twig\Node\Node;
use Twig\Util\CallableArgumentsExtractor;
/**
* Parses expressions.
@@ -439,7 +440,6 @@ class ExpressionParser
{
switch ($name) {
case 'parent':
$this->parseArguments();
if (!\count($this->parser->getBlockStack())) {
throw new SyntaxError('Calling "parent" outside a block is forbidden.', $line, $this->parser->getStream()->getSourceContext());
}
@@ -448,21 +448,23 @@ class ExpressionParser
throw new SyntaxError('Calling "parent" on a template that does not extend nor "use" another template is forbidden.', $line, $this->parser->getStream()->getSourceContext());
}
$this->parseArguments(true);
return new ParentExpression($this->parser->peekBlockStack(), $line);
case 'block':
$args = $this->parseArguments();
if (\count($args) < 1) {
throw new SyntaxError('The "block" function takes one argument (the block name).', $line, $this->parser->getStream()->getSourceContext());
}
$fakeNode = new Node(lineno: $line);
$fakeNode->setSourceContext($this->parser->getStream()->getSourceContext());
$fakeFunction = new TwigFunction('block', fn ($name, $template = null) => null);
$args = (new CallableArgumentsExtractor($fakeNode, $fakeFunction))->extractArguments($this->parseArguments(true));
return new BlockReferenceExpression($args->getNode('0'), \count($args) > 1 ? $args->getNode('1') : null, $line);
return new BlockReferenceExpression($args[0], $args[1] ?? null, $line);
case 'attribute':
$args = $this->parseArguments();
if (\count($args) < 2) {
throw new SyntaxError('The "attribute" function takes at least two arguments (the variable and the attributes).', $line, $this->parser->getStream()->getSourceContext());
}
$fakeNode = new Node(lineno: $line);
$fakeNode->setSourceContext($this->parser->getStream()->getSourceContext());
$fakeFunction = new TwigFunction('attribute', fn ($variable, $attribute, $arguments = null) => null);
$args = (new CallableArgumentsExtractor($fakeNode, $fakeFunction))->extractArguments($this->parseArguments(true));
return new GetAttrExpression($args->getNode('0'), $args->getNode('1'), \count($args) > 2 ? $args->getNode('2') : null, Template::ANY_CALL, $line);
return new GetAttrExpression($args[0], $args[1], $args[2] ?? null, Template::ANY_CALL, $line);
case 'loop':
$args = $this->parseArguments();
if (\count($args) < 1) {
+3 -3
View File
@@ -121,7 +121,7 @@ final class CallableArgumentsExtractor
$optionalArguments = [];
++$pos;
} elseif ($callableParameter->isDefaultValueAvailable()) {
$optionalArguments[] = new ConstantExpression($callableParameter->getDefaultValue(), -1);
$optionalArguments[] = new ConstantExpression($callableParameter->getDefaultValue(), $this->node->getTemplateLine());
} elseif ($callableParameter->isOptional()) {
if (!$extractedArguments) {
break;
@@ -134,12 +134,12 @@ final class CallableArgumentsExtractor
}
if ($this->twigCallable->isVariadic()) {
$arbitraryArguments = $isPhpVariadic ? new VariadicExpression([], -1) : new ArrayExpression([], -1);
$arbitraryArguments = $isPhpVariadic ? new VariadicExpression([], $this->node->getTemplateLine()) : new ArrayExpression([], $this->node->getTemplateLine());
foreach ($extractedArguments as $key => $value) {
if (\is_int($key)) {
$arbitraryArguments->addElement($value);
} else {
$arbitraryArguments->addElement($value, new ConstantExpression($key, -1));
$arbitraryArguments->addElement($value, new ConstantExpression($key, $this->node->getTemplateLine()));
}
unset($extractedArguments[$key]);
}
+4
View File
@@ -2,17 +2,21 @@
"attribute" function
--TEMPLATE--
{{ attribute(obj, method) }}
{{ attribute(variable=obj, attribute=method) }}
{{ attribute(array, item) }}
{{ attribute(obj, "bar", ["a", "b"]) }}
{{ attribute(obj, "bar", arguments) }}
{{ attribute(variable=obj, attribute="bar", arguments=arguments) }}
{{ attribute(obj, method) is defined ? 'ok' : 'ko' }}
{{ attribute(obj, nonmethod) is defined ? 'ok' : 'ko' }}
--DATA--
return ['obj' => new Twig\Tests\TwigTestFoo(), 'method' => 'foo', 'array' => ['foo' => 'bar'], 'item' => 'foo', 'nonmethod' => 'xxx', 'arguments' => ['a', 'b']]
--EXPECT--
foo
foo
bar
bar_a-b
bar_a-b
bar_a-b
ok
ko
@@ -0,0 +1,8 @@
--TEST--
"attribute" function
--TEMPLATE--
{{ attribute(var=var, template="tpl") }}
--DATA--
return ['var' => null]
--EXCEPTION--
Twig\Error\SyntaxError: Value for argument "variable" is required for function "attribute" in "index.twig" at line 2.
+2 -1
View File
@@ -5,8 +5,9 @@
{% block bar %}BAR{% endblock %}
--TEMPLATE(base.twig)--
{% block foo %}{{ block('bar') }}{% endblock %}
{% block baz %}{{ block(name='bar') }}{% endblock %}
{% block bar %}BAR_BASE{% endblock %}
--DATA--
return []
--EXPECT--
BARBAR
BARBARBAR
@@ -6,6 +6,10 @@
{{ block('foo', included_loaded_internal) }}
{% set output = block('foo', 'included.twig') %}
{{ output }}
{% set output = block(name='foo', template='included.twig') %}
{{ output }}
{% set output = block(template='included.twig', name='foo') %}
{{ output }}
{% block foo %}NOT FOO{% endblock %}
--TEMPLATE(included.twig)--
{% block foo %}FOO{% endblock %}
@@ -19,4 +23,6 @@ FOO
FOO
FOO
FOO
FOO
FOO
NOT FOO
@@ -9,4 +9,4 @@
--DATA--
return []
--EXCEPTION--
Twig\Error\SyntaxError: The "block" function takes one argument (the block name) in "base.twig" at line 2.
Twig\Error\SyntaxError: Value for argument "name" is required for function "block" in "base.twig" at line 2.