diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dc62062f0..3f372f4bbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Added `$grav['uri]->getCurrentUri()` method to get `Grav\Framework\Uri\Uri` instance for the current URL * Added `$grav['uri]->getCurrentRoute()` method to get `Grav\Framework\Route\Route` instance for the current URL * Added ability to have `php` version dependencies in GPM assets + * Added new `{% switch %}` twig tag for more elegant if statements * Added new `{% markdown %}` twig tag 1. [](#improved) * Vendor library updated to latest diff --git a/system/src/Grav/Common/Twig/Node/TwigNodeSwitch.php b/system/src/Grav/Common/Twig/Node/TwigNodeSwitch.php new file mode 100644 index 0000000000..e0e8127006 --- /dev/null +++ b/system/src/Grav/Common/Twig/Node/TwigNodeSwitch.php @@ -0,0 +1,71 @@ + $value, 'cases' => $cases, 'default' => $default), array(), $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param \Twig_Compiler A Twig_Compiler instance + */ + public function compile(\Twig_Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write("switch (") + ->subcompile($this->getNode('value')) + ->raw(") {\n") + ->indent(); + + foreach ($this->getNode('cases') as $case) + { + if (!$case->hasNode('body')) + { + continue; + } + + foreach ($case->getNode('values') as $value) + { + $compiler + ->write('case ') + ->subcompile($value) + ->raw(":\n"); + } + + $compiler + ->write("{\n") + ->indent() + ->subcompile($case->getNode('body')) + ->write("break;\n") + ->outdent() + ->write("}\n"); + } + + if ($this->hasNode('default') && $this->getNode('default') !== null) + { + $compiler + ->write("default:\n") + ->write("{\n") + ->indent() + ->subcompile($this->getNode('default')) + ->outdent() + ->write("}\n"); + } + + $compiler + ->outdent() + ->write("}\n"); + } +} diff --git a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserSwitch.php b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserSwitch.php new file mode 100644 index 0000000000..2da932de4c --- /dev/null +++ b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserSwitch.php @@ -0,0 +1,138 @@ +getLine(); + $stream = $this->parser->getStream(); + + $name = $this->parser->getExpressionParser()->parseExpression(); + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + // There can be some whitespace between the {% switch %} and first {% case %} tag. + while ($stream->getCurrent()->getType() == \Twig_Token::TEXT_TYPE && trim($stream->getCurrent()->getValue()) == '') + { + $stream->next(); + } + + $stream->expect(\Twig_Token::BLOCK_START_TYPE); + + $expressionParser = $this->parser->getExpressionParser(); + + $default = null; + $cases = array(); + $end = false; + + while (!$end) + { + $next = $stream->next(); + + switch ($next->getValue()) + { + case 'case': + { + $values = array(); + + while (true) + { + $values[] = $expressionParser->parsePrimaryExpression(); + // Multiple allowed values? + if ($stream->test(\Twig_Token::OPERATOR_TYPE, 'or')) + { + $stream->next(); + } + else + { + break; + } + } + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + $body = $this->parser->subparse(array($this, 'decideIfFork')); + $cases[] = new \Twig_Node(array( + 'values' => new \Twig_Node($values), + 'body' => $body + )); + break; + } + case 'default': + { + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + $default = $this->parser->subparse(array($this, 'decideIfEnd')); + break; + } + case 'endswitch': + { + $end = true; + break; + } + default: + { + throw new \Twig_Error_Syntax(sprintf('Unexpected end of template. Twig was looking for the following tags "case", "default", or "endswitch" to close the "switch" block started at line %d)', $lineno), -1); + } + } + } + + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + return new TwigNodeSwitch($name, new \Twig_Node($cases), $default, $lineno, $this->getTag()); + } + + /** + * Decide if current token marks switch logic. + * + * @param \Twig_Token $token + * @return bool + */ + public function decideIfFork(\Twig_Token $token) + { + return $token->test(array('case', 'default', 'endswitch')); + } + + /** + * Decide if current token marks end of swtich block. + * + * @param \Twig_Token $token + * @return bool + */ + public function decideIfEnd(\Twig_Token $token) + { + return $token->test(array('endswitch')); + } + + + /** + * {@inheritdoc} + */ + public function getTag() + { + return 'switch'; + } +} diff --git a/system/src/Grav/Common/Twig/TwigExtension.php b/system/src/Grav/Common/Twig/TwigExtension.php index df11cba8a6..23af8addc7 100644 --- a/system/src/Grav/Common/Twig/TwigExtension.php +++ b/system/src/Grav/Common/Twig/TwigExtension.php @@ -13,6 +13,7 @@ use Grav\Common\Page\Media; use Grav\Common\Twig\TokenParser\TwigTokenParserScript; use Grav\Common\Twig\TokenParser\TwigTokenParserStyle; +use Grav\Common\Twig\TokenParser\TwigTokenParserSwitch; use Grav\Common\Twig\TokenParser\TwigTokenParserTryCatch; use Grav\Common\Twig\TokenParser\TwigTokenParserMarkdown; use Grav\Common\Utils; @@ -158,6 +159,7 @@ public function getTokenParsers() new TwigTokenParserScript(), new TwigTokenParserStyle(), new TwigTokenParserMarkdown(), + new TwigTokenParserSwitch(), ]; }