diff --git a/CHANGELOG.md b/CHANGELOG.md index 54ca2513d..7956dc3b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# v1.7.41.2 +## 06/01/2023 + +1. [](#improved) + * Added the ability to set a configurable 'key' for the Twig Cache Tag: `{% cache 'my-key' 600 %}` +1. [](#bugfix) + * Fixed an issue with special characters in slug's would cause redirect loops + # v1.7.41.1 ## 05/10/2023 diff --git a/system/defines.php b/system/defines.php index bccdd42b4..02b37c901 100644 --- a/system/defines.php +++ b/system/defines.php @@ -9,7 +9,7 @@ // Some standard defines define('GRAV', true); -define('GRAV_VERSION', '1.7.41.1'); +define('GRAV_VERSION', '1.7.41.2'); define('GRAV_SCHEMA', '1.7.0_2020-11-20_1'); define('GRAV_TESTING', false); diff --git a/system/src/Grav/Common/Service/PagesServiceProvider.php b/system/src/Grav/Common/Service/PagesServiceProvider.php index 0e10d144e..a6fc59ed2 100644 --- a/system/src/Grav/Common/Service/PagesServiceProvider.php +++ b/system/src/Grav/Common/Service/PagesServiceProvider.php @@ -59,7 +59,7 @@ class PagesServiceProvider implements ServiceProviderInterface /** @var Uri $uri */ $uri = $grav['uri']; - $path = $uri->path() ?: '/'; // Don't trim to support trailing slash default routes + $path = $uri->path() ? urldecode($uri->path()) : '/'; // Don't trim to support trailing slash default routes $page = $pages->dispatch($path); // Redirection tests diff --git a/system/src/Grav/Common/Twig/Node/TwigNodeCache.php b/system/src/Grav/Common/Twig/Node/TwigNodeCache.php index 64a13a684..c7d4eea45 100644 --- a/system/src/Grav/Common/Twig/Node/TwigNodeCache.php +++ b/system/src/Grav/Common/Twig/Node/TwigNodeCache.php @@ -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"); } -} +} \ No newline at end of file diff --git a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserCache.php b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserCache.php index d3658b23b..3b030a433 100644 --- a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserCache.php +++ b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserCache.php @@ -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'; } -} +} \ No newline at end of file