TYPO3  7.6
BooleanNode.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Fluid\Core\Parser\SyntaxTree;
3 
4 /* *
5  * This script is backported from the TYPO3 Flow package "TYPO3.Fluid". *
6  * *
7  * It is free software; you can redistribute it and/or modify it under *
8  * the terms of the GNU Lesser General Public License, either version 3 *
9  * of the License, or (at your option) any later version. *
10  * *
11  * The TYPO3 project - inspiring people to share! *
12  * */
13 
18 {
28  protected static $comparators = array('==', '!=', '%', '>=', '>', '<=', '<');
29 
37  ^ # Start with first input symbol
38  (?: # start repeat
39  COMPARATORS # We allow all comparators
40  |\s* # Arbitary spaces
41  |-? # Numbers, possibly with the "minus" symbol in front.
42  [0-9]+ # some digits
43  (?: # and optionally a dot, followed by some more digits
44  \\.
45  [0-9]+
46  )?
47  |\'[^\'\\\\]* # single quoted string literals with possibly escaped single quotes
48  (?:
49  \\\\. # escaped character
50  [^\'\\\\]* # unrolled loop following Jeffrey E.F. Friedl
51  )*\'
52  |"[^"\\\\]* # double quoted string literals with possibly escaped double quotes
53  (?:
54  \\\\. # escaped character
55  [^"\\\\]* # unrolled loop following Jeffrey E.F. Friedl
56  )*"
57  )*
58  $/x';
59 
65  protected $leftSide;
66 
72  protected $rightSide;
73 
81  protected $comparator;
82 
89  protected $syntaxTreeNode;
90 
98  public function __construct(\TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\AbstractNode $syntaxTreeNode)
99  {
100  $childNodes = $syntaxTreeNode->getChildNodes();
101  if (count($childNodes) > 3) {
102  throw new \TYPO3\CMS\Fluid\Core\Parser\Exception('A boolean expression has more than tree parts.', 1244201848);
103  } elseif (count($childNodes) === 0) {
104  // In this case, we do not have child nodes; i.e. the current SyntaxTreeNode
105  // is a text node with a literal comparison like "1 == 1"
106  $childNodes = array($syntaxTreeNode);
107  }
108 
109  $this->leftSide = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\RootNode();
110  $this->rightSide = new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\RootNode();
111  $this->comparator = null;
112  foreach ($childNodes as $childNode) {
113  if ($childNode instanceof \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\TextNode && !preg_match(str_replace('COMPARATORS', implode('|', self::$comparators), self::$booleanExpressionTextNodeCheckerRegularExpression), $childNode->getText())) {
114  // $childNode is text node, and no comparator found.
115  $this->comparator = null;
116  // skip loop and fall back to classical to boolean conversion.
117  break;
118  }
119 
120  if ($this->comparator !== null) {
121  // comparator already set, we are evaluating the right side of the comparator
122  $this->rightSide->addChildNode($childNode);
123  } elseif ($childNode instanceof \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\TextNode
124  && ($this->comparator = $this->getComparatorFromString($childNode->getText()))) {
125  // comparator in current string segment
126  $explodedString = explode($this->comparator, $childNode->getText());
127  if (isset($explodedString[0]) && trim($explodedString[0]) !== '') {
128  $value = trim($explodedString[0]);
129  if (is_numeric($value)) {
130  $this->leftSide->addChildNode(new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\NumericNode($value));
131  } else {
132  $this->leftSide->addChildNode(new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\TextNode(preg_replace('/(^[\'"]|[\'"]$)/', '', $value)));
133  }
134  }
135  if (isset($explodedString[1]) && trim($explodedString[1]) !== '') {
136  $value = trim($explodedString[1]);
137  if (is_numeric($value)) {
138  $this->rightSide->addChildNode(new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\NumericNode($value));
139  } else {
140  $this->rightSide->addChildNode(new \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\TextNode(preg_replace('/(^[\'"]|[\'"]$)/', '', $value)));
141  }
142  }
143  } else {
144  // comparator not found yet, on the left side of the comparator
145  $this->leftSide->addChildNode($childNode);
146  }
147  }
148 
149  if ($this->comparator === null) {
150  // No Comparator found, we need to evaluate the given syntax tree node manually
151  $this->syntaxTreeNode = $syntaxTreeNode;
152  }
153  }
154 
159  public function getComparator()
160  {
161  return $this->comparator;
162  }
163 
168  public function getSyntaxTreeNode()
169  {
170  return $this->syntaxTreeNode;
171  }
172 
177  public function getLeftSide()
178  {
179  return $this->leftSide;
180  }
181 
186  public function getRightSide()
187  {
188  return $this->rightSide;
189  }
190 
195  public function evaluate(\TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface $renderingContext)
196  {
197  if ($this->comparator !== null) {
198  return self::evaluateComparator($this->comparator, $this->leftSide->evaluate($renderingContext), $this->rightSide->evaluate($renderingContext));
199  } else {
200  $value = $this->syntaxTreeNode->evaluate($renderingContext);
201  return self::convertToBoolean($value);
202  }
203  }
204 
225  public static function evaluateComparator($comparator, $evaluatedLeftSide, $evaluatedRightSide)
226  {
227  switch ($comparator) {
228  case '==':
229  if (is_object($evaluatedLeftSide) || is_object($evaluatedRightSide)) {
230  return ($evaluatedLeftSide === $evaluatedRightSide);
231  } else {
232  return ($evaluatedLeftSide == $evaluatedRightSide);
233  }
234  case '!=':
235  if (is_object($evaluatedLeftSide) || is_object($evaluatedRightSide)) {
236  return ($evaluatedLeftSide !== $evaluatedRightSide);
237  } else {
238  return ($evaluatedLeftSide != $evaluatedRightSide);
239  }
240  case '%':
241  if (!self::isComparable($evaluatedLeftSide, $evaluatedRightSide)) {
242  return false;
243  }
244  return (bool)((int)$evaluatedLeftSide % (int)$evaluatedRightSide);
245  case '>':
246  if (!self::isComparable($evaluatedLeftSide, $evaluatedRightSide)) {
247  return false;
248  }
249  return $evaluatedLeftSide > $evaluatedRightSide;
250  case '>=':
251  if (!self::isComparable($evaluatedLeftSide, $evaluatedRightSide)) {
252  return false;
253  }
254  return $evaluatedLeftSide >= $evaluatedRightSide;
255  case '<':
256  if (!self::isComparable($evaluatedLeftSide, $evaluatedRightSide)) {
257  return false;
258  }
259  return $evaluatedLeftSide < $evaluatedRightSide;
260  case '<=':
261  if (!self::isComparable($evaluatedLeftSide, $evaluatedRightSide)) {
262  return false;
263  }
264  return $evaluatedLeftSide <= $evaluatedRightSide;
265  default:
266  throw new \TYPO3\CMS\Fluid\Core\Parser\Exception('Comparator "' . $comparator . '" is not implemented.', 1244234398);
267  }
268  }
269 
280  protected static function isComparable($evaluatedLeftSide, $evaluatedRightSide)
281  {
282  if ((is_null($evaluatedLeftSide) || is_string($evaluatedLeftSide))
283  && is_string($evaluatedRightSide)) {
284  return true;
285  }
286  if (is_bool($evaluatedLeftSide) || is_null($evaluatedLeftSide)) {
287  return true;
288  }
289  if (is_object($evaluatedLeftSide) && is_object($evaluatedRightSide)) {
290  return true;
291  }
292  if ((is_string($evaluatedLeftSide) || is_resource($evaluatedLeftSide) || is_numeric($evaluatedLeftSide))
293  && (is_string($evaluatedRightSide) || is_resource($evaluatedRightSide) || is_numeric($evaluatedRightSide))) {
294  return true;
295  }
296  if (is_array($evaluatedLeftSide) && is_array($evaluatedRightSide)) {
297  return true;
298  }
299  return false;
300  }
301 
308  protected function getComparatorFromString($string)
309  {
310  foreach (self::$comparators as $comparator) {
311  if (strpos($string, $comparator) !== false) {
312  return $comparator;
313  }
314  }
315  return null;
316  }
317 
326  public static function convertToBoolean($value)
327  {
328  if (is_bool($value)) {
329  return $value;
330  }
331 
332  if (is_integer($value) || is_float($value)) {
333  return !empty($value);
334  }
335 
336  if (is_numeric($value)) {
337  return ($value != 0);
338  }
339 
340  if (is_string($value)) {
341  return (!empty($value) && strtolower($value) !== 'false');
342  }
343  if (is_array($value) || (is_object($value) && $value instanceof \Countable)) {
344  return (bool)count($value);
345  }
346  if (is_object($value)) {
347  return true;
348  }
349 
350  return false;
351  }
352 }