Skip to content

Commit e780fa7

Browse files
committed
bug #892 [TwigComponent] Fix opening of default block inside an open twig block (sneakyvv)
This PR was merged into the 2.x branch. Discussion ---------- [TwigComponent] Fix opening of default block inside an open twig block | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | Tickets | | License | MIT ## Symptom SyntaxError when using a Embedded Component inside a block `A template that extends another one cannot include content outside Twig blocks. Did you forget to put the content inside a {% block %} tag?` ## Reproduce Embed a component inside a twig block. ```twig <twig:Foo> <twig:block name="foo_block"> <twig:Foo> <twig:Foo /> </twig:Foo> </twig:block> </twig:Foo> ``` ## Problem This currently renders to ```twig {% component 'Foo' %} {% block foo_block %} {% component 'Foo' %} {{ component('Foo') }} {% endcomponent %} {% endblock %} {% endcomponent %} ``` The last `{{ component('Foo') }}` should be enclosed within a `content` block. Note: the error won't happen if another non-whitespace character is used before the last `<twig:Foo />`, because then the transformed output contains a `content` block for that. It ony happens when lexing a twig block with a component inside which immediately has another component inside it. ## Cause https://github.com/symfony/ux/blob/2.x/src/TwigComponent/src/Twig/TwigPreLexer.php#L82-L89 checks imo incorrectly whether it's inside a block already. As long as a new component is being processed it's OK to open a new default `content` block. (see solution) ## Solution This PR will lex the above twig syntax to: ```twig {% component 'Foo' %} {% block foo_block %} {% component 'Foo' %} {% block content %}{{ component('Foo') }}{% endblock %} {% endcomponent %} {% endblock %} {% endcomponent %} ``` Even a content block inside a content block is still OK, because the block is part of another component's template. ```twig <twig:Foo> foo_content <twig:block name="foo_block"> <twig:Foo> <twig:Foo /> </twig:Foo> </twig:block> </twig:Foo> ``` ```twig {% component 'Foo' %} {% block content %}foo_content{% endblock %} {% block foo_block %} {% component 'Foo' %} {% block content %}{{ component('Foo') }}{% endblock %} {% endcomponent %} {% endblock %} {% endcomponent %} ``` Note that regular block content is (still) NOT enclosed inside a default block. ```twig <twig:Foo> foo_content <twig:block name="foo_block"> foo_block_content <twig:Foo> <twig:Foo /> </twig:Foo> </twig:block> </twig:Foo> ``` ```twig {% component 'Foo' %} {% block content %}foo_content{% endblock %} {% block foo_block %} foo_block_content {% component 'Foo' %} {% block content %}{{ component('Foo') }}{% endblock %} {% endcomponent %} {% endblock %} {% endcomponent %} ``` Commits ------- 0c636f4 Fix opening of default block inside an open block
2 parents 47906c4 + 0c636f4 commit e780fa7

File tree

2 files changed

+15
-9
lines changed

2 files changed

+15
-9
lines changed

src/TwigComponent/src/Twig/TwigPreLexer.php

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,7 @@ public function __construct(int $startingLine = 1)
3030
$this->line = $startingLine;
3131
}
3232

33-
/**
34-
* @param bool $insideOfBlock Are we sub-parsing the content inside a block?
35-
*/
36-
public function preLexComponents(string $input, bool $insideOfBlock = false): string
33+
public function preLexComponents(string $input): string
3734
{
3835
$this->input = $input;
3936
$this->length = \strlen($input);
@@ -79,11 +76,10 @@ public function preLexComponents(string $input, bool $insideOfBlock = false): st
7976
continue;
8077
}
8178

82-
// if we're already inside a component, and we're not inside a block,
79+
// if we're already inside a component,
8380
// *and* we've just found a new component, then we should try to
8481
// open the default block
85-
if (!$insideOfBlock
86-
&& !empty($this->currentComponents)
82+
if (!empty($this->currentComponents)
8783
&& !$this->currentComponents[\count($this->currentComponents) - 1]['hasDefaultBlock']) {
8884
$output .= $this->addDefaultBlock();
8985
}
@@ -365,7 +361,7 @@ private function consumeBlock(string $componentName): string
365361
$blockContents = $this->consumeUntilEndBlock();
366362

367363
$subLexer = new self($this->line);
368-
$output .= $subLexer->preLexComponents($blockContents, true);
364+
$output .= $subLexer->preLexComponents($blockContents);
369365

370366
$this->consume($closingTag);
371367
$output .= '{% endblock %}';

src/TwigComponent/tests/Unit/TwigPreLexerTest.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,21 @@ public function getLexTests(): iterable
6262
'Hello {% block foo_block %}Foo{% endblock %}{{ component(\'foo\') }}{% block bar_block %}Bar{% endblock %}',
6363
];
6464

65-
yield 'component_with_embedded_component_inside_block' => [
65+
yield 'component_with_component_inside_block' => [
6666
'<twig:foo><twig:block name="foo_block"><twig:bar /></twig:block></twig:foo>',
6767
'{% component \'foo\' %}{% block foo_block %}{{ component(\'bar\') }}{% endblock %}{% endcomponent %}',
6868
];
6969

70+
yield 'component_with_embedded_component_inside_block' => [
71+
'<twig:foo><twig:block name="foo_block"><twig:bar><twig:baz /></twig:bar></twig:block></twig:foo>',
72+
'{% component \'foo\' %}{% block foo_block %}{% component \'bar\' %}{% block content %}{{ component(\'baz\') }}{% endblock %}{% endcomponent %}{% endblock %}{% endcomponent %}',
73+
];
74+
75+
yield 'component_with_embedded_component' => [
76+
'<twig:foo>foo_content<twig:bar><twig:baz /></twig:bar></twig:foo>',
77+
'{% component \'foo\' %}{% block content %}foo_content{% component \'bar\' %}{% block content %}{{ component(\'baz\') }}{% endblock %}{% endcomponent %}{% endblock %}{% endcomponent %}',
78+
];
79+
7080
yield 'attribute_with_no_value' => [
7181
'<twig:foo bar />',
7282
'{{ component(\'foo\', { bar: true }) }}',

0 commit comments

Comments
 (0)