[FEATURE] Support literal strings in boolean arguments
authorChristian Kuhn <lolli@schwarzbu.ch>
Sat, 9 Feb 2013 13:01:42 +0000 (14:01 +0100)
committerChristian Kuhn <lolli@schwarzbu.ch>
Sat, 9 Feb 2013 17:02:40 +0000 (18:02 +0100)
This change adds support for string literals in boolean comparisons.
String literals have to be surrounded by single or double quotes and
may contain escaped single or double quotes.

This is possible now:
<f:if condition="{foo} == 'foo'">

This is a port of the feature from FLOW.Fluid 1.2 from issue #6757.

Change-Id: I1799ff8c4187d33f47a2251404e59f87ff9cba1b
Resolves: #45316
Releases: 6.1
Reviewed-on: https://review.typo3.org/18156
Reviewed-by: Wouter Wolters
Tested-by: Wouter Wolters
Reviewed-by: Markus Günther
Tested-by: Markus Günther
Reviewed-by: Christian Kuhn
Tested-by: Christian Kuhn
typo3/sysext/fluid/Classes/Core/Compiler/TemplateCompiler.php
typo3/sysext/fluid/Classes/Core/Parser/SyntaxTree/BooleanNode.php
typo3/sysext/fluid/Classes/Core/Parser/SyntaxTree/NumericNode.php [new file with mode: 0644]
typo3/sysext/fluid/Tests/Unit/Core/Parser/SyntaxTree/BooleanNodeTest.php
typo3/sysext/fluid/Tests/Unit/Core/Parser/SyntaxTree/NumericNodeTest.php [new file with mode: 0644]

