mirror of
https://github.com/twigphp/Twig.git
synced 2026-08-28 19:17:37 +00:00
Abstract node capture in its own Node
This commit is contained in:
@@ -12,13 +12,17 @@
|
||||
namespace Twig\Extra\Cache\Node;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\CaptureNode;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Node;
|
||||
|
||||
class CacheNode extends Node
|
||||
class CacheNode extends AbstractExpression
|
||||
{
|
||||
public function __construct(AbstractExpression $key, ?AbstractExpression $ttl, ?AbstractExpression $tags, Node $body, int $lineno, string $tag)
|
||||
{
|
||||
$body = new CaptureNode($body, $lineno);
|
||||
$body->setAttribute('raw', true);
|
||||
|
||||
$nodes = ['key' => $key, 'body' => $body];
|
||||
if (null !== $ttl) {
|
||||
$nodes['ttl'] = $ttl;
|
||||
@@ -34,7 +38,7 @@ class CacheNode extends Node
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('$cached = $this->env->getRuntime(\'Twig\Extra\Cache\CacheRuntime\')->getCache()->get(')
|
||||
->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")
|
||||
->indent()
|
||||
@@ -57,13 +61,11 @@ class CacheNode extends Node
|
||||
}
|
||||
|
||||
$compiler
|
||||
->write("ob_start(function () { return ''; });\n")
|
||||
->write('return ')
|
||||
->subcompile($this->getNode('body'))
|
||||
->write("\n")
|
||||
->write("return ob_get_clean();\n")
|
||||
->raw(";\n")
|
||||
->outdent()
|
||||
->write("});\n")
|
||||
->write("echo '' === \$cached ? '' : new Markup(\$cached, \$this->env->getCharset());\n")
|
||||
->write("})\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,10 @@ namespace Twig\Extra\Cache\TokenParser;
|
||||
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\Extra\Cache\Node\CacheNode;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\FilterExpression;
|
||||
use Twig\Node\Node;
|
||||
use Twig\Node\PrintNode;
|
||||
use Twig\Token;
|
||||
use Twig\TokenParser\AbstractTokenParser;
|
||||
|
||||
@@ -54,7 +57,10 @@ class CacheTokenParser extends AbstractTokenParser
|
||||
$body = $this->parser->subparse([$this, 'decideCacheEnd'], true);
|
||||
$stream->expect(Token::BLOCK_END_TYPE);
|
||||
|
||||
return new CacheNode($key, $ttl, $tags, $body, $token->getLine(), $this->getTag());
|
||||
$body = new CacheNode($key, $ttl, $tags, $body, $token->getLine(), $this->getTag());
|
||||
$body = new FilterExpression($body, new ConstantExpression('raw', $token->getLine()), new Node(), $token->getLine());
|
||||
|
||||
return new PrintNode($body, $token->getLine(), $this->getTag());
|
||||
}
|
||||
|
||||
public function decideCacheEnd(Token $token): bool
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"symfony/cache": "^5.0|^6.0|^7.0",
|
||||
"twig/twig": "^3.0"
|
||||
"twig/twig": "^3.9"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "^6.4|^7.0"
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<?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 for which we need to capture the output.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
if ($this->getAttribute('with_blocks')) {
|
||||
$compiler->raw("(function () use (&\$context, \$macros, \$blocks) {\n");
|
||||
} else {
|
||||
$compiler->raw("(function () use (&\$context, \$macros) {\n");
|
||||
}
|
||||
$compiler->indent();
|
||||
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")
|
||||
;
|
||||
if ($this->getAttribute('raw')) {
|
||||
$compiler->write("return ob_get_contents();\n");
|
||||
} else {
|
||||
$compiler->write("return ('' === \$tmp = ob_get_contents()) ? '' : new Markup(\$tmp, \$this->env->getCharset());\n");
|
||||
}
|
||||
$compiler
|
||||
->outdent()
|
||||
->write("} finally {\n")
|
||||
->indent()
|
||||
->write("ob_end_clean();\n")
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
->outdent()
|
||||
->write('})()')
|
||||
;
|
||||
}
|
||||
}
|
||||
+4
-20
@@ -31,6 +31,8 @@ class MacroNode extends Node
|
||||
}
|
||||
}
|
||||
|
||||
$body = new CaptureNode($body, $lineno, $tag);
|
||||
|
||||
parent::__construct(['body' => $body, 'arguments' => $arguments], ['name' => $name], $lineno, $tag);
|
||||
}
|
||||
|
||||
@@ -81,31 +83,13 @@ class MacroNode extends Node
|
||||
->write('')
|
||||
->string(self::VARARGS_NAME)
|
||||
->raw(' => ')
|
||||
;
|
||||
|
||||
$compiler
|
||||
->raw("\$__varargs__,\n")
|
||||
->outdent()
|
||||
->write("]);\n\n")
|
||||
->write("\$blocks = [];\n\n")
|
||||
;
|
||||
if ($compiler->getEnvironment()->isDebug()) {
|
||||
$compiler->write("ob_start();\n");
|
||||
} else {
|
||||
$compiler->write("ob_start(function () { return ''; });\n");
|
||||
}
|
||||
$compiler
|
||||
->write("try {\n")
|
||||
->indent()
|
||||
->write('return ')
|
||||
->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")
|
||||
->raw(";\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
|
||||
+14
-26
@@ -23,22 +23,24 @@ class SetNode extends Node implements NodeCaptureInterface
|
||||
{
|
||||
public function __construct(bool $capture, Node $names, Node $values, int $lineno, string $tag = null)
|
||||
{
|
||||
parent::__construct(['names' => $names, 'values' => $values], ['capture' => $capture, 'safe' => false], $lineno, $tag);
|
||||
|
||||
/*
|
||||
* Optimizes the node when capture is used for a large block of text.
|
||||
*
|
||||
* {% set foo %}foo{% endset %} is compiled to $context['foo'] = new Twig\Markup("foo");
|
||||
*/
|
||||
if ($this->getAttribute('capture')) {
|
||||
$this->setAttribute('safe', true);
|
||||
|
||||
$values = $this->getNode('values');
|
||||
$safe = false;
|
||||
if ($capture) {
|
||||
$safe = true;
|
||||
if ($values instanceof TextNode) {
|
||||
$this->setNode('values', new ConstantExpression($values->getAttribute('data'), $values->getTemplateLine()));
|
||||
$this->setAttribute('capture', false);
|
||||
$values = new ConstantExpression($values->getAttribute('data'), $values->getTemplateLine());
|
||||
$capture = false;
|
||||
} else {
|
||||
$values = new CaptureNode($values, $values->getTemplateLine());
|
||||
$values->setAttribute('with_blocks', true);
|
||||
}
|
||||
}
|
||||
|
||||
parent::__construct(['names' => $names, 'values' => $values], ['capture' => $capture, 'safe' => $safe], $lineno, $tag);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
@@ -56,27 +58,13 @@ class SetNode extends Node implements NodeCaptureInterface
|
||||
}
|
||||
$compiler->raw(')');
|
||||
} else {
|
||||
if ($this->getAttribute('capture')) {
|
||||
if ($compiler->getEnvironment()->isDebug()) {
|
||||
$compiler->write("ob_start();\n");
|
||||
} else {
|
||||
$compiler->write("ob_start(function () { return ''; });\n");
|
||||
}
|
||||
$compiler
|
||||
->subcompile($this->getNode('values'))
|
||||
;
|
||||
}
|
||||
|
||||
$compiler->subcompile($this->getNode('names'), false);
|
||||
|
||||
if ($this->getAttribute('capture')) {
|
||||
$compiler->raw(" = ('' === \$tmp = ob_get_clean()) ? '' : new Markup(\$tmp, \$this->env->getCharset())");
|
||||
}
|
||||
}
|
||||
$compiler->raw(' = ');
|
||||
|
||||
if (!$this->getAttribute('capture')) {
|
||||
$compiler->raw(' = ');
|
||||
|
||||
if ($this->getAttribute('capture')) {
|
||||
$compiler->subcompile($this->getNode('values'));
|
||||
} else {
|
||||
if (\count($this->getNode('names')) > 1) {
|
||||
$compiler->write('[');
|
||||
foreach ($this->getNode('values') as $idx => $value) {
|
||||
|
||||
@@ -26,7 +26,7 @@ class MacroTest extends NodeTestCase
|
||||
$arguments = new Node([new NameExpression('foo', 1)], [], 1);
|
||||
$node = new MacroNode('foo', $body, $arguments, 1);
|
||||
|
||||
$this->assertEquals($body, $node->getNode('body'));
|
||||
$this->assertEquals($body, $node->getNode('body')->getNode('body'));
|
||||
$this->assertEquals($arguments, $node->getNode('arguments'));
|
||||
$this->assertEquals('foo', $node->getAttribute('name'));
|
||||
}
|
||||
@@ -54,14 +54,16 @@ public function macro_foo(\$__foo__ = null, \$__bar__ = "Foo", ...\$__varargs__)
|
||||
|
||||
\$blocks = [];
|
||||
|
||||
ob_start(function () { return ''; });
|
||||
try {
|
||||
echo "foo";
|
||||
return (function () use (&\$context, \$macros) {
|
||||
ob_start(function () { return ''; });
|
||||
try {
|
||||
echo "foo";
|
||||
|
||||
return ('' === \$tmp = ob_get_contents()) ? '' : new Markup(\$tmp, \$this->env->getCharset());
|
||||
} finally {
|
||||
ob_end_clean();
|
||||
}
|
||||
return ('' === \$tmp = ob_get_contents()) ? '' : new Markup(\$tmp, \$this->env->getCharset());
|
||||
} finally {
|
||||
ob_end_clean();
|
||||
}
|
||||
})();
|
||||
}
|
||||
EOF
|
||||
],
|
||||
|
||||
+10
-3
@@ -51,9 +51,16 @@ EOF
|
||||
$node = new SetNode(true, $names, $values, 1);
|
||||
$tests[] = [$node, <<<EOF
|
||||
// line 1
|
||||
ob_start(function () { return ''; });
|
||||
echo "foo";
|
||||
\$context["foo"] = ('' === \$tmp = ob_get_clean()) ? '' : new Markup(\$tmp, \$this->env->getCharset());
|
||||
\$context["foo"] = (function () use (&\$context, \$macros, \$blocks) {
|
||||
ob_start(function () { return ''; });
|
||||
try {
|
||||
echo "foo";
|
||||
|
||||
return ('' === \$tmp = ob_get_contents()) ? '' : new Markup(\$tmp, \$this->env->getCharset());
|
||||
} finally {
|
||||
ob_end_clean();
|
||||
}
|
||||
})();
|
||||
EOF
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user