Repository: twigphp/Twig (php)

Issue:
Warning in compiled Twig template : "Warning: Undefined variable $blocks"
With the version 3.9.3, I get this warning when I'm using variable on cache tag on my symfony project (version 6.4.7)
> An exception has been thrown during the rendering of a template ("Warning: Undefined variable $blocks").

You can find bellow a small example 
```
{% cache 'project_%s_%s'|format(project.id, project.updatedAt.timestamp) ttl(36000) %}
   {% set list %}
      <div class="list-group list-group-flush"></div>
   {% endset %}
   {{ list }}
{% endcache %}
```

Here is my composer.json file about twig packages with the current versions in my project

> "twig/cache-extra": "3.9.0",
> "twig/cssinliner-extra": "3.9.0",
> "twig/extra-bundle": "3.9.3",
> "twig/intl-extra": "3.9.2",
> "twig/string-extra": "3.9.0",
> "twig/twig": "3.9.3"

![Capture d’écran 2024-05-07 à 16 40 12](https://github.com/twigphp/Twig/assets/4007104/359c3864-5080-4d77-b435-93ce6a41caef)

Hope it can help


Pull request:
Fix blocks not available under some circumstancies
Fix #4079

Patch:
diff --git a/extra/cache-extra/Node/CacheNode.php b/extra/cache-extra/Node/CacheNode.php
index de6d964f..5cb73c59 100644
--- a/extra/cache-extra/Node/CacheNode.php
+++ b/extra/cache-extra/Node/CacheNode.php
@@ -40,7 +40,7 @@ class CacheNode extends AbstractExpression
             ->addDebugInfo($this)
             ->raw('$this->env->getRuntime(\'Twig\Extra\Cache\CacheRuntime\')->getCache()->get(')
             ->subcompile($this->getNode('key'))
-            ->raw(", function (\Symfony\Contracts\Cache\ItemInterface \$item) use (\$context, \$macros) {\n")
+            ->raw(", function (\Symfony\Contracts\Cache\ItemInterface \$item) use (\$context, \$macros, \$blocks) {\n")
             ->indent()
         ;
 
diff --git a/src/Node/CaptureNode.php b/src/Node/CaptureNode.php
index 068dcfaa..d7db218c 100644
--- a/src/Node/CaptureNode.php
+++ b/src/Node/CaptureNode.php
@@ -24,7 +24,7 @@ class CaptureNode extends Node
 {
     public function __construct(Node $body, int $lineno, ?string $tag = null)
     {
-        parent::__construct(['body' => $body], ['raw' => false, 'with_blocks' => false], $lineno, $tag);
+        parent::__construct(['body' => $body], ['raw' => false], $lineno, $tag);
     }
 
     public function compile(Compiler $compiler): void
@@ -34,13 +34,9 @@ class CaptureNode extends Node
         if (!$this->getAttribute('raw')) {
             $compiler->raw("('' === \$tmp = ");
         }
-        $compiler->raw($useYield ? "implode('', iterator_to_array(" : '\\Twig\\Extension\\CoreExtension::captureOutput(');
-        if ($this->getAttribute('with_blocks')) {
-            $compiler->raw("(function () use (&\$context, \$macros, \$blocks) {\n");
-        } else {
-            $compiler->raw("(function () use (&\$context, \$macros) {\n");
-        }
         $compiler
+            ->raw($useYield ? "implode('', iterator_to_array(" : '\\Twig\\Extension\\CoreExtension::captureOutput(')
+            ->raw("(function () use (&\$context, \$macros, \$blocks) {\n")
             ->indent()
             ->subcompile($this->getNode('body'))
             ->write("return; yield '';\n")
@@ -53,7 +49,8 @@ class CaptureNode extends Node
             $compiler->raw(')');
         }
         if (!$this->getAttribute('raw')) {
-            $compiler->raw(") ? '' : new Markup(\$tmp, \$this->env->getCharset());");
+            $compiler->raw(") ? '' : new Markup(\$tmp, \$this->env->getCharset())");
         }
+        $compiler->raw(';');
     }
 }
diff --git a/src/Node/MacroNode.php b/src/Node/MacroNode.php
index 761ef55d..78e1b6f7 100644
--- a/src/Node/MacroNode.php
+++ b/src/Node/MacroNode.php
@@ -80,7 +80,6 @@ class MacroNode extends Node
         }
 
         $node = new CaptureNode($this->getNode('body'), $this->getNode('body')->lineno, $this->getNode('body')->tag);
-        $node->setAttribute('with_blocks', true);
 
         $compiler
             ->write('')
diff --git a/src/Node/SetNode.php b/src/Node/SetNode.php
index 6b4c873e..0900f154 100644
--- a/src/Node/SetNode.php
+++ b/src/Node/SetNode.php
@@ -38,7 +38,6 @@ class SetNode extends Node implements NodeCaptureInterface
                 $capture = false;
             } else {
                 $values = new CaptureNode($values, $values->getTemplateLine());
-                $values->setAttribute('with_blocks', true);
             }
         }
 


Test patch:
diff --git a/extra/cache-extra/Tests/Fixtures/cache_complex.test b/extra/cache-extra/Tests/Fixtures/cache_complex.test
new file mode 100644
index 00000000..5a2e28f4
--- /dev/null
+++ b/extra/cache-extra/Tests/Fixtures/cache_complex.test
@@ -0,0 +1,15 @@
+--TEST--
+"cache" tag
+--TEMPLATE--
+{% cache 'test_%s_%s'|format(10, 10000) ttl(36000) %}
+   {% set content %}
+      OK
+   {% endset %}
+   {% apply spaceless %}
+       {{ content }}
+    {% endapply %}
+{% endcache %}
+--DATA--
+return []
+--EXPECT--
+OK
diff --git a/extra/cache-extra/Tests/Fixtures/cache_with_blocks.test b/extra/cache-extra/Tests/Fixtures/cache_with_blocks.test
new file mode 100644
index 00000000..79721d7f
--- /dev/null
+++ b/extra/cache-extra/Tests/Fixtures/cache_with_blocks.test
@@ -0,0 +1,15 @@
+--TEST--
+"cache" tag
+--TEMPLATE--
+{% extends "layout.twig" %}
+{% block bar %}
+    {% cache "foo" %}
+        {%- block content %}FOO{% endblock %}
+    {% endcache %}
+{% endblock %}
+--TEMPLATE(layout.twig)--
+{% block content %}{% endblock %}
+--DATA--
+return []
+--EXPECT--
+FOO