mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-12 18:36:53 +00:00
Make {} optional for the types tag
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
# 3.19.0 (2025-XX-XX)
|
# 3.19.0 (2025-XX-XX)
|
||||||
|
|
||||||
|
* Make `{}` optional for the `types` tag
|
||||||
* Add `LastModifiedExtensionInterface` and implementation in `AbstractExtension` to track modification of runtime classes
|
* Add `LastModifiedExtensionInterface` and implementation in `AbstractExtension` to track modification of runtime classes
|
||||||
|
|
||||||
# 3.18.0 (2024-12-29)
|
# 3.18.0 (2024-12-29)
|
||||||
|
|||||||
+22
-9
@@ -6,17 +6,23 @@
|
|||||||
The ``types`` tag was added in Twig 3.13. This tag is **experimental** and
|
The ``types`` tag was added in Twig 3.13. This tag is **experimental** and
|
||||||
can change based on usage and feedback.
|
can change based on usage and feedback.
|
||||||
|
|
||||||
The ``types`` tag declares the types of template variables.
|
Use the ``types`` tag to declare the type of a variable:
|
||||||
|
|
||||||
.. note::
|
.. code-block:: twig
|
||||||
|
|
||||||
The types declared in a template are local to that template and must not be
|
{% types is_correct: 'boolean' %}
|
||||||
propagated to included templates. This is because a template can be
|
{% types score: 'number' %}
|
||||||
included from multiple different places, each potentially having different
|
|
||||||
variable types.
|
|
||||||
|
|
||||||
Here is how to declare that ``is_correct`` is a boolean, while ``score`` is a
|
Or multiple variables:
|
||||||
number (see note below):
|
|
||||||
|
.. code-block:: twig
|
||||||
|
|
||||||
|
{% types
|
||||||
|
is_correct: 'boolean',
|
||||||
|
score: 'number',
|
||||||
|
%}
|
||||||
|
|
||||||
|
You can also enclose types with ``{}``:
|
||||||
|
|
||||||
.. code-block:: twig
|
.. code-block:: twig
|
||||||
|
|
||||||
@@ -25,7 +31,7 @@ number (see note below):
|
|||||||
score: 'number',
|
score: 'number',
|
||||||
} %}
|
} %}
|
||||||
|
|
||||||
You can declare variables as optional by adding the ``?`` suffix:
|
Declare optional variables by adding a ``?`` suffix:
|
||||||
|
|
||||||
.. code-block:: twig
|
.. code-block:: twig
|
||||||
|
|
||||||
@@ -43,6 +49,13 @@ validate variables or their types, this tag enables extensions to do this.
|
|||||||
Additionally, :ref:`Twig extensions <creating_extensions>` can analyze these
|
Additionally, :ref:`Twig extensions <creating_extensions>` can analyze these
|
||||||
tags to perform compile-time and runtime analysis of templates.
|
tags to perform compile-time and runtime analysis of templates.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The types declared in a template are local to that template and must not be
|
||||||
|
propagated to included templates. This is because a template can be
|
||||||
|
included from multiple different places, each potentially having different
|
||||||
|
variable types.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
The syntax for and contents of type strings are intentionally left out of
|
The syntax for and contents of type strings are intentionally left out of
|
||||||
|
|||||||
@@ -31,9 +31,7 @@ final class TypesTokenParser extends AbstractTokenParser
|
|||||||
public function parse(Token $token): Node
|
public function parse(Token $token): Node
|
||||||
{
|
{
|
||||||
$stream = $this->parser->getStream();
|
$stream = $this->parser->getStream();
|
||||||
|
|
||||||
$types = $this->parseSimpleMappingExpression($stream);
|
$types = $this->parseSimpleMappingExpression($stream);
|
||||||
|
|
||||||
$stream->expect(Token::BLOCK_END_TYPE);
|
$stream->expect(Token::BLOCK_END_TYPE);
|
||||||
|
|
||||||
return new TypesNode($types, $token->getLine());
|
return new TypesNode($types, $token->getLine());
|
||||||
@@ -46,17 +44,15 @@ final class TypesTokenParser extends AbstractTokenParser
|
|||||||
*/
|
*/
|
||||||
private function parseSimpleMappingExpression(TokenStream $stream): array
|
private function parseSimpleMappingExpression(TokenStream $stream): array
|
||||||
{
|
{
|
||||||
$stream->expect(Token::PUNCTUATION_TYPE, '{', 'A mapping element was expected');
|
$enclosed = null !== $stream->nextIf(Token::PUNCTUATION_TYPE, '{');
|
||||||
|
|
||||||
$types = [];
|
$types = [];
|
||||||
|
|
||||||
$first = true;
|
$first = true;
|
||||||
while (!$stream->test(Token::PUNCTUATION_TYPE, '}')) {
|
while (!($stream->test(Token::PUNCTUATION_TYPE, '}') || $stream->test(Token::BLOCK_END_TYPE))) {
|
||||||
if (!$first) {
|
if (!$first) {
|
||||||
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'A type string must be followed by a comma');
|
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'A type string must be followed by a comma');
|
||||||
|
|
||||||
// trailing ,?
|
// trailing ,?
|
||||||
if ($stream->test(Token::PUNCTUATION_TYPE, '}')) {
|
if ($stream->test(Token::PUNCTUATION_TYPE, '}') || $stream->test(Token::BLOCK_END_TYPE)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,7 +74,10 @@ final class TypesTokenParser extends AbstractTokenParser
|
|||||||
'optional' => $isOptional,
|
'optional' => $isOptional,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
$stream->expect(Token::PUNCTUATION_TYPE, '}', 'An opened mapping is not properly closed');
|
|
||||||
|
if ($enclosed) {
|
||||||
|
$stream->expect(Token::PUNCTUATION_TYPE, '}', 'An opened mapping is not properly closed');
|
||||||
|
}
|
||||||
|
|
||||||
return $types;
|
return $types;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,15 @@ class TypesTokenParserTest extends TestCase
|
|||||||
'baz' => ['type' => 'baz', 'optional' => false],
|
'baz' => ['type' => 'baz', 'optional' => false],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// without {} enclosing
|
||||||
|
[
|
||||||
|
'{% types foo: "foo", bar: "bar" %}',
|
||||||
|
[
|
||||||
|
'foo' => ['type' => 'foo', 'optional' => false],
|
||||||
|
'bar' => ['type' => 'bar', 'optional' => false],
|
||||||
|
],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user