index 0578433..79cd0c5 100644 (file)
@@ -134,6 +134,8 @@ return %s;
        protected function convert(\TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\AbstractNode $node) {
                if ($node instanceof \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\TextNode) {
                        return $this->convertTextNode($node);
+               } elseif ($node instanceof \TYPO3\Fluid\Core\Parser\SyntaxTree\NumericNode) {
+                       return $this->convertNumericNode($node);
                } elseif ($node instanceof \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\ViewHelperNode) {
                        return $this->convertViewHelperNode($node);
                } elseif ($node instanceof \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\ObjectAccessorNode) {
@@ -162,6 +164,18 @@ return %s;
        }
 
        /**
+        * @param \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\NumericNode $node
+        * @return array
+        * @see convert()
+        */
+       protected function convertNumericNode(\TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\NumericNode $node) {
+               return array(
+                       'initialization' => '',
+                       'execution' => $node->getValue()
+               );
+       }
+
+       /**
         * Convert a single ViewHelperNode into its cached representation. If the ViewHelper implements the "Compilable" facet,
         * the ViewHelper itself is asked for its cached PHP code representation. If not, a ViewHelper is built and then invoked.
         *
index 977174d..2c8225a 100644 (file)
@@ -36,13 +36,23 @@ class BooleanNode extends \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\AbstractNode {
                ^                 # Start with first input symbol
                (?:               # start repeat
                        COMPARATORS   # We allow all comparators
-                       |\\s*          # Arbitary spaces
+                       |\s*          # Arbitary spaces
                        |-?           # Numbers, possibly with the "minus" symbol in front.
                                [0-9]+    # some digits
                                (?:       # and optionally a dot, followed by some more digits
                                        \\.
                                        [0-9]+
                                )?
+                       |\'[^\'\\\\]* # single quoted string literals with possibly escaped single quotes
+                               (?:
+                                       \\\\.      # escaped character
+                                       [^\'\\\\]* # unrolled loop following Jeffrey E.F. Friedl
+                               )*\'
+                       |"[^"\\\\]*   # double quoted string literals with possibly escaped double quotes
+                               (?:
+                                       \\\\.     # escaped character
+                                       [^"\\\\]* # unrolled loop following Jeffrey E.F. Friedl
+                               )*"
                )*
                $/x';
 
@@ -110,10 +120,20 @@ class BooleanNode extends \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\AbstractNode {
                                // comparator in current string segment
                                $explodedString = explode($this->comparator, $childNode->getText());
                                if (isset($explodedString[0]) && trim($explodedString[0]) !== '') {
-                                       $this->leftSide->addChildNode(new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\TextNode(trim($explodedString[0])));
+                                       $value = trim($explodedString[0]);
+                                       if (is_numeric($value)) {
+                                               $this->leftSide->addChildNode(new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\NumericNode($value));
+                                       } else {
+                                               $this->leftSide->addChildNode(new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\TextNode(preg_replace('/(^[\'"]|[\'"]$)/', '', $value)));
+                                       }
                                }
                                if (isset($explodedString[1]) && trim($explodedString[1]) !== '') {
-                                       $this->rightSide->addChildNode(new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\TextNode(trim($explodedString[1])));
+                                       $value = trim($explodedString[1]);
+                                       if (is_numeric($value)) {
+                                               $this->rightSide->addChildNode(new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\NumericNode($value));
+                                       } else {
+                                               $this->rightSide->addChildNode(new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\TextNode(preg_replace('/(^[\'"]|[\'"]$)/', '', $value)));
+                                       }
                                }
                        } else {
                                // comparator not found yet, on the left side of the comparator
diff --git a/typo3/sysext/fluid/Classes/Core/Parser/SyntaxTree/NumericNode.php b/typo3/sysext/fluid/Classes/Core/Parser/SyntaxTree/NumericNode.php
new file mode 100644 (file)
index 0000000..6451bc2
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+namespace TYPO3\CMS\Fluid\Core\Parser\SyntaxTree;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow package "Fluid".                 *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * Numeric Syntax Tree Node - is a container for numerics.
+ *
+ */
+class NumericNode extends \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\AbstractNode {
+
+       /**
+        * Contents of the numeric node
+        * @var number
+        */
+       protected $value;
+
+       /**
+        * Constructor.
+        *
+        * @param string|number $value value to store in this numericNode
+        * @throws \TYPO3\CMS\Fluid\Core\Parser\Exception
+        */
+       public function __construct($value) {
+               if (!is_numeric($value)) {
+                       throw new \TYPO3\CMS\Fluid\Core\Parser\Exception('Numeric node requires an argument of type number, "' . gettype($value) . '" given.', 1360414192);
+               }
+               $this->value = $value + 0;
+       }
+
+       /**
+        * Return the value associated to the syntax tree.
+        *
+        * @param \TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface $renderingContext
+        * @return number the value stored in this node/subtree.
+        */
+       public function evaluate(\TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface $renderingContext) {
+               return $this->value;
+       }
+
+       /**
+        * Getter for value
+        *
+        * @return number The value of this node
+        */
+       public function getValue() {
+               return $this->value;
+       }
+
+       /**
+        * NumericNode does not allow adding child nodes, so this will always throw an exception.
+        *
+        * @param \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\NodeInterface $childNode The subnode to add
+        * @throws \TYPO3\CMS\Fluid\Core\Parser\Exception
+        * @return void
+        */
+       public function addChildNode(\TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\NodeInterface $childNode) {
+               throw new \TYPO3\CMS\Fluid\Core\Parser\Exception('Numeric nodes may not contain child nodes, tried to add "' . get_class($childNode) . '".', 1360414193);
+       }
+}
+
+?>
\ No newline at end of file
index 91249a2..fc8f8b3 100644 (file)
@@ -247,6 +247,83 @@ class BooleanNodeTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
        /**
         * @test
         */
+       public function notEqualReturnsFalseIfComparingMatchingStrings() {
+               $rootNode = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\RootNode();
+               $rootNode->addChildNode(new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\TextNode('\'stringA\' != "stringA"'));
+
+               $booleanNode = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\BooleanNode($rootNode);
+               $this->assertFalse($booleanNode->evaluate($this->renderingContext));
+       }
+
+       /**
+        * @test
+        */
+       public function notEqualReturnsTrueIfComparingNonMatchingStrings() {
+               $rootNode = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\RootNode();
+               $rootNode->addChildNode(new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\TextNode('\'stringA\' != \'stringB\''));
+
+               $booleanNode = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\BooleanNode($rootNode);
+               $this->assertTrue($booleanNode->evaluate($this->renderingContext));
+       }
+
+       /**
+        * @test
+        */
+       public function equalsReturnsFalseIfComparingNonMatchingStrings() {
+               $rootNode = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\RootNode();
+               $rootNode->addChildNode(new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\TextNode('\'stringA\' == \'stringB\''));
+
+               $booleanNode = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\BooleanNode($rootNode);
+               $this->assertFalse($booleanNode->evaluate($this->renderingContext));
+       }
+
+       /**
+        * @test
+        */
+       public function equalsReturnsTrueIfComparingMatchingStrings() {
+               $rootNode = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\RootNode();
+               $rootNode->addChildNode(new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\TextNode('\'stringA\' == "stringA"'));
+
+               $booleanNode = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\BooleanNode($rootNode);
+               $this->assertTrue($booleanNode->evaluate($this->renderingContext));
+       }
+
+       /**
+        * @test
+        */
+       public function equalsReturnsTrueIfComparingMatchingStringsWithEscapedQuotes() {
+               $rootNode = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\RootNode();
+               $rootNode->addChildNode(new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\TextNode('\'\\\'stringA\\\'\' == \'\\\'stringA\\\'\''));
+
+               $booleanNode = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\BooleanNode($rootNode);
+               $this->assertTrue($booleanNode->evaluate($this->renderingContext));
+       }
+
+       /**
+        * @test
+        */
+       public function equalsReturnsFalseIfComparingStringWithNonZero() {
+               $rootNode = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\RootNode();
+               $rootNode->addChildNode(new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\TextNode('\'stringA\' == 42'));
+
+               $booleanNode = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\BooleanNode($rootNode);
+               $this->assertFalse($booleanNode->evaluate($this->renderingContext));
+       }
+
+       /**
+        * @test
+        */
+       public function equalsReturnsTrueIfComparingStringWithZero() {
+               $rootNode = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\RootNode();
+               $rootNode->addChildNode(new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\TextNode('\'stringA\' == 0'));
+
+               $booleanNode = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\BooleanNode($rootNode);
+               $this->assertTrue($booleanNode->evaluate($this->renderingContext));
+       }
+
+       /**
+        * @test
+        */
        public function objectsAreComparedStrictly() {
                $object1 = new \stdClass();
                $object2 = new \stdClass();
diff --git a/typo3/sysext/fluid/Tests/Unit/Core/Parser/SyntaxTree/NumericNodeTest.php b/typo3/sysext/fluid/Tests/Unit/Core/Parser/SyntaxTree/NumericNodeTest.php
new file mode 100644 (file)
index 0000000..08aa633
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+namespace TYPO3\CMS\Fluid\Tests\Unit\Core\Parser\SyntaxTree;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow package "Fluid".                 *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * Testcase for NumericNode
+ *
+ */
+class NumericNodeTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
+
+       /**
+        * @test
+        */
+       public function renderReturnsProperIntegerGivenInConstructor() {
+               $string = '1';
+               $node = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\NumericNode($string);
+               $this->assertEquals($node->evaluate($this->getMock('TYPO3\CMS\Fluid\Core\Rendering\RenderingContext')), 1, 'The rendered value of a numeric node does not match the string given in the constructor.');
+       }
+
+       /**
+        * @test
+        */
+       public function renderReturnsProperFloatGivenInConstructor() {
+               $string = '1.1';
+               $node = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\NumericNode($string);
+               $this->assertEquals($node->evaluate($this->getMock('TYPO3\CMS\Fluid\Core\Rendering\RenderingContext')), 1.1, 'The rendered value of a numeric node does not match the string given in the constructor.');
+       }
+
+       /**
+        * @test
+        * @expectedException \TYPO3\CMS\Fluid\Core\Parser\Exception
+        */
+       public function constructorThrowsExceptionIfNoNumericGiven() {
+               new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\NumericNode('foo');
+       }
+
+       /**
+        * @test
+        * @expectedException \TYPO3\CMS\Fluid\Core\Parser\Exception
+        */
+       public function addChildNodeThrowsException() {
+               $node = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\NumericNode('1');
+               $node->addChildNode(clone $node);
+       }
+}
+?>
\ No newline at end of file