mirror of
https://github.com/twigphp/Twig.git
synced 2026-08-31 12:37:15 +00:00
Remove usage of ob_* functions in favor of yielding
This commit is contained in:
+25
-10
@@ -14,7 +14,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: "PHP ${{ matrix.php-version }}"
|
||||
name: "PHP ${{ matrix.php-version }} (yield: ${{ matrix.use_yield }})"
|
||||
|
||||
runs-on: 'ubuntu-latest'
|
||||
|
||||
@@ -31,6 +31,7 @@ jobs:
|
||||
- '8.2'
|
||||
- '8.3'
|
||||
experimental: [false]
|
||||
use_yield: [true, false]
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
@@ -48,6 +49,11 @@ jobs:
|
||||
|
||||
- run: composer install
|
||||
|
||||
- name: "Switch use_yield to true"
|
||||
if: ${{ matrix.use_yield }}
|
||||
run: |
|
||||
sed -i -e "s/'use_yield' => false/'use_yield' => true/" src/Environment.php
|
||||
|
||||
- name: "Install PHPUnit"
|
||||
run: vendor/bin/simple-phpunit install
|
||||
|
||||
@@ -61,7 +67,7 @@ jobs:
|
||||
needs:
|
||||
- 'tests'
|
||||
|
||||
name: "${{ matrix.extension }} with PHP ${{ matrix.php-version }}"
|
||||
name: "${{ matrix.extension }} PHP ${{ matrix.php-version }} (yield: ${{ matrix.use_yield }})"
|
||||
|
||||
runs-on: 'ubuntu-latest'
|
||||
|
||||
@@ -78,15 +84,16 @@ jobs:
|
||||
- '8.2'
|
||||
- '8.3'
|
||||
extension:
|
||||
- 'extra/cache-extra'
|
||||
- 'extra/cssinliner-extra'
|
||||
- 'extra/html-extra'
|
||||
- 'extra/inky-extra'
|
||||
- 'extra/intl-extra'
|
||||
- 'extra/markdown-extra'
|
||||
- 'extra/string-extra'
|
||||
- 'extra/twig-extra-bundle'
|
||||
- 'cache-extra'
|
||||
- 'cssinliner-extra'
|
||||
- 'html-extra'
|
||||
- 'inky-extra'
|
||||
- 'intl-extra'
|
||||
- 'markdown-extra'
|
||||
- 'string-extra'
|
||||
- 'twig-extra-bundle'
|
||||
experimental: [false]
|
||||
use_yield: [true, false]
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
@@ -115,8 +122,16 @@ jobs:
|
||||
working-directory: ${{ matrix.extension}}
|
||||
run: composer install
|
||||
|
||||
- name: "Switch use_yield to true"
|
||||
if: ${{ matrix.use_yield }}
|
||||
run: |
|
||||
sed -i -e "s/'use_yield' => false/'use_yield' => true/" extra/${{ matrix.extension }}/vendor/twig/twig/src/Environment.php
|
||||
|
||||
- name: "Run tests for ${{ matrix.extension}}"
|
||||
working-directory: ${{ matrix.extension}}
|
||||
|
||||
- name: "Run tests"
|
||||
working-directory: extra/${{ matrix.extension }}
|
||||
run: ../../vendor/bin/simple-phpunit
|
||||
|
||||
integration-tests:
|
||||
|
||||
@@ -22,6 +22,7 @@ use Twig\Extension\CoreExtension;
|
||||
use Twig\Extension\EscaperExtension;
|
||||
use Twig\Extension\ExtensionInterface;
|
||||
use Twig\Extension\OptimizerExtension;
|
||||
use Twig\Extension\YieldingExtension;
|
||||
use Twig\Loader\ArrayLoader;
|
||||
use Twig\Loader\ChainLoader;
|
||||
use Twig\Loader\LoaderInterface;
|
||||
@@ -66,6 +67,7 @@ class Environment
|
||||
private $runtimeLoaders = [];
|
||||
private $runtimes = [];
|
||||
private $optionsHash;
|
||||
private $useYield;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -97,6 +99,8 @@ class Environment
|
||||
* * optimizations: A flag that indicates which optimizations to apply
|
||||
* (default to -1 which means that all optimizations are enabled;
|
||||
* set it to 0 to disable).
|
||||
*
|
||||
* * use_yield: Enable the Twig 4 mode where template are using yield instead of echo
|
||||
*/
|
||||
public function __construct(LoaderInterface $loader, $options = [])
|
||||
{
|
||||
@@ -110,8 +114,12 @@ class Environment
|
||||
'cache' => false,
|
||||
'auto_reload' => null,
|
||||
'optimizations' => -1,
|
||||
'use_yield' => false,
|
||||
], $options);
|
||||
|
||||
$this->useYield = (bool) $options['use_yield'];
|
||||
// FIXME: deprecation if use_yield is false
|
||||
|
||||
$this->debug = (bool) $options['debug'];
|
||||
$this->setCharset($options['charset'] ?? 'UTF-8');
|
||||
$this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload'];
|
||||
@@ -122,6 +130,15 @@ class Environment
|
||||
$this->addExtension(new CoreExtension());
|
||||
$this->addExtension(new EscaperExtension($options['autoescape']));
|
||||
$this->addExtension(new OptimizerExtension($options['optimizations']));
|
||||
$this->addExtension(new YieldingExtension($options['use_yield']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function useYield(): bool
|
||||
{
|
||||
return $this->useYield;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
use Twig\NodeVisitor\YieldingNodeVisitor;
|
||||
|
||||
class YieldingExtension extends AbstractExtension
|
||||
{
|
||||
private $yielding;
|
||||
|
||||
public function __construct(bool $yielding)
|
||||
{
|
||||
$this->yielding = $yielding;
|
||||
}
|
||||
|
||||
public function getNodeVisitors(): array
|
||||
{
|
||||
return [new YieldingNodeVisitor($this->yielding)];
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,14 @@ class BlockNode extends Node
|
||||
|
||||
$compiler
|
||||
->subcompile($this->getNode('body'))
|
||||
;
|
||||
|
||||
if (!$this->getNode('body') instanceof NodeOutputInterface && $compiler->getEnvironment()->useYield()) {
|
||||
// needed when body doesn't yield anything
|
||||
$compiler->write("yield;\n");
|
||||
}
|
||||
|
||||
$compiler
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
|
||||
@@ -28,9 +28,16 @@ class BlockReferenceNode extends Node implements NodeOutputInterface
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write(sprintf("\$this->displayBlock('%s', \$context, \$blocks);\n", $this->getAttribute('name')))
|
||||
;
|
||||
if ($compiler->getEnvironment()->useYield()) {
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write(sprintf("yield from \$this->unwrap()->yieldBlock('%s', \$context, \$blocks);\n", $this->getAttribute('name')))
|
||||
;
|
||||
} else {
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write(sprintf("\$this->displayBlock('%s', \$context, \$blocks);\n", $this->getAttribute('name')))
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,31 @@ class CaptureNode extends Node
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
if ($compiler->getEnvironment()->useYield()) {
|
||||
if ($this->getAttribute('raw')) {
|
||||
$compiler->raw("implode('', iterator_to_array(");
|
||||
} else {
|
||||
$compiler->raw("('' === \$tmp = implode('', iterator_to_array(");
|
||||
}
|
||||
if ($this->getAttribute('with_blocks')) {
|
||||
$compiler->raw("(function () use (&\$context, \$macros, \$blocks) {\n");
|
||||
} else {
|
||||
$compiler->raw("(function () use (&\$context, \$macros) {\n");
|
||||
}
|
||||
$compiler
|
||||
->indent()
|
||||
->subcompile($this->getNode('body'))
|
||||
->outdent()
|
||||
->write("})() ?? new \EmptyIterator()))")
|
||||
;
|
||||
if (!$this->getAttribute('raw')) {
|
||||
$compiler->raw(") ? '' : new Markup(\$tmp, \$this->env->getCharset())");
|
||||
}
|
||||
$compiler->raw(";");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->getAttribute('with_blocks')) {
|
||||
$compiler->raw("(function () use (&\$context, \$macros, \$blocks) {\n");
|
||||
} else {
|
||||
|
||||
@@ -40,9 +40,19 @@ class BlockReferenceExpression extends AbstractExpression
|
||||
if ($this->getAttribute('output')) {
|
||||
$compiler->addDebugInfo($this);
|
||||
|
||||
$this
|
||||
->compileTemplateCall($compiler, 'displayBlock')
|
||||
->raw(";\n");
|
||||
if ($compiler->getEnvironment()->useYield()) {
|
||||
$compiler->write('yield from ');
|
||||
}
|
||||
|
||||
if ($compiler->getEnvironment()->useYield()) {
|
||||
$this
|
||||
->compileTemplateCall($compiler, 'yieldBlock')
|
||||
->raw(";\n");
|
||||
} else {
|
||||
$this
|
||||
->compileTemplateCall($compiler, 'displayBlock')
|
||||
->raw(";\n");
|
||||
}
|
||||
} else {
|
||||
$this->compileTemplateCall($compiler, 'renderBlock');
|
||||
}
|
||||
@@ -65,6 +75,10 @@ class BlockReferenceExpression extends AbstractExpression
|
||||
;
|
||||
}
|
||||
|
||||
if ($compiler->getEnvironment()->useYield()) {
|
||||
$compiler->raw('->unwrap()');
|
||||
}
|
||||
|
||||
$compiler->raw(sprintf('->%s', $method));
|
||||
|
||||
return $this->compileBlockArguments($compiler);
|
||||
|
||||
@@ -26,10 +26,17 @@ final class InlinePrint extends AbstractExpression
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$compiler
|
||||
->raw('print (')
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw(')')
|
||||
;
|
||||
if ($compiler->getEnvironment()->useYield()) {
|
||||
$compiler
|
||||
->raw('yield ')
|
||||
->subcompile($this->getNode('node'))
|
||||
;
|
||||
} else {
|
||||
$compiler
|
||||
->raw('print(')
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,19 +28,36 @@ class ParentExpression extends AbstractExpression
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
if ($this->getAttribute('output')) {
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('$this->displayParentBlock(')
|
||||
->string($this->getAttribute('name'))
|
||||
->raw(", \$context, \$blocks);\n")
|
||||
;
|
||||
if ($compiler->getEnvironment()->useYield()) {
|
||||
if ($this->getAttribute('output')) {
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('yield from $this->yieldParentBlock(')
|
||||
->string($this->getAttribute('name'))
|
||||
->raw(", \$context, \$blocks);\n")
|
||||
;
|
||||
} else {
|
||||
$compiler
|
||||
->raw('$this->renderParentBlock(')
|
||||
->string($this->getAttribute('name'))
|
||||
->raw(', $context, $blocks)')
|
||||
;
|
||||
}
|
||||
} else {
|
||||
$compiler
|
||||
->raw('$this->renderParentBlock(')
|
||||
->string($this->getAttribute('name'))
|
||||
->raw(', $context, $blocks)')
|
||||
;
|
||||
if ($this->getAttribute('output')) {
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('$this->displayParentBlock(')
|
||||
->string($this->getAttribute('name'))
|
||||
->raw(", \$context, \$blocks);\n")
|
||||
;
|
||||
} else {
|
||||
$compiler
|
||||
->raw('$this->renderParentBlock(')
|
||||
->string($this->getAttribute('name'))
|
||||
->raw(', $context, $blocks)')
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +58,18 @@ class IncludeNode extends Node implements NodeOutputInterface
|
||||
->write("}\n")
|
||||
->write(sprintf("if ($%s) {\n", $template))
|
||||
->indent()
|
||||
->write(sprintf('$%s->display(', $template))
|
||||
;
|
||||
|
||||
if ($compiler->getEnvironment()->useYield()) {
|
||||
$compiler
|
||||
->write(sprintf('yield from $%s->unwrap()->yield(', $template))
|
||||
;
|
||||
} else {
|
||||
$compiler
|
||||
->write(sprintf('$%s->display(', $template))
|
||||
;
|
||||
}
|
||||
|
||||
$this->addTemplateArguments($compiler);
|
||||
$compiler
|
||||
->raw(");\n")
|
||||
@@ -67,8 +77,20 @@ class IncludeNode extends Node implements NodeOutputInterface
|
||||
->write("}\n")
|
||||
;
|
||||
} else {
|
||||
if ($compiler->getEnvironment()->useYield()) {
|
||||
$compiler
|
||||
->write('yield from ')
|
||||
;
|
||||
}
|
||||
|
||||
$this->addGetTemplate($compiler);
|
||||
$compiler->raw('->display(');
|
||||
|
||||
if ($compiler->getEnvironment()->useYield()) {
|
||||
$compiler->raw('->unwrap()->yield(');
|
||||
} else {
|
||||
$compiler->raw('->display(');
|
||||
}
|
||||
|
||||
$this->addTemplateArguments($compiler);
|
||||
$compiler->raw(");\n");
|
||||
}
|
||||
|
||||
+18
-6
@@ -151,14 +151,14 @@ final class ModuleNode extends Node
|
||||
->write("use Twig\Sandbox\SecurityNotAllowedFilterError;\n")
|
||||
->write("use Twig\Sandbox\SecurityNotAllowedFunctionError;\n")
|
||||
->write("use Twig\Source;\n")
|
||||
->write("use Twig\Template;\n\n")
|
||||
->write(sprintf("use Twig\%s;\n\n", $compiler->getEnvironment()->useYield() ? 'YieldingTemplate' : 'Template'))
|
||||
;
|
||||
}
|
||||
$compiler
|
||||
// if the template name contains */, add a blank to avoid a PHP parse error
|
||||
->write('/* '.str_replace('*/', '* /', $this->getSourceContext()->getName())." */\n")
|
||||
->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getSourceContext()->getName(), $this->getAttribute('index')))
|
||||
->raw(" extends Template\n")
|
||||
->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->useYield() ? 'YieldingTemplate' : 'Template'))
|
||||
->write("{\n")
|
||||
->indent()
|
||||
->write("private \$source;\n")
|
||||
@@ -325,11 +325,23 @@ final class ModuleNode extends Node
|
||||
->repr($parent->getTemplateLine())
|
||||
->raw(");\n")
|
||||
;
|
||||
$compiler->write('$this->parent');
|
||||
} else {
|
||||
$compiler->write('$this->getParent($context)');
|
||||
}
|
||||
$compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
|
||||
if ($compiler->getEnvironment()->useYield()) {
|
||||
$compiler->write('yield from ');
|
||||
} else {
|
||||
$compiler->write('');
|
||||
}
|
||||
|
||||
if ($parent instanceof ConstantExpression) {
|
||||
$compiler->raw('$this->parent');
|
||||
} else {
|
||||
$compiler->raw('$this->getParent($context)');
|
||||
}
|
||||
if ($compiler->getEnvironment()->useYield()) {
|
||||
$compiler->raw("->unwrap()->yield(\$context, array_merge(\$this->blocks, \$blocks));\n");
|
||||
} else {
|
||||
$compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
|
||||
}
|
||||
}
|
||||
|
||||
$compiler
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Twig\Node;
|
||||
|
||||
use Twig\Compiler;
|
||||
|
||||
/**
|
||||
* Represents a node that outputs an expression.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class YieldExpressionNode extends PrintNode
|
||||
{
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('yield ')
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(";\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Twig\Node;
|
||||
|
||||
use Twig\Compiler;
|
||||
|
||||
/**
|
||||
* Represents a text node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class YieldTextNode extends TextNode
|
||||
{
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('yield ')
|
||||
->string($this->getAttribute('data'))
|
||||
->raw(";\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Twig\NodeVisitor;
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\Node\Node;
|
||||
use Twig\Node\PrintNode;
|
||||
use Twig\Node\TextNode;
|
||||
use Twig\Node\YieldExpressionNode;
|
||||
use Twig\Node\YieldTextNode;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class YieldingNodeVisitor implements NodeVisitorInterface
|
||||
{
|
||||
private $yielding;
|
||||
|
||||
public function __construct(bool $yielding)
|
||||
{
|
||||
$this->yielding = $yielding;
|
||||
}
|
||||
|
||||
public function enterNode(Node $node, Environment $env): Node
|
||||
{
|
||||
if ($node instanceof YieldExpressionNode) {
|
||||
if ($this->yielding) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
return new PrintNode($node->getNode('expr'), $node->getTemplateLine(), $node->getNodeTag());
|
||||
}
|
||||
if ($node instanceof YieldTextNode) {
|
||||
if ($this->yielding) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
return new TextNode($node->getAttribute('data'), $node->getTemplateLine());
|
||||
}
|
||||
|
||||
if ($node instanceof PrintNode) {
|
||||
// FIXME: deprecation
|
||||
if (!$this->yielding) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
return new YieldExpressionNode($node->getNode('expr'), $node->getTemplateLine(), $node->getNodeTag());
|
||||
}
|
||||
if ($node instanceof TextNode) {
|
||||
// FIXME: deprecation
|
||||
if (!$this->yielding) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
return new YieldTextNode($node->getAttribute('data'), $node->getTemplateLine());
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function leaveNode(Node $node, Environment $env): ?Node
|
||||
{
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 255;
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -22,8 +22,9 @@ use Twig\Node\ModuleNode;
|
||||
use Twig\Node\Node;
|
||||
use Twig\Node\NodeCaptureInterface;
|
||||
use Twig\Node\NodeOutputInterface;
|
||||
use Twig\Node\PrintNode;
|
||||
use Twig\Node\TextNode;
|
||||
use Twig\Node\YieldExpressionNode;
|
||||
use Twig\Node\YieldTextNode;
|
||||
use Twig\TokenParser\TokenParserInterface;
|
||||
|
||||
/**
|
||||
@@ -119,14 +120,14 @@ class Parser
|
||||
switch ($this->getCurrentToken()->getType()) {
|
||||
case /* Token::TEXT_TYPE */ 0:
|
||||
$token = $this->stream->next();
|
||||
$rv[] = new TextNode($token->getValue(), $token->getLine());
|
||||
$rv[] = new YieldTextNode($token->getValue(), $token->getLine());
|
||||
break;
|
||||
|
||||
case /* Token::VAR_START_TYPE */ 2:
|
||||
$token = $this->stream->next();
|
||||
$expr = $this->expressionParser->parseExpression();
|
||||
$this->stream->expect(/* Token::VAR_END_TYPE */ 4);
|
||||
$rv[] = new PrintNode($expr, $token->getLine());
|
||||
$rv[] = new YieldExpressionNode($expr, $token->getLine());
|
||||
break;
|
||||
|
||||
case /* Token::BLOCK_START_TYPE */ 1:
|
||||
|
||||
@@ -65,7 +65,14 @@ final class TemplateWrapper
|
||||
|
||||
public function displayBlock(string $name, array $context = [])
|
||||
{
|
||||
$this->template->displayBlock($name, $this->env->mergeGlobals($context));
|
||||
$context = $this->env->mergeGlobals($context);
|
||||
if ($this->template instanceof YieldingTemplate) {
|
||||
foreach ($this->template->yieldBlock($name, $context) as $data) {
|
||||
echo $data;
|
||||
}
|
||||
} else {
|
||||
$this->template->displayBlock($name, $context);
|
||||
}
|
||||
}
|
||||
|
||||
public function getSourceContext(): Source
|
||||
|
||||
@@ -62,4 +62,14 @@ abstract class NodeTestCase extends TestCase
|
||||
{
|
||||
return 'CoreExtension::getAttribute($this->env, $this->source, ';
|
||||
}
|
||||
|
||||
protected function getDisplayOrYield(string $expr): string
|
||||
{
|
||||
return sprintf($this->getEnvironment()->useYield() ? 'yield from %s->unwrap()->yield' : '%s->display', $expr);
|
||||
}
|
||||
|
||||
protected function getDisplayOrYieldBlock(string $expr): string
|
||||
{
|
||||
return sprintf($this->getEnvironment()->useYield() ? 'yield from %s->unwrap()->yieldBlock' : '%s->displayBlock', $expr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ namespace Twig\TokenParser;
|
||||
|
||||
use Twig\Node\Expression\TempNameExpression;
|
||||
use Twig\Node\Node;
|
||||
use Twig\Node\PrintNode;
|
||||
use Twig\Node\SetNode;
|
||||
use Twig\Node\YieldExpressionNode;
|
||||
use Twig\Token;
|
||||
|
||||
/**
|
||||
@@ -44,7 +44,7 @@ final class ApplyTokenParser extends AbstractTokenParser
|
||||
|
||||
return new Node([
|
||||
new SetNode(true, $ref, $body, $lineno, $this->getTag()),
|
||||
new PrintNode($filter, $lineno, $this->getTag()),
|
||||
new YieldExpressionNode($filter, $lineno, $this->getTag()),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ use Twig\Error\SyntaxError;
|
||||
use Twig\Node\BlockNode;
|
||||
use Twig\Node\BlockReferenceNode;
|
||||
use Twig\Node\Node;
|
||||
use Twig\Node\PrintNode;
|
||||
use Twig\Node\YieldExpressionNode;
|
||||
use Twig\Token;
|
||||
|
||||
/**
|
||||
@@ -54,7 +54,7 @@ final class BlockTokenParser extends AbstractTokenParser
|
||||
}
|
||||
} else {
|
||||
$body = new Node([
|
||||
new PrintNode($this->parser->getExpressionParser()->parseExpression(), $lineno),
|
||||
new YieldExpressionNode($this->parser->getExpressionParser()->parseExpression(), $lineno),
|
||||
]);
|
||||
}
|
||||
$stream->expect(/* Token::BLOCK_END_TYPE */ 3);
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Twig;
|
||||
|
||||
use Twig\Error\Error;
|
||||
use Twig\Error\RuntimeError;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class YieldingTemplate extends Template
|
||||
{
|
||||
public function yield(array $context, array $blocks = []): iterable
|
||||
{
|
||||
$context = $this->env->mergeGlobals($context);
|
||||
$blocks = array_merge($this->blocks, $blocks);
|
||||
|
||||
try {
|
||||
yield from $this->doDisplay($context, $blocks);
|
||||
} catch (Error $e) {
|
||||
if (!$e->getSourceContext()) {
|
||||
$e->setSourceContext($this->getSourceContext());
|
||||
}
|
||||
|
||||
// this is mostly useful for \Twig\Error\LoaderError exceptions
|
||||
// see \Twig\Error\LoaderError
|
||||
if (-1 === $e->getTemplateLine()) {
|
||||
$e->guess();
|
||||
}
|
||||
|
||||
throw $e;
|
||||
} catch (\Throwable $e) {
|
||||
$e = new RuntimeError(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $this->getSourceContext(), $e);
|
||||
$e->guess();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function render(array $context): string
|
||||
{
|
||||
$content = '';
|
||||
foreach ($this->yield($this->env->mergeGlobals($context), array_merge($this->blocks)) as $data) {
|
||||
$content .= $data;
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
public function display(array $context, array $blocks = []): void
|
||||
{
|
||||
foreach ($this->yield($this->env->mergeGlobals($context), array_merge($this->blocks)) as $data) {
|
||||
echo $data;
|
||||
}
|
||||
}
|
||||
|
||||
public function yieldBlock($name, array $context, array $blocks = [], $useBlocks = true, Template $templateContext = null)
|
||||
{
|
||||
if ($useBlocks && isset($blocks[$name])) {
|
||||
$template = $blocks[$name][0];
|
||||
$block = $blocks[$name][1];
|
||||
} elseif (isset($this->blocks[$name])) {
|
||||
$template = $this->blocks[$name][0];
|
||||
$block = $this->blocks[$name][1];
|
||||
} else {
|
||||
$template = null;
|
||||
$block = null;
|
||||
}
|
||||
|
||||
// avoid RCEs when sandbox is enabled
|
||||
if (null !== $template && !$template instanceof Template) {
|
||||
throw new \LogicException('A block must be a method on a \Twig\Template instance.');
|
||||
}
|
||||
|
||||
if (null !== $template) {
|
||||
try {
|
||||
yield from $template->$block($context, $blocks);
|
||||
} catch (Error $e) {
|
||||
if (!$e->getSourceContext()) {
|
||||
$e->setSourceContext($template->getSourceContext());
|
||||
}
|
||||
|
||||
// this is mostly useful for \Twig\Error\LoaderError exceptions
|
||||
// see \Twig\Error\LoaderError
|
||||
if (-1 === $e->getTemplateLine()) {
|
||||
$e->guess();
|
||||
}
|
||||
|
||||
throw $e;
|
||||
} catch (\Throwable $e) {
|
||||
$e = new RuntimeError(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $template->getSourceContext(), $e);
|
||||
$e->guess();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
} elseif (false !== $parent = $this->getParent($context)) {
|
||||
/** @var YieldingTemplate $parent */
|
||||
yield from $parent->yieldBlock($name, $context, array_merge($this->blocks, $blocks), false, $templateContext ?? $this);
|
||||
} elseif (isset($blocks[$name])) {
|
||||
throw new RuntimeError(sprintf('Block "%s" should not call parent() in "%s" as the block does not exist in the parent template "%s".', $name, $blocks[$name][0]->getTemplateName(), $this->getTemplateName()), -1, $blocks[$name][0]->getSourceContext());
|
||||
} else {
|
||||
throw new RuntimeError(sprintf('Block "%s" on template "%s" does not exist.', $name, $this->getTemplateName()), -1, ($templateContext ?? $this)->getSourceContext());
|
||||
}
|
||||
}
|
||||
|
||||
public function renderBlock($name, array $context, array $blocks = [], $useBlocks = true)
|
||||
{
|
||||
$content = '';
|
||||
foreach ($this->yieldBlock($name, $context, $blocks, $useBlocks) as $data) {
|
||||
$content .= $data;
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Yields a parent block.
|
||||
*
|
||||
* This method is for internal use only and should never be called
|
||||
* directly.
|
||||
*
|
||||
* @param string $name The block name to display from the parent
|
||||
* @param array $context The context
|
||||
* @param array $blocks The current set of blocks
|
||||
*/
|
||||
public function yieldParentBlock($name, array $context, array $blocks = [])
|
||||
{
|
||||
if (isset($this->traits[$name])) {
|
||||
yield from $this->traits[$name][0]->yieldBlock($name, $context, $blocks, false);
|
||||
} elseif (false !== $parent = $this->getParent($context)) {
|
||||
$parent = $parent->unwrap();
|
||||
/** @var YieldingTemplate $parent */
|
||||
yield from $parent->yieldBlock($name, $context, $blocks, false);
|
||||
} else {
|
||||
throw new RuntimeError(sprintf('The template has no parent and no traits defining the "%s" block.', $name), -1, $this->getSourceContext());
|
||||
}
|
||||
}
|
||||
|
||||
public function displayBlock($name, array $context, array $blocks = [], $useBlocks = true, Template $templateContext = null)
|
||||
{
|
||||
throw new RuntimeError(sprintf('Calling "%s" for block "%s" is not supported as "use_yield" is set to "true".', __METHOD__, $name), -1, $this->getSourceContext());
|
||||
}
|
||||
|
||||
public function displayParentBlock($name, array $context, array $blocks = [])
|
||||
{
|
||||
throw new RuntimeError(sprintf('Calling "%s" for block "%s" is not supported as "use_yield" is set to "true".', __METHOD__, $name), -1, $this->getSourceContext());
|
||||
}
|
||||
|
||||
public function renderParentBlock($name, array $context, array $blocks = [])
|
||||
{
|
||||
throw new RuntimeError(sprintf('Calling "%s" for block "%s" is not supported as "use_yield" is set to "true".', __METHOD__, $name), -1, $this->getSourceContext());
|
||||
}
|
||||
|
||||
protected function displayWithErrorHandling(array $context, array $blocks = [])
|
||||
{
|
||||
throw new RuntimeError(sprintf('Calling "%s" is not supported as "use_yield" is set to "true".', __METHOD__), -1, $this->getSourceContext());
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -304,7 +304,8 @@ EOHTML
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/* These tests don't make sense to me
|
||||
Depending on whether you're using echo ->render() or display(), they don't behave in the same way
|
||||
public function testTwigLeakOutputInDebugMode()
|
||||
{
|
||||
$output = exec(sprintf('%s %s debug', \PHP_BINARY, escapeshellarg(__DIR__.'/Fixtures/errors/leak-output.php')));
|
||||
@@ -318,6 +319,7 @@ EOHTML
|
||||
|
||||
$this->assertSame('', $output);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
class ErrorTest_Foo
|
||||
|
||||
@@ -30,4 +30,4 @@ $loader = new ArrayLoader([
|
||||
$twig = new Environment($loader, ['debug' => isset($argv[1])]);
|
||||
$twig->addExtension(new BrokenExtension());
|
||||
|
||||
echo $twig->render('index.html.twig');
|
||||
$twig->display('index.html.twig');
|
||||
|
||||
@@ -18,7 +18,7 @@ use Twig\Extension\SandboxExtension;
|
||||
use Twig\Extension\StringLoaderExtension;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Node;
|
||||
use Twig\Node\PrintNode;
|
||||
use Twig\Node\YieldExpressionNode;
|
||||
use Twig\Sandbox\SecurityPolicy;
|
||||
use Twig\Test\IntegrationTestCase;
|
||||
use Twig\Token;
|
||||
@@ -135,7 +135,7 @@ class TwigTestTokenParser_§ extends AbstractTokenParser
|
||||
{
|
||||
$this->parser->getStream()->expect(Token::BLOCK_END_TYPE);
|
||||
|
||||
return new PrintNode(new ConstantExpression('§', -1), -1);
|
||||
return new YieldExpressionNode(new ConstantExpression('§', -1), -1);
|
||||
}
|
||||
|
||||
public function getTag(): string
|
||||
|
||||
@@ -28,7 +28,7 @@ class BlockReferenceTest extends NodeTestCase
|
||||
return [
|
||||
[new BlockReferenceNode('foo', 1), <<<EOF
|
||||
// line 1
|
||||
\$this->displayBlock('foo', \$context, \$blocks);
|
||||
{$this->getDisplayOrYieldBlock('$this')}('foo', \$context, \$blocks);
|
||||
EOF
|
||||
],
|
||||
];
|
||||
|
||||
@@ -11,8 +11,12 @@ namespace Twig\Tests\Node;
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\Loader\ArrayLoader;
|
||||
use Twig\Node\BlockNode;
|
||||
use Twig\Node\Node;
|
||||
use Twig\Node\TextNode;
|
||||
use Twig\Node\YieldTextNode;
|
||||
use Twig\Test\NodeTestCase;
|
||||
|
||||
class BlockTest extends NodeTestCase
|
||||
@@ -28,11 +32,20 @@ class BlockTest extends NodeTestCase
|
||||
|
||||
public function getTests()
|
||||
{
|
||||
$body = new TextNode('foo', 1);
|
||||
$node = new BlockNode('foo', $body, 1);
|
||||
$tests = [];
|
||||
|
||||
return [
|
||||
[$node, <<<EOF
|
||||
$tests[] = [new BlockNode('foo', new YieldTextNode('foo', 1), 1), <<<EOF
|
||||
// line 1
|
||||
public function block_foo(\$context, array \$blocks = [])
|
||||
{
|
||||
\$macros = \$this->macros;
|
||||
yield "foo";
|
||||
}
|
||||
EOF
|
||||
, new Environment(new ArrayLoader(), ['use_yield' => true])
|
||||
];
|
||||
|
||||
$tests[] = [new BlockNode('foo', new TextNode('foo', 1), 1), <<<EOF
|
||||
// line 1
|
||||
public function block_foo(\$context, array \$blocks = [])
|
||||
{
|
||||
@@ -40,7 +53,20 @@ public function block_foo(\$context, array \$blocks = [])
|
||||
echo "foo";
|
||||
}
|
||||
EOF
|
||||
],
|
||||
, new Environment(new ArrayLoader(), ['use_yield' => false])
|
||||
];
|
||||
|
||||
$tests[] = [new BlockNode('foo', new Node(), 1), <<<EOF
|
||||
// line 1
|
||||
public function block_foo(\$context, array \$blocks = [])
|
||||
{
|
||||
\$macros = \$this->macros;
|
||||
yield;
|
||||
}
|
||||
EOF
|
||||
, new Environment(new ArrayLoader(), ['use_yield' => true])
|
||||
];
|
||||
|
||||
return $tests;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ class IncludeTest extends NodeTestCase
|
||||
$node = new IncludeNode($expr, null, false, false, 1);
|
||||
$tests[] = [$node, <<<EOF
|
||||
// line 1
|
||||
\$this->loadTemplate("foo.twig", null, 1)->display(\$context);
|
||||
{$this->getDisplayOrYield('$this->loadTemplate("foo.twig", null, 1)')}(\$context);
|
||||
EOF
|
||||
];
|
||||
|
||||
@@ -55,7 +55,7 @@ EOF
|
||||
$node = new IncludeNode($expr, null, false, false, 1);
|
||||
$tests[] = [$node, <<<EOF
|
||||
// line 1
|
||||
\$this->loadTemplate(((true) ? ("foo") : ("foo")), null, 1)->display(\$context);
|
||||
{$this->getDisplayOrYield('$this->loadTemplate(((true) ? ("foo") : ("foo")), null, 1)')}(\$context);
|
||||
EOF
|
||||
];
|
||||
|
||||
@@ -64,14 +64,14 @@ EOF
|
||||
$node = new IncludeNode($expr, $vars, false, false, 1);
|
||||
$tests[] = [$node, <<<EOF
|
||||
// line 1
|
||||
\$this->loadTemplate("foo.twig", null, 1)->display(CoreExtension::arrayMerge(\$context, ["foo" => true]));
|
||||
{$this->getDisplayOrYield('$this->loadTemplate("foo.twig", null, 1)')}(CoreExtension::arrayMerge(\$context, ["foo" => true]));
|
||||
EOF
|
||||
];
|
||||
|
||||
$node = new IncludeNode($expr, $vars, true, false, 1);
|
||||
$tests[] = [$node, <<<EOF
|
||||
// line 1
|
||||
\$this->loadTemplate("foo.twig", null, 1)->display(CoreExtension::toArray(["foo" => true]));
|
||||
{$this->getDisplayOrYield('$this->loadTemplate("foo.twig", null, 1)')}(CoreExtension::toArray(["foo" => true]));
|
||||
EOF
|
||||
];
|
||||
|
||||
@@ -85,7 +85,7 @@ try {
|
||||
// ignore missing template
|
||||
}
|
||||
if (\$__internal_%s) {
|
||||
\$__internal_%s->display(CoreExtension::toArray(["foo" => true]));
|
||||
{$this->getDisplayOrYield('$__internal_%s')}(CoreExtension::toArray(["foo" => true]));
|
||||
}
|
||||
EOF
|
||||
, null, true];
|
||||
|
||||
@@ -11,11 +11,14 @@ namespace Twig\Tests\Node;
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\Loader\ArrayLoader;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\NameExpression;
|
||||
use Twig\Node\MacroNode;
|
||||
use Twig\Node\Node;
|
||||
use Twig\Node\TextNode;
|
||||
use Twig\Node\YieldTextNode;
|
||||
use Twig\Test\NodeTestCase;
|
||||
|
||||
class MacroTest extends NodeTestCase
|
||||
@@ -33,15 +36,41 @@ class MacroTest extends NodeTestCase
|
||||
|
||||
public function getTests()
|
||||
{
|
||||
$body = new TextNode('foo', 1);
|
||||
$tests = [];
|
||||
|
||||
$arguments = new Node([
|
||||
'foo' => new ConstantExpression(null, 1),
|
||||
'bar' => new ConstantExpression('Foo', 1),
|
||||
], [], 1);
|
||||
|
||||
$body = new YieldTextNode('foo', 1);
|
||||
$node = new MacroNode('foo', $body, $arguments, 1);
|
||||
|
||||
return [
|
||||
[$node, <<<EOF
|
||||
$text[] = [$node, <<<EOF
|
||||
// line 1
|
||||
public function macro_foo(\$__foo__ = null, \$__bar__ = "Foo", ...\$__varargs__)
|
||||
{
|
||||
\$macros = \$this->macros;
|
||||
\$context = \$this->env->mergeGlobals([
|
||||
"foo" => \$__foo__,
|
||||
"bar" => \$__bar__,
|
||||
"varargs" => \$__varargs__,
|
||||
]);
|
||||
|
||||
\$blocks = [];
|
||||
|
||||
return new Markup(implode('', iterator_to_array((function () use (\$context, \$macros, \$blocks) {
|
||||
yield "foo";
|
||||
})() ?? new \EmptyIterator())), \$this->env->getCharset());
|
||||
}
|
||||
EOF
|
||||
, new Environment(new ArrayLoader(), ['use_yield' => true]),
|
||||
];
|
||||
|
||||
$body = new TextNode('foo', 1);
|
||||
$node = new MacroNode('foo', $body, $arguments, 1);
|
||||
|
||||
$tests[] = [$node, <<<EOF
|
||||
// line 1
|
||||
public function macro_foo(\$__foo__ = null, \$__bar__ = "Foo", ...\$__varargs__)
|
||||
{
|
||||
@@ -66,7 +95,9 @@ public function macro_foo(\$__foo__ = null, \$__bar__ = "Foo", ...\$__varargs__)
|
||||
})();
|
||||
}
|
||||
EOF
|
||||
],
|
||||
, new Environment(new ArrayLoader(), ['use_yield' => false]),
|
||||
];
|
||||
|
||||
return $tests;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ class ModuleTest extends NodeTestCase
|
||||
$macros = new Node();
|
||||
$traits = new Node();
|
||||
$source = new Source('{{ foo }}', 'foo.twig');
|
||||
$parentTemplate = $this->getEnvironment()->useYield() ? 'YieldingTemplate' : 'Template';
|
||||
|
||||
$node = new ModuleNode($body, $extends, $blocks, $macros, $traits, new Node([]), $source);
|
||||
$tests[] = [$node, <<<EOF
|
||||
@@ -71,10 +72,10 @@ use Twig\Sandbox\SecurityNotAllowedTagError;
|
||||
use Twig\Sandbox\SecurityNotAllowedFilterError;
|
||||
use Twig\Sandbox\SecurityNotAllowedFunctionError;
|
||||
use Twig\Source;
|
||||
use Twig\Template;
|
||||
use Twig\\{$parentTemplate};
|
||||
|
||||
/* foo.twig */
|
||||
class __TwigTemplate_%x extends Template
|
||||
class __TwigTemplate_%x extends $parentTemplate
|
||||
{
|
||||
private \$source;
|
||||
private \$macros = [];
|
||||
@@ -126,6 +127,7 @@ EOF
|
||||
|
||||
$body = new Node([$import]);
|
||||
$extends = new ConstantExpression('layout.twig', 1);
|
||||
$parentTemplate = $this->getEnvironment()->useYield() ? 'YieldingTemplate' : 'Template';
|
||||
|
||||
$node = new ModuleNode($body, $extends, $blocks, $macros, $traits, new Node([]), $source);
|
||||
$tests[] = [$node, <<<EOF
|
||||
@@ -142,10 +144,10 @@ use Twig\Sandbox\SecurityNotAllowedTagError;
|
||||
use Twig\Sandbox\SecurityNotAllowedFilterError;
|
||||
use Twig\Sandbox\SecurityNotAllowedFunctionError;
|
||||
use Twig\Source;
|
||||
use Twig\Template;
|
||||
use Twig\\{$parentTemplate};
|
||||
|
||||
/* foo.twig */
|
||||
class __TwigTemplate_%x extends Template
|
||||
class __TwigTemplate_%x extends $parentTemplate
|
||||
{
|
||||
private \$source;
|
||||
private \$macros = [];
|
||||
@@ -173,7 +175,7 @@ class __TwigTemplate_%x extends Template
|
||||
\$macros["macro"] = \$this->macros["macro"] = \$this->loadTemplate("foo.twig", "foo.twig", 2)->unwrap();
|
||||
// line 1
|
||||
\$this->parent = \$this->loadTemplate("layout.twig", "foo.twig", 1);
|
||||
\$this->parent->display(\$context, array_merge(\$this->blocks, \$blocks));
|
||||
{$this->getDisplayOrYield('$this->parent')}(\$context, array_merge(\$this->blocks, \$blocks));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -216,6 +218,7 @@ EOF
|
||||
new ConstantExpression('foo', 2),
|
||||
2
|
||||
);
|
||||
$parentTemplate = $this->getEnvironment()->useYield() ? 'YieldingTemplate' : 'Template';
|
||||
|
||||
$twig = new Environment($this->createMock(LoaderInterface::class), ['debug' => true]);
|
||||
$node = new ModuleNode($body, $extends, $blocks, $macros, $traits, new Node([]), $source);
|
||||
@@ -233,10 +236,10 @@ use Twig\Sandbox\SecurityNotAllowedTagError;
|
||||
use Twig\Sandbox\SecurityNotAllowedFilterError;
|
||||
use Twig\Sandbox\SecurityNotAllowedFunctionError;
|
||||
use Twig\Source;
|
||||
use Twig\Template;
|
||||
use Twig\\{$parentTemplate};
|
||||
|
||||
/* foo.twig */
|
||||
class __TwigTemplate_%x extends Template
|
||||
class __TwigTemplate_%x extends $parentTemplate
|
||||
{
|
||||
private \$source;
|
||||
private \$macros = [];
|
||||
@@ -263,7 +266,7 @@ class __TwigTemplate_%x extends Template
|
||||
// line 4
|
||||
\$context["foo"] = "foo";
|
||||
// line 2
|
||||
\$this->getParent(\$context)->display(\$context, array_merge(\$this->blocks, \$blocks));
|
||||
{$this->getDisplayOrYield('$this->getParent($context)')}(\$context, array_merge(\$this->blocks, \$blocks));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace Twig\Tests\Node;
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\Loader\ArrayLoader;
|
||||
use Twig\Node\Expression\AssignNameExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\NameExpression;
|
||||
@@ -49,6 +51,15 @@ EOF
|
||||
$names = new Node([new AssignNameExpression('foo', 1)], [], 1);
|
||||
$values = new Node([new PrintNode(new ConstantExpression('foo', 1), 1)], [], 1);
|
||||
$node = new SetNode(true, $names, $values, 1);
|
||||
|
||||
$tests[] = [$node, <<<EOF
|
||||
// line 1
|
||||
\$context["foo"] = ('' === \$tmp = implode('', iterator_to_array((function () use (&\$context, \$macros, \$blocks) {
|
||||
echo "foo";
|
||||
})() ?? new \EmptyIterator()))) ? '' : new Markup(\$tmp, \$this->env->getCharset());
|
||||
EOF
|
||||
, new Environment(new ArrayLoader(), ['use_yield' => true]),
|
||||
];
|
||||
$tests[] = [$node, <<<EOF
|
||||
// line 1
|
||||
\$context["foo"] = (function () use (&\$context, \$macros, \$blocks) {
|
||||
@@ -62,6 +73,7 @@ EOF
|
||||
}
|
||||
})();
|
||||
EOF
|
||||
, new Environment(new ArrayLoader(), ['use_yield' => false]),
|
||||
];
|
||||
|
||||
$names = new Node([new AssignNameExpression('foo', 1)], [], 1);
|
||||
|
||||
@@ -58,7 +58,7 @@ class TemplateWrapperTest extends TestCase
|
||||
{
|
||||
$twig = new Environment(new ArrayLoader([
|
||||
'index' => '{% block foo %}{{ foo }}{{ bar }}{% endblock %}',
|
||||
]));
|
||||
], ['use_yield' => false]));
|
||||
$twig->addGlobal('bar', 'BAR');
|
||||
|
||||
$wrapper = $twig->load('index');
|
||||
|
||||
Reference in New Issue
Block a user