Add support for yielding from a generator in PrintNode

This commit is contained in:
Fabien Potencier
2024-07-22 13:46:32 +02:00
parent 3c52cd9814
commit 27f6171f2f
6 changed files with 57 additions and 4 deletions
+1
View File
@@ -1,5 +1,6 @@
# 3.11.0 (2024-XX-XX)
* Add the possibility to yield from a generator in `PrintNode`
* Add the `shuffle` filter
* Add the `singular` and `plural` filters in `StringExtension`
* Deprecate the second argument of `Twig\Node\Expression\CallExpression::compileArguments()`
@@ -21,4 +21,8 @@ use Twig\Node\Node;
*/
abstract class AbstractExpression extends Node
{
public function isGenerator(): bool
{
return $this->hasAttribute('is_generator') && $this->getAttribute('is_generator');
}
}
+2 -3
View File
@@ -31,10 +31,9 @@ class PrintNode extends Node implements NodeOutputInterface
public function compile(Compiler $compiler): void
{
$compiler->addDebugInfo($this);
$compiler
->write('yield ')
->addDebugInfo($this)
->write($this->getNode('expr')->isGenerator() ? 'yield from ' : 'yield ')
->subcompile($this->getNode('expr'))
->raw(";\n")
;
+1 -1
View File
@@ -119,7 +119,7 @@ final class SandboxNodeVisitor implements NodeVisitorInterface
private function wrapNode(Node $node, string $name): void
{
$expr = $node->getNode($name);
if ($expr instanceof NameExpression || $expr instanceof GetAttrExpression) {
if (($expr instanceof NameExpression || $expr instanceof GetAttrExpression) && !$expr->isGenerator()) {
$node->setNode($name, new CheckToStringNode($expr));
}
}
+9
View File
@@ -12,7 +12,10 @@ namespace Twig\Tests\Node;
*/
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\GetAttrExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\PrintNode;
use Twig\Template;
use Twig\Test\NodeTestCase;
class PrintTest extends NodeTestCase
@@ -30,6 +33,12 @@ class PrintTest extends NodeTestCase
$tests = [];
$tests[] = [new PrintNode(new ConstantExpression('foo', 1), 1), "// line 1\nyield \"foo\";"];
$expr = new NameExpression('foo', 1);
$attr = new ConstantExpression('bar', 1);
$node = new GetAttrExpression($expr, $attr, null, Template::METHOD_CALL, 1);
$node->setAttribute('is_generator', true);
$tests[] = [new PrintNode($node, 1), "// line 1\nyield from CoreExtension::getAttribute(\$this->env, \$this->source, (\$context[\"foo\"] ?? null), \"bar\", [], \"method\", false, false, false, 1);"];
return $tests;
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Twig\Tests\NodeVisitor;
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use PHPUnit\Framework\TestCase;
use Twig\Environment;
use Twig\Loader\ArrayLoader;
use Twig\Node\CheckToStringNode;
use Twig\Node\Expression\NameExpression;
use Twig\Node\ModuleNode;
use Twig\Node\Node;
use Twig\Node\PrintNode;
use Twig\NodeTraverser;
use Twig\NodeVisitor\SandboxNodeVisitor;
use Twig\Source;
class SandboxTest extends TestCase
{
public function testGeneratorExpression()
{
$env = new Environment(new ArrayLoader());
$expr = new NameExpression('foo', 1);
$expr->setAttribute('is_generator', true);
$node = new ModuleNode(new PrintNode($expr, 1), null, new Node(), new Node(), new Node(), new Node([]), new Source('foo', 'foo'));
$traverser = new NodeTraverser($env, [new SandboxNodeVisitor($env)]);
$node = $traverser->traverse($node);
$this->assertNotInstanceOf(CheckToStringNode::class, $node->getNode('body')->getNode('expr'));
$this->assertSame("// line 1\nyield from (\$context[\"foo\"] ?? null);\n", $env->compile($node->getNode('body')));
}
}