improved the Twig Cache Tag with customizable key (lang specific if needed)

This commit is contained in:
Andy Miller
2023-05-23 15:54:48 -06:00
parent 4de3cab522
commit 31aeaf6309
3 changed files with 82 additions and 38 deletions

View File

@@ -1,3 +1,9 @@
# v1.7.42
## mm/dd/2023
1. [](#improved)
* Added the ability to set a configurable 'key' for the Twig Cache Tag: `{% cache 'my-key' 600 %}`
# v1.7.41.1
## 05/10/2023

View File

@@ -10,13 +10,15 @@
namespace Grav\Common\Twig\Node;
use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Node;
use Twig\Node\NodeOutputInterface;
/**
* Class TwigNodeCache
* @package Grav\Common\Twig\Node
*/
class TwigNodeCache extends Node
class TwigNodeCache extends Node implements NodeOutputInterface
{
/**
* @param string $key unique name for key
@@ -25,25 +27,58 @@ class TwigNodeCache extends Node
* @param integer $lineno
* @param string|null $tag
*/
public function __construct(string $key, int $lifetime, Node $body, $lineno, $tag = null)
public function __construct(Node $body, ?AbstractExpression $key, ?AbstractExpression $lifetime, array $defaults, int $lineno, string $tag)
{
parent::__construct(array('body' => $body), array( 'key' => $key, 'lifetime' => $lifetime), $lineno, $tag);
$nodes = ['body' => $body];
if ($key !== null) {
$nodes['key'] = $key;
}
if ($lifetime !== null) {
$nodes['lifetime'] = $lifetime;
}
parent::__construct($nodes, $defaults, $lineno, $tag);
}
/**
* {@inheritDoc}
*/
public function compile(Compiler $compiler): void
{
$boo = $this->getAttribute('key');
$compiler->addDebugInfo($this);
// Generate the cache key
if ($this->hasNode('key')) {
$compiler
->write('$key = "twigcache-" . ')
->subcompile($this->getNode('key'))
->raw(";\n");
} else {
$compiler
->write('$key = ')
->string($this->getAttribute('key'))
->raw(";\n");
}
// Set the cache timeout
if ($this->hasNode('lifetime')) {
$compiler
->write('$lifetime = ')
->subcompile($this->getNode('lifetime'))
->raw(";\n");
} else {
$compiler
->write('$lifetime = ')
->write($this->getAttribute('lifetime'))
->raw(";\n");
}
$compiler
->addDebugInfo($this)
->write("\$cache = \\Grav\\Common\\Grav::instance()['cache'];\n")
->write("\$key = \"twigcache-\" . \"" . $this->getAttribute('key') . "\";\n")
->write("\$lifetime = " . $this->getAttribute('lifetime') . ";\n")
->write("\$cache_body = \$cache->fetch(\$key);\n")
->write("if (\$cache_body === false) {\n")
->indent()
->write("\\Grav\\Common\\Grav::instance()['debugger']->addMessage(\"Cache Key: \$key, Lifetime: \$lifetime\");\n")
->write("ob_start();\n")
->indent()
->subcompile($this->getNode('body'))
@@ -53,6 +88,6 @@ class TwigNodeCache extends Node
->write("\$cache->save(\$key, \$cache_body, \$lifetime);\n")
->outdent()
->write("}\n")
->write("echo \$cache_body;\n");
->write("echo '' === \$cache_body ? '' : new Markup(\$cache_body, \$this->env->getCharset());\n");
}
}
}

View File

@@ -11,7 +11,6 @@ namespace Grav\Common\Twig\TokenParser;
use Grav\Common\Grav;
use Grav\Common\Twig\Node\TwigNodeCache;
use Twig\Error\SyntaxError;
use Twig\Token;
use Twig\TokenParser\AbstractTokenParser;
@@ -22,50 +21,54 @@ use Twig\TokenParser\AbstractTokenParser;
* {{ some_complex_work() }}
* {% endcache %}
*
* Where the `600` is an optional lifetime in seconds
* Also can provide a unique key for the cache:
*
* {% cache "prefix-"~lang 600 %}
*
* Where the "prefix-"~lang will use a unique key based on the current language. "prefix-en" for example
*/
class TwigTokenParserCache extends AbstractTokenParser
{
/**
* @param Token $token
* @return TwigNodeCache
* @throws SyntaxError
*/
public function parse(Token $token)
{
$lineno = $token->getLine();
$stream = $this->parser->getStream();
$key = $this->parser->getVarName() . $lineno;
$lifetime = Grav::instance()['cache']->getLifetime();
$lineno = $token->getLine();
// Check for optional lifetime override
if (!$stream->test(Token::BLOCK_END_TYPE)) {
$lifetime_expr = $this->parser->getExpressionParser()->parseExpression();
$lifetime = $lifetime_expr->getAttribute('value');
// Parse the optional key and timeout parameters
$defaults = [
'key' => $this->parser->getVarName() . $lineno,
'lifetime' => Grav::instance()['cache']->getLifetime()
];
$key = null;
$lifetime = null;
while (!$stream->test(Token::BLOCK_END_TYPE)) {
if ($stream->test(Token::STRING_TYPE)) {
$key = $this->parser->getExpressionParser()->parseExpression();
} elseif ($stream->test(Token::NUMBER_TYPE)) {
$lifetime = $this->parser->getExpressionParser()->parseExpression();
} else {
throw new \Twig\Error\SyntaxError("Unexpected token type in cache tag.", $token->getLine(), $stream->getSourceContext());
}
}
$stream->expect(Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this, 'decideCacheEnd'), true);
// Parse the content inside the cache block
$body = $this->parser->subparse([$this, 'decideCacheEnd'], true);
$stream->expect(Token::BLOCK_END_TYPE);
return new TwigNodeCache($key, $lifetime, $body, $lineno, $this->getTag());
return new TwigNodeCache($body, $key, $lifetime, $defaults, $lineno, $this->getTag());
}
/**
* Decide if current token marks end of cache block.
*
* @param Token $token
* @return bool
*/
public function decideCacheEnd(Token $token): bool
{
return $token->test('endcache');
}
/**
* {@inheritDoc}
*/
public function getTag(): string
{
return 'cache';
}
}
}