Skip to content

Commit 7ecfb65

Browse files
committed
Allow Twig 3.9
1 parent 39b3be1 commit 7ecfb65

File tree

7 files changed

+101
-61
lines changed

7 files changed

+101
-61
lines changed

src/TwigComponent/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"symfony/deprecation-contracts": "^2.2|^3.0",
3232
"symfony/event-dispatcher": "^5.4|^6.0|^7.0",
3333
"symfony/property-access": "^5.4|^6.0|^7.0",
34-
"twig/twig": "~3.8.0"
34+
"twig/twig": "^3.8"
3535
},
3636
"require-dev": {
3737
"symfony/console": "^5.4|^6.0|^7.0",

src/TwigComponent/src/Twig/ComponentNode.php

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,22 @@
1212
namespace Symfony\UX\TwigComponent\Twig;
1313

1414
use Symfony\UX\TwigComponent\BlockStack;
15+
use Twig\Attribute\YieldReady;
1516
use Twig\Compiler;
17+
use Twig\Environment;
1618
use Twig\Extension\CoreExtension;
1719
use Twig\Node\Expression\AbstractExpression;
1820
use Twig\Node\Node;
21+
use Twig\Node\NodeOutputInterface;
1922

2023
/**
2124
* @author Fabien Potencier <fabien@symfony.com>
2225
* @author Kevin Bond <kevinbond@gmail.com>
2326
*
2427
* @internal
2528
*/
26-
final class ComponentNode extends Node
29+
#[YieldReady]
30+
final class ComponentNode extends Node implements NodeOutputInterface
2731
{
2832
public function __construct(string $component, string $embeddedTemplateName, int $embeddedTemplateIndex, ?AbstractExpression $props, bool $only, int $lineno, string $tag)
2933
{
@@ -64,24 +68,25 @@ public function compile(Compiler $compiler): void
6468
->string($this->getAttribute('component'))
6569
->raw(', ')
6670
->raw($twig_to_array)
67-
->raw('(')
68-
;
71+
->raw('(');
6972
$this->writeProps($compiler)
7073
->raw(')')
71-
->raw(");\n")
72-
;
74+
->raw(");\n");
7375

7476
$compiler
7577
->write('if (null !== $preRendered) {')
7678
->raw("\n")
77-
->indent()
78-
->write('echo $preRendered;')
79-
->raw("\n")
79+
->indent();
80+
if (method_exists(Environment::class, 'useYield') && $compiler->getEnvironment()->useYield()) {
81+
$compiler->write('yield from $preRendered; ');
82+
} else {
83+
$compiler->write('echo $preRendered; ');
84+
}
85+
$compiler->raw("\n")
8086
->outdent()
8187
->write('} else {')
8288
->raw("\n")
83-
->indent()
84-
;
89+
->indent();
8590

8691
/*
8792
* Block 2) Create the component & return render info
@@ -97,17 +102,15 @@ public function compile(Compiler $compiler): void
97102
->string($this->getAttribute('component'))
98103
->raw(', ')
99104
->raw($twig_to_array)
100-
->raw('(')
101-
;
105+
->raw('(');
102106
$this->writeProps($compiler)
103107
->raw('), ')
104108
->raw($this->getAttribute('only') ? '[]' : '$context')
105109
->raw(', ')
106110
->string(TemplateNameParser::parse($this->getAttribute('embedded_template')))
107111
->raw(', ')
108112
->raw($this->getAttribute('embedded_index'))
109-
->raw(");\n")
110-
;
113+
->raw(");\n");
111114
$compiler
112115
->write('$embeddedContext = $preRenderEvent->getVariables();')
113116
->raw("\n")
@@ -121,8 +124,7 @@ public function compile(Compiler $compiler): void
121124
// happens to contain a {% component %} tag. So we don't need to worry
122125
// about trying to allow a specific embedded template to be targeted.
123126
->write('$embeddedContext["__parent__"] = $preRenderEvent->getTemplate();')
124-
->raw("\n")
125-
;
127+
->raw("\n");
126128

127129
/*
128130
* Block 3) Add & update the block stack
@@ -143,14 +145,17 @@ public function compile(Compiler $compiler): void
143145
->string('outerBlocks')
144146
->raw(']->convert($blocks, ')
145147
->raw($this->getAttribute('embedded_index'))
146-
->raw(");\n")
147-
;
148+
->raw(");\n");
148149

149150
/*
150151
* Block 4) Render the component template
151152
*
152153
* This will actually render the child component template.
153154
*/
155+
if (method_exists(Environment::class, 'useYield') && $compiler->getEnvironment()->useYield()) {
156+
$compiler
157+
->write('yield from ');
158+
}
154159
$compiler
155160
->write('$this->loadTemplate(')
156161
->string($this->getAttribute('embedded_template'))
@@ -160,10 +165,16 @@ public function compile(Compiler $compiler): void
160165
->repr($this->getTemplateLine())
161166
->raw(', ')
162167
->string($this->getAttribute('embedded_index'))
163-
->raw(')')
164-
->raw('->display($embeddedContext, $embeddedBlocks);')
165-
->raw("\n")
166-
;
168+
->raw(')');
169+
170+
if (method_exists(Environment::class, 'useYield') && $compiler->getEnvironment()->useYield()) {
171+
$compiler->raw('->unwrap()->yield(');
172+
} else {
173+
$compiler->raw('->display(');
174+
}
175+
$compiler
176+
->raw('$embeddedContext, $embeddedBlocks')
177+
->raw(");\n");
167178

168179
$compiler->write('$this->extensions[')
169180
->string(ComponentExtension::class)

src/TwigComponent/src/Twig/PropsNode.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\UX\TwigComponent\Twig;
1313

14+
use Twig\Attribute\YieldReady;
1415
use Twig\Compiler;
1516
use Twig\Node\Node;
1617

@@ -19,6 +20,7 @@
1920
*
2021
* @internal
2122
*/
23+
#[YieldReady]
2224
class PropsNode extends Node
2325
{
2426
public function __construct(array $propsNames, array $values, $lineno = 0, ?string $tag = null)
@@ -47,35 +49,50 @@ public function compile(Compiler $compiler): void
4749

4850
$compiler
4951
->write('$propsNames[] = \''.$name.'\';')
52+
->write("\n")
5053
->write('$context[\'attributes\'] = $context[\'attributes\']->remove(\''.$name.'\');')
54+
->write("\n")
5155
->write('if (!isset($context[\''.$name.'\'])) {');
5256

5357
if (!$this->hasNode($name)) {
5458
$compiler
59+
->indent()
5560
->write('throw new \Twig\Error\RuntimeError("'.$name.' should be defined for component '.$this->getTemplateName().'");')
56-
->write('}');
61+
->write("\n")
62+
->outdent()
63+
->write('}')
64+
->write("\n");
5765

5866
continue;
5967
}
6068

6169
$compiler
70+
->indent()
6271
->write('$context[\''.$name.'\'] = ')
6372
->subcompile($this->getNode($name))
6473
->raw(";\n")
65-
->write('}');
74+
->outdent()
75+
->write('}')
76+
->write("\n");
6677
}
6778

6879
$compiler
6980
->write('$attributesKeys = array_keys($context[\'attributes\']->all());')
7081
->raw("\n")
7182
->write('foreach ($context as $key => $value) {')
7283
->raw("\n")
84+
->indent()
7385
->write('if (in_array($key, $attributesKeys) && !in_array($key, $propsNames)) {')
7486
->raw("\n")
87+
->indent()
7588
->raw('unset($context[$key]);')
7689
->raw("\n")
90+
->outdent()
7791
->write('}')
92+
->raw("\n")
93+
->outdent()
7894
->write('}')
95+
->raw("\n")
7996
;
8097

8198
// overwrite the context value if a props with a similar name and a default value exist
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
<twig:DivComponent6/>
2-
32
<twig:DivComponent6>Override content<twig:block name="foo">Override foo</twig:block></twig:DivComponent6>
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
<twig:table caption='data table' :headers='["key", "value"]' :data='[[1, 2], [3, 4]]'>
22
<twig:block name="th">custom th ({{ parent() }})</twig:block>
33
<twig:block name="td">custom td ({{ parent() }})</twig:block>
4-
54
<twig:block name="footer">
65
My footer
76
</twig:block>
8-
</twig:table>
7+
</twig:table>

src/TwigComponent/tests/Integration/ComponentLexerTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ public function testComponentSyntaxOpenTags(): void
3232
public function testComponentSyntaxSelfCloseTags(): void
3333
{
3434
$output = self::getContainer()->get(Environment::class)->render('tags/self_close_tag.html.twig');
35-
3635
$this->assertStringContainsString('propA: 1', $output);
3736
$this->assertStringContainsString('propB: hello', $output);
3837
}

0 commit comments

Comments
 (0)