Skip to content

Commit

Permalink
bug #859 [TwigComponent] Fixing bug where traditional blocks aren't h…
Browse files Browse the repository at this point in the history
…andled correctly (weaverryan)

This PR was squashed before being merged into the 2.x branch.

Discussion
----------

[TwigComponent] Fixing bug where traditional blocks aren't handled correctly

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| Tickets       | Fixes part of #844 (comment)
| License       | MIT

Using `{% block traditional_block %}` inside of a `<twig:Component` syntax should now work. However, even though I tried to make the TwigPreLexer a bit more intelligent than just a regex parser, it's reaching its limits of complexity. Even this fix will break down if the user adds extra whitespace - e.g. `{%       block traditional_block %}`. It's likely that `TwigPreLexer` will need to be converted to an actual Lexer -> token stream -> parser type of a system. On the bright side, that would make it easier to integrate into Twig core if we ever chose to do that (the parser wouldn't convert over directly, but the lexer & tokens in theory would).

Cheers!

Commits
-------

2ca9430 [TwigComponent] Fixing bug where traditional blocks aren't handled correctly
  • Loading branch information
weaverryan committed May 12, 2023
2 parents c6e3560 + 2ca9430 commit 8700c90
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 3 deletions.
33 changes: 30 additions & 3 deletions src/TwigComponent/src/Twig/TwigPreLexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@ public function preLexComponents(string $input, bool $insideOfBlock = false): st
}
}

if ($this->consume('<twig:')) {
$componentName = $this->consumeComponentName();
$isTwigHtmlOpening = $this->consume('<twig:');
$isTraditionalBlockOpening = false;
if ($isTwigHtmlOpening || (0 !== \count($this->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
Expand All @@ -63,6 +65,15 @@ public function preLexComponents(string $input, bool $insideOfBlock = false): st
$this->currentComponents[\count($this->currentComponents) - 1]['hasDefaultBlock'] = false;
}

if ($isTraditionalBlockOpening) {
// add what we've consumed so far
$output .= '{% block';
$output .= $this->consumeUntil('%}');
$output .= $this->consumeUntilEndBlock();

continue;
}

$output .= $this->consumeBlock($componentName);

continue;
Expand Down Expand Up @@ -119,7 +130,7 @@ public function preLexComponents(string $input, bool $insideOfBlock = false): st
continue;
}

$char = $this->consumeChar();
$char = $this->input[$this->position];
if ("\n" === $char) {
++$this->line;
}
Expand All @@ -128,11 +139,13 @@ public function preLexComponents(string $input, bool $insideOfBlock = false): st
if (!empty($this->currentComponents)
&& !$this->currentComponents[\count($this->currentComponents) - 1]['hasDefaultBlock']
&& preg_match('/\S/', $char)
&& !$this->check('{% block')
) {
$output .= $this->addDefaultBlock();
}

$output .= $char;
$this->consumeChar();
}

if (!empty($this->currentComponents)) {
Expand Down Expand Up @@ -374,10 +387,24 @@ private function consumeUntilEndBlock(): string
}
}

if ('{% 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;
break;
} else {
--$depth;
}
}

if ('<twig:block' === substr($this->input, $this->position, 11)) {
++$depth;
}

if ('{% block' === substr($this->input, $this->position, 8)) {
++$depth;
}

if ("\n" === $this->input[$this->position]) {
++$this->line;
}
Expand Down
10 changes: 10 additions & 0 deletions src/TwigComponent/tests/Unit/TwigPreLexerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ public function getLexTests(): iterable
'{% component \'foo\' %}{% block foo_block %}Foo{% endblock %}{% endcomponent %}',
];

yield 'component_with_traditional_block' => [
'<twig:foo>{% block foo_block %}Foo{% endblock %}</twig:foo>',
'{% component \'foo\' %}{% block foo_block %}Foo{% endblock %}{% endcomponent %}',
];

yield 'traditional_blocks_around_component_do_not_confuse' => [
'Hello {% block foo_block %}Foo{% endblock %}<twig:foo />{% block bar_block %}Bar{% endblock %}',
'Hello {% block foo_block %}Foo{% endblock %}{{ component(\'foo\') }}{% block bar_block %}Bar{% endblock %}',
];

yield 'component_with_embedded_component_inside_block' => [
'<twig:foo><twig:block name="foo_block"><twig:bar /></twig:block></twig:foo>',
'{% component \'foo\' %}{% block foo_block %}{{ component(\'bar\') }}{% endblock %}{% endcomponent %}',
Expand Down

0 comments on commit 8700c90

Please sign in to comment.