Skip to content

Commit

Permalink
Remove usage of ob_* functions in favor of yielding
Browse files Browse the repository at this point in the history
  • Loading branch information
fabpot committed Dec 21, 2023
1 parent 7072a72 commit 3f35487
Show file tree
Hide file tree
Showing 29 changed files with 637 additions and 89 deletions.
18 changes: 18 additions & 0 deletions src/Environment.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
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;
Expand Down Expand Up @@ -66,6 +67,7 @@ class Environment
private $runtimeLoaders = [];
private $runtimes = [];
private $optionsHash;
private bool $useYield;

/**
* Constructor.
Expand Down Expand Up @@ -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 = [])
{
Expand All @@ -110,8 +114,13 @@ public function __construct(LoaderInterface $loader, $options = [])
'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'];
Expand All @@ -122,6 +131,15 @@ public function __construct(LoaderInterface $loader, $options = [])
$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;
}

/**
Expand Down
29 changes: 29 additions & 0 deletions src/Extension/YieldingExtension.php
Original file line number Diff line number Diff line change
@@ -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)];
}
}
8 changes: 8 additions & 0 deletions src/Node/BlockNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ public function compile(Compiler $compiler): void

$compiler
->subcompile($this->getNode('body'))
;

if ($compiler->getEnvironment()->useYield()) {
// needed when body doesn't yield anything
$compiler->write("yield;\n");
}

$compiler
->outdent()
->write("}\n\n")
;
Expand Down
15 changes: 11 additions & 4 deletions src/Node/BlockReferenceNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,16 @@ public function __construct(string $name, int $lineno, string $tag = null)

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')))
;
}
}
}
20 changes: 17 additions & 3 deletions src/Node/Expression/BlockReferenceExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,19 @@ public function compile(Compiler $compiler): void
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');
}
Expand All @@ -65,6 +75,10 @@ private function compileTemplateCall(Compiler $compiler, string $method): Compil
;
}

if ($compiler->getEnvironment()->useYield()) {
$compiler->raw('->unwrap()');
}

$compiler->raw(sprintf('->%s', $method));

return $this->compileBlockArguments($compiler);
Expand Down
17 changes: 12 additions & 5 deletions src/Node/Expression/InlinePrint.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,17 @@ public function __construct(Node $node, int $lineno)

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(')')
;
}
}
}
41 changes: 29 additions & 12 deletions src/Node/Expression/ParentExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,36 @@ public function __construct(string $name, int $lineno, string $tag = null)

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)')
;
}
}
}
}
26 changes: 24 additions & 2 deletions src/Node/IncludeNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,39 @@ public function compile(Compiler $compiler): void
->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")
->outdent()
->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");
}
Expand Down
41 changes: 27 additions & 14 deletions src/Node/MacroNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,23 +89,36 @@ public function compile(Compiler $compiler): void
->write("]);\n\n")
->write("\$blocks = [];\n\n")
;
if ($compiler->getEnvironment()->isDebug()) {
$compiler->write("ob_start();\n");

if ($compiler->getEnvironment()->useYield()) {
$compiler
->write("return new Markup(implode('', iterator_to_array((function () use (\$context, \$macros, \$blocks) {\n")
->indent()
->subcompile($this->getNode('body'))
->outdent()
->write("})() ?? [])), \$this->env->getCharset());\n")
;
} else {
$compiler->write("ob_start(function () { return ''; });\n");
if ($compiler->getEnvironment()->isDebug()) {
$compiler->write("ob_start();\n");
} else {
$compiler->write("ob_start(function () { return ''; });\n");
}
$compiler
->write("try {\n")
->indent()
->subcompile($this->getNode('body'))
->raw("\n")
->write("return ('' === \$tmp = ob_get_contents()) ? '' : new Markup(\$tmp, \$this->env->getCharset());\n")
->outdent()
->write("} finally {\n")
->indent()
->write("ob_end_clean();\n")
->outdent()
->write("}\n")
;
}
$compiler
->write("try {\n")
->indent()
->subcompile($this->getNode('body'))
->raw("\n")
->write("return ('' === \$tmp = ob_get_contents()) ? '' : new Markup(\$tmp, \$this->env->getCharset());\n")
->outdent()
->write("} finally {\n")
->indent()
->write("ob_end_clean();\n")
->outdent()
->write("}\n")
->outdent()
->write("}\n\n")
;
Expand Down
22 changes: 17 additions & 5 deletions src/Node/ModuleNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,14 @@ protected function compileClassHeader(Compiler $compiler)
->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")
Expand Down Expand Up @@ -325,11 +325,23 @@ protected function compileDisplay(Compiler $compiler)
->repr($parent->getTemplateLine())
->raw(");\n")
;
$compiler->write('$this->parent');
}
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->write('$this->getParent($context)');
$compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
}
$compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
}

$compiler
Expand Down
Loading

0 comments on commit 3f35487

Please sign in to comment.