Code Monkey home page Code Monkey logo

Comments (7)

fabpot avatar fabpot commented on August 25, 2024 2

Reproducer:

<?php

require_once __DIR__.'/vendor/autoload.php';

use Twig\Compiler;
use Twig\Environment;
use Twig\Extension\AbstractExtension;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Node;
use Twig\Node\PrintNode;
use Twig\Token;
use Twig\TokenParser\AbstractTokenParser;

error_reporting(-1);

class EchoNode extends Node
{
    public function compile(Compiler $compiler): void
    {
        $compiler
            ->addDebugInfo($this)
            ->write("ob_start();\n")
            ->subcompile($this->getNode('content1'))
            ->write('echo "\n";', "\n")
            ->write("echo 'foo';\n")
            ->write('echo "\n";', "\n")
            ->subcompile($this->getNode('content2'))
            ->write('echo "\n";', "\n")
            ->write("echo ob_get_clean();\n")
        ;
    }
}

class EchoTokenParser extends AbstractTokenParser
{
    public function parse(Token $token): Node
    {
        $this->parser->getStream()->expect(Token::BLOCK_END_TYPE);

        return new EchoNode([
            'content1' => new PrintNode(new ConstantExpression('content1', -1), -1),
            'content2' => new PrintNode(new ConstantExpression('content2', -1), -1),
        ]);
    }

    public function getTag(): string
    {
        return 'echo';
    }
}

class EchoExtension extends AbstractExtension
{
    public function getTokenParsers(): array
    {
        return [
            new EchoTokenParser(),
        ];
    }
}

$twig = new Environment(new Twig\Loader\ArrayLoader(['_bug.twig' => "HERE\n{% echo %}\nHERE"]), [
    'strict_variables' => true,
    'debug' => true,
    'cache' => false,
    'autoescape' => false,
    'use_yield' => false,
]);
$twig->addExtension(new EchoExtension());

$template = '_bug.twig';
echo $twig->parse($twig->tokenize(new Twig\Source($twig->getLoader()->getSourceContext($template)->getCode(), $template)))."\n\n";
echo $twig->compile($twig->parse($twig->tokenize(new Twig\Source($twig->getLoader()->getSourceContext($template)->getCode(), $template))))."\n\n";

$template = $twig->load($template);
echo $template->render([]);

from twig.

nicolas-grekas avatar nicolas-grekas commented on August 25, 2024

Can you please check if #4216 fixes the issue with nodes that use output buffering?

from twig.

brandonkelly avatar brandonkelly commented on August 25, 2024

This is still not quite working for nodes that call subcompile() and expect any raw output to be echoed out, and captured in an output buffer, e.g. like this:

https://github.com/craftcms/cms/blob/6183b53d6aadab0808e673bf1deed675d9f408e4/src/web/twig/nodes/TagNode.php#L27-L33

You’ll end up with yield statements rather than echo statements:

// line 87
ob_start();
// line 90
yield "    ";
…

from twig.

nicolas-grekas avatar nicolas-grekas commented on August 25, 2024

Please open a new issue and provide a reproducer if possible.

from twig.

brandonkelly avatar brandonkelly commented on August 25, 2024

Isn’t it the same issue? I specifically referenced PrintNode in the OP.

from twig.

nicolas-grekas avatar nicolas-grekas commented on August 25, 2024

OK, reopening. But I'm missing a realworld description: the fix involves echoing strings that where yielded, so that ob_start() should catch yielded strings. Which means that as is, the updated report is incomplete. Can you please share a reproducer?

from twig.

brandonkelly avatar brandonkelly commented on August 25, 2024

@fabpot Still seeing an issue after #4221 when macros are involved.

Here’s an example:

{% macro button() %}
  {% tag 'button' %}
    Button Label
  {% endtag %}
{% endmacro %}
{{ _self.button() }}
use Twig\Token;
use Twig\TokenParser\AbstractTokenParser;

class TagTokenParser extends AbstractTokenParser
{
    public function getTag(): string
    {
        return 'tag';
    }

    public function parse(Token $token): TagNode
    {
        $lineno = $token->getLine();
        $expressionParser = $this->parser->getExpressionParser();
        $stream = $this->parser->getStream();

        $nodes = [
            'name' => $expressionParser->parseExpression(),
        ];

        $stream->expect(Token::BLOCK_END_TYPE);
        $nodes['content'] = $this->parser->subparse(function(Token $token) {
            return $token->test('endtag');
        }, true);
        $stream->expect(Token::BLOCK_END_TYPE);

        return new TagNode($nodes, [], $lineno, $this->getTag());
    }
}
use Twig\Compiler;
use Twig\Node\Node;

class TagNode extends Node
{
    public function compile(Compiler $compiler): void
    {
        $compiler
            ->addDebugInfo($this)
            ->write("ob_start();\n")
            ->subcompile($this->getNode('content'))
            ->write("\$content = ob_get_clean();\n")
            ->write("echo '<' . ")
            ->subcompile($this->getNode('name'))
            ->raw(" . '>';\n")
            ->write("echo \$content;\n")
            ->write("echo '</' . ")
            ->subcompile($this->getNode('name'))
            ->raw(" . '>';\n");
    }
}

On Twig 3.8.0 that results in:

protected function doDisplay(array $context, array $blocks = [])
{
    $macros = $this->macros;
    // line 6
    echo twig_call_macro($macros["_self"], "macro_button", [], 6, $context, $this->getSourceContext());
    echo "
";
}

// line 1
public function macro_button(...$__varargs__)
{
    $macros = $this->macros;
    $context = $this->env->mergeGlobals([
        "varargs" => $__varargs__,
    ]);

    $blocks = [];

    ob_start();
    try {
        // line 2
        echo "  ";
        ob_start();
        // line 3
        echo "    Button Label
";
        $content = ob_get_clean();
        echo '<' . "button" . '>';
        echo $content;
        echo '</' . "button" . '>';

        return ('' === $tmp = ob_get_contents()) ? '' : new Markup($tmp, $this->env->getCharset());
    } finally {
        ob_end_clean();
    }
}
  <button>    Button Label
  </button>

But on 7173182 that gives me:

protected function doDisplay(array $context, array $blocks = [])
{
    $macros = $this->macros;
    // line 6
    yield CoreExtension::callMacro($macros["_self"], "macro_button", [], 6, $context, $this->getSourceContext());
    yield "
";
    return; yield '';
}

// line 1
public function macro_button(...$__varargs__)
{
    $macros = $this->macros;
    $context = $this->env->mergeGlobals([
        "varargs" => $__varargs__,
    ]);

    $blocks = [];

    return ('' === $tmp = \Twig\Extension\CoreExtension::captureOutput((function () use (&$context, $macros, $blocks) {
        // line 2
        yield "  ";
        ob_start();
        // line 3
        yield "    Button Label
";
        $content = ob_get_clean();
        echo '<' . "button" . '>';
        echo $content;
        echo '</' . "button" . '>';
        return; yield '';
    })())) ? '' : new Markup($tmp, $this->env->getCharset());
}
      Button Label
  <button></button>

from twig.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.