From 06a5bf532f753cf5138e7a48d45c75a4d0a2e409 Mon Sep 17 00:00:00 2001
From: Hugo Alliaume
Date: Sat, 13 Jan 2024 14:00:44 +0100
Subject: [PATCH] =?UTF-8?q?[TwigComponent]=C2=A0Fix=20usage=20of=20{%=20em?=
=?UTF-8?q?bed=20%}=20with=20{%=20block=20%}=20in=20=20components,?=
=?UTF-8?q?=20close=20#952?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/TwigComponent/src/Twig/TwigPreLexer.php | 41 +++++++++++++++++--
.../tests/Unit/TwigPreLexerTest.php | 39 ++++++++++++++++++
2 files changed, 76 insertions(+), 4 deletions(-)
diff --git a/src/TwigComponent/src/Twig/TwigPreLexer.php b/src/TwigComponent/src/Twig/TwigPreLexer.php
index fe4e7d2c680..13b4ceb5470 100644
--- a/src/TwigComponent/src/Twig/TwigPreLexer.php
+++ b/src/TwigComponent/src/Twig/TwigPreLexer.php
@@ -12,6 +12,7 @@
namespace Symfony\UX\TwigComponent\Twig;
use Twig\Error\SyntaxError;
+use Twig\Lexer;
/**
* Rewrites syntaxes to {% component %} syntaxes.
@@ -42,6 +43,8 @@ public function preLexComponents(string $input): string
$this->length = \strlen($input);
$output = '';
+ $inTwigEmbed = false;
+
while ($this->position < $this->length) {
// ignore content inside verbatim block #947
if ($this->consume('{% verbatim %}')) {
@@ -67,24 +70,54 @@ public function preLexComponents(string $input): string
}
}
+ if ($this->consume('{% embed')) {
+ $inTwigEmbed = true;
+ $output .= '{% embed';
+ $output .= $this->consumeUntil('%}');
+
+ continue;
+ }
+
+ if ($this->consume('{% endembed %}')) {
+ $inTwigEmbed = false;
+ $output .= '{% endembed %}';
+
+ continue;
+ }
+
$isTwigHtmlOpening = $this->consume('currentComponents) && $isTraditionalBlockOpening = $this->consume('{% block'))) {
$componentName = $isTraditionalBlockOpening ? 'block' : $this->consumeComponentName();
if ('block' === $componentName) {
// if we're already inside the "default" block, let's close it
- if (!empty($this->currentComponents) && $this->currentComponents[\count($this->currentComponents) - 1]['hasDefaultBlock']) {
+ if (!empty($this->currentComponents) && $this->currentComponents[\count($this->currentComponents) - 1]['hasDefaultBlock'] && !$inTwigEmbed) {
$output .= '{% endblock %}';
$this->currentComponents[\count($this->currentComponents) - 1]['hasDefaultBlock'] = false;
}
if ($isTraditionalBlockOpening) {
+ $prevPosition = $this->position;
+
// add what we've consumed so far
$output .= '{% block';
$output .= $this->consumeUntil('%}');
- $output .= $this->consumeUntilEndBlock();
+
+ // If the last consumed string does not match the Twig's block name regex, we assume the block is self-closing
+ $isBlockSelfClosing = '' !== preg_replace(
+ Lexer::REGEX_NAME,
+ '',
+ trim(mb_substr($this->input, $prevPosition, $this->position - $prevPosition)))
+ ;
+
+ if ($isBlockSelfClosing && $this->consume('%}')) {
+ $output .= '%}';
+ } else {
+ $output .= $this->consumeUntilEndBlock();
+ }
continue;
}
@@ -420,8 +453,8 @@ private function consumeUntilEndBlock(): string
if (!$inComment && '{% endblock %}' === substr($this->input, $this->position, 14)) {
if (1 === $depth) {
- // in this case, we want to advanced ALL the way beyond the endblock
- $this->position += 14;
+ // in this case, we want to advance ALL the way beyond the endblock
+ $this->position += 14 /* strlen('{% endblock %}') */;
break;
} else {
--$depth;
diff --git a/src/TwigComponent/tests/Unit/TwigPreLexerTest.php b/src/TwigComponent/tests/Unit/TwigPreLexerTest.php
index 99b88c7801b..2674b84fb2b 100644
--- a/src/TwigComponent/tests/Unit/TwigPreLexerTest.php
+++ b/src/TwigComponent/tests/Unit/TwigPreLexerTest.php
@@ -183,6 +183,45 @@ public static function getLexTests(): iterable
EOF
];
+ yield 'component_where_entire_default_block_is_twig_embed' => [
+ <<
+
+ {% embed "my_embed.html.twig" with { foo: 'bar' } %}{% endembed %}
+
+
+ EOF,
+ <<
+ {% embed "my_embed.html.twig" with { foo: 'bar' } %}{% endembed %}
+
+ {% endblock %}{% endcomponent %}
+ EOF,
+ ];
+ yield 'component_where_entire_default_block_is_twig_embed_with_blocks' => [
+ <<
+
+ {% embed "my_embed.html.twig" %}
+ {% block my_embed_block_1 "foo" %}
+ {% block my_embed_block_2 %}bar{% endblock %}
+ {% endembed %}
+
+
+ EOF,
+ <<
+ {% embed "my_embed.html.twig" %}
+ {% block my_embed_block_1 "foo" %}
+ {% block my_embed_block_2 %}bar{% endblock %}
+ {% endembed %}
+
+ {% endblock %}{% endcomponent %}
+ EOF,
+ ];
+
yield 'string_inside_of_twig_code_not_escaped' => [
<<