mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-13 10:56:38 +00:00
Make embeds deterministic
This commit is contained in:
committed by
Fabien Potencier
parent
99d95dd6b8
commit
2b4aa4458a
@@ -1,6 +1,6 @@
|
|||||||
# 3.24.1 (2026-XX-XX)
|
# 3.25.0 (2026-XX-XX)
|
||||||
|
|
||||||
* n/a
|
* Use deterministic suffixes for generated embed classes
|
||||||
|
|
||||||
# 3.24.0 (2026-03-17)
|
# 3.24.0 (2026-03-17)
|
||||||
|
|
||||||
|
|||||||
+16
-1
@@ -794,6 +794,21 @@ operand is contained in the right:
|
|||||||
You can use this operator to perform a containment test on strings,
|
You can use this operator to perform a containment test on strings,
|
||||||
sequences, mappings, or objects implementing the ``Traversable`` interface.
|
sequences, mappings, or objects implementing the ``Traversable`` interface.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
For sequences, mappings, and ``Traversable`` objects, ``in`` uses a loose
|
||||||
|
comparison (similar to ``==``); use :doc:`same as <tests/sameas>` for a
|
||||||
|
strict comparison. Like PHP's ``in_array()``, this can yield unexpected
|
||||||
|
results when the left operand is a boolean:
|
||||||
|
|
||||||
|
.. code-block:: twig
|
||||||
|
|
||||||
|
{# returns true because true == 'foo' under PHP loose comparison #}
|
||||||
|
{{ true in ['foo', 'bar'] }}
|
||||||
|
|
||||||
|
Containment on strings only accepts string, integer, and float operands on
|
||||||
|
the left; other types always return ``false``.
|
||||||
|
|
||||||
To perform a negative test, use the ``not in`` operator:
|
To perform a negative test, use the ``not in`` operator:
|
||||||
|
|
||||||
.. code-block:: twig
|
.. code-block:: twig
|
||||||
@@ -1221,7 +1236,7 @@ Twig supports two modifiers:
|
|||||||
|
|
||||||
* *Line whitespace trimming* via the ``~`` modifier: Removes all whitespace
|
* *Line whitespace trimming* via the ``~`` modifier: Removes all whitespace
|
||||||
(excluding newlines). Using this modifier on the right disables the default
|
(excluding newlines). Using this modifier on the right disables the default
|
||||||
removal of the first newline inherited from PHP.
|
removal of the first newline mentioned above.
|
||||||
|
|
||||||
The modifiers can be used on either side of the tags like in ``{%-`` or ``-%}``
|
The modifiers can be used on either side of the tags like in ``{%-`` or ``-%}``
|
||||||
and they consume all whitespace for that side of the tag. It is possible to use
|
and they consume all whitespace for that side of the tag. It is possible to use
|
||||||
|
|||||||
+8
-2
@@ -54,6 +54,7 @@ class Parser
|
|||||||
private $importedSymbols;
|
private $importedSymbols;
|
||||||
private $traits;
|
private $traits;
|
||||||
private $embeddedTemplates = [];
|
private $embeddedTemplates = [];
|
||||||
|
private int $lastEmbedIndex = 0;
|
||||||
private $varNameSalt = 0;
|
private $varNameSalt = 0;
|
||||||
private $ignoreUnknownTwigCallables = false;
|
private $ignoreUnknownTwigCallables = false;
|
||||||
private ExpressionParsers $parsers;
|
private ExpressionParsers $parsers;
|
||||||
@@ -81,8 +82,13 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
public function parse(TokenStream $stream, $test = null, bool $dropNeedle = false): ModuleNode
|
public function parse(TokenStream $stream, $test = null, bool $dropNeedle = false): ModuleNode
|
||||||
{
|
{
|
||||||
|
// reset on root parse() calls only, so the counter spans nested/reentrant parses
|
||||||
|
if (!$this->stack) {
|
||||||
|
$this->lastEmbedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
$vars = get_object_vars($this);
|
$vars = get_object_vars($this);
|
||||||
unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames'], $vars['varNameSalt']);
|
unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames'], $vars['lastEmbedIndex'], $vars['varNameSalt']);
|
||||||
$this->stack[] = $vars;
|
$this->stack[] = $vars;
|
||||||
|
|
||||||
// node visitors
|
// node visitors
|
||||||
@@ -319,7 +325,7 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
public function embedTemplate(ModuleNode $template)
|
public function embedTemplate(ModuleNode $template)
|
||||||
{
|
{
|
||||||
$template->setIndex(mt_rand());
|
$template->setIndex(++$this->lastEmbedIndex);
|
||||||
|
|
||||||
$this->embeddedTemplates[] = $template;
|
$this->embeddedTemplates[] = $template;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,6 +206,20 @@ EOF, 'index')));
|
|||||||
$this->assertTrue($argumentNodes->getNode(3)->getAttribute('value'));
|
$this->assertTrue($argumentNodes->getNode(3)->getAttribute('value'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testEmbeddedTemplatesHaveSequentialIndices(): void
|
||||||
|
{
|
||||||
|
$template = new Source('{% embed "first" %}{% endembed %}{% embed "second" %}{% endembed %}', 'index');
|
||||||
|
$lexer = new Lexer(new Environment(new ArrayLoader()));
|
||||||
|
$stream = $lexer->tokenize($template);
|
||||||
|
|
||||||
|
$embeds = $this->getParser()
|
||||||
|
->parse($stream)
|
||||||
|
->getAttribute('embedded_templates');
|
||||||
|
|
||||||
|
$this->assertSame(1, $embeds->getNode(0)->getAttribute('index'));
|
||||||
|
$this->assertSame(2, $embeds->getNode(1)->getAttribute('index'));
|
||||||
|
}
|
||||||
|
|
||||||
protected function getParser()
|
protected function getParser()
|
||||||
{
|
{
|
||||||
$parser = new Parser(new Environment(new ArrayLoader()));
|
$parser = new Parser(new Environment(new ArrayLoader()));
|
||||||
|
|||||||
Reference in New Issue
Block a user