/** * Arranges the given search expression before it can be converted to its string representation * As IMAP SEARCH command uses prefixed OR operator in its search expressions we need to prepare * our search expression before it will be converted to a string. * Examples: * 'val1 OR val2' need to be changed to 'OR val1 val2' * 'val1 OR val2 OR val3' need to be changed to 'OR val1 OR val2 val3' * 'NOT val1 OR val2' need to be changed to 'OR NOT val1 val2' * * @param SearchQueryExpr $expr The search expression * @return SearchQueryExpr */ protected function arrangeExpr(SearchQueryExpr $expr) { // Make a clone of the expression and find out OR operators $result = new SearchQueryExpr(); $orOperatorPositions = array(); $i = 0; foreach ($expr as $item) { if ($item instanceof SearchQueryExprOperator) { $result->add($item); if ($item->getName() === 'OR') { $orOperatorPositions[] = $i; } } elseif ($item instanceof SearchQueryExpr) { $result->add($this->arrangeExpr($item)); } else { /** @var SearchQueryExprValueBase $item */ $value = $item->getValue(); if ($value instanceof SearchQueryExpr) { $item->setValue($this->arrangeExpr($value)); } $result->add($item); } $i++; } // Arrange OR operators is any foreach ($orOperatorPositions as $orPos) { $i = $orPos - 1; $parenthesisCounter = 0; while ($i >= 0) { $item = $result[$i]; if ($item instanceof SearchQueryExprOperator) { /** @var SearchQueryExprOperator $item */ switch ($item->getName()) { case '(': $parenthesisCounter--; break; case ')': $parenthesisCounter++; break; } } elseif ($parenthesisCounter === 0) { $this->moveOperator($result, $orPos, $this->correctNewPositionOfOperatorIfNeeded($result, $i)); break; } $i--; } if ($i === -1 && $parenthesisCounter === 0) { $this->moveOperator($result, $orPos, 0); } } return $result; }
/** * Adds close parenthesis ')'. */ public function closeParenthesis() { $this->expr->add(new SearchQueryExprOperator(')')); }