* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 * @author Icinga Development Team * */ // {{{ICINGA_LICENSE_HEADER}}} namespace Icinga\Protocol\Statusdat\Query; /** * Class Group * @package Icinga\Protocol\Statusdat\Query */ class Group implements IQueryPart { /** * */ const GROUP_BEGIN = "("; /** * */ const GROUP_END = ")"; /** * */ const CONJUNCTION_AND = "AND "; /** * */ const CONJUNCTION_OR = "OR "; /** * */ const EXPRESSION = 0; /** * */ const EOF = -1; /** * */ const TYPE_AND = "AND"; /** * */ const TYPE_OR = "OR"; /** * @var array */ private $items = array(); /** * @var int */ private $parsePos = 0; /** * @var string */ private $expression = ""; /** * @var null */ private $expressionClass = null; /** * @var string */ private $type = ""; /** * @var int */ private $subExpressionStart = 0; /** * @var int */ private $subExpressionLength = 0; /** * Optional query to use * * @var Query */ private $query = null; /** * @var */ private $value; /** * @param $value */ public function setValue($value) { $this->value = $value; } /** * @return array */ public function getItems() { return $this->items; } /** * @return string */ public function getType() { return $this->type ? $this->type : self::TYPE_AND; } /** * @param $type */ public function setType($type) { $this->type = $type; } /** * @throws \Exception */ private function tokenize() { $token = 0; $subgroupCount = 0; while ($token != self::EOF) { $token = $this->getNextToken(); if ($token === self::GROUP_BEGIN) { /** * check if this is a nested group, if so then it's * considered part of the subexpression */ if ($subgroupCount == 0) { $this->startNewSubExpression(); } $subgroupCount++; continue; } if ($token === self::GROUP_END) { if ($subgroupCount < 1) { throw new \Exception("Invalid Query: unexpected ')' at pos " . $this->parsePos); } $subgroupCount--; /* * check if this is a nested group, if so then it's * considered part of the subexpression */ if ($subgroupCount == 0) { $this->addSubgroupFromExpression(); } continue; } if ($token === self::CONJUNCTION_AND && $subgroupCount == 0) { $this->startNewSubExpression(); if ($this->type != self::TYPE_AND && $this->type != "") { $this->createImplicitGroup(self::TYPE_AND); break; } else { $this->type = self::TYPE_AND; } continue; } if ($token === self::CONJUNCTION_OR && $subgroupCount == 0) { $this->startNewSubExpression(); if ($this->type != self::TYPE_OR && $this->type != "") { $this->createImplicitGroup(self::TYPE_OR); break; } else { $this->type = self::TYPE_OR; } continue; } $this->subExpressionLength = $this->parsePos - $this->subExpressionStart; } if ($subgroupCount > 0) { throw new \Exception("Unexpected end of query, are you missing a parenthesis?"); } $this->startNewSubExpression(); } /** * @param $type */ private function createImplicitGroup($type) { $group = new Group(); $group->setType($type); $group->addItem(array_pop($this->items)); $group->fromString(substr($this->expression, $this->parsePos), $this->value, $this->expressionClass); $this->items[] = $group; $this->parsePos = strlen($this->expression); } /** * */ private function startNewSubExpression() { if ($this->getCurrentSubExpression() != "") { if (!$this->expressionClass) { $this->items[] = new Expression($this->getCurrentSubExpression(), $this->value); } else { $this->items[] = new $this->expressionClass($this->getCurrentSubExpression(), $this->value); } } $this->subExpressionStart = $this->parsePos; $this->subExpressionLength = 0; } /** * @return string */ private function getCurrentSubExpression() { return substr($this->expression, $this->subExpressionStart, $this->subExpressionLength); } /** * */ private function addSubgroupFromExpression() { if (!$this->expressionClass) { $this->items[] = new Group($this->getCurrentSubExpression(), $this->value); } else { $group = new Group(); $group->fromString($this->getCurrentSubExpression(), $this->value, $this->expressionClass); $this->items[] = $group; } $this->subExpressionStart = $this->parsePos; $this->subExpressionLength = 0; } /** * @return bool */ private function isEOF() { if ($this->parsePos >= strlen($this->expression)) { return true; } return false; } /** * @return int|string */ private function getNextToken() { if ($this->isEOF()) { return self::EOF; } // skip whitespaces while ($this->expression[$this->parsePos] == " ") { $this->parsePos++; if ($this->isEOF()) { return self::EOF; } } if ($this->expression[$this->parsePos] == self::GROUP_BEGIN) { $this->parsePos++; return self::GROUP_BEGIN; } if ($this->expression[$this->parsePos] == self::GROUP_END) { $this->parsePos++; return self::GROUP_END; } if (substr_compare( $this->expression, self::CONJUNCTION_AND, $this->parsePos, strlen(self::CONJUNCTION_AND), true ) === 0) { $this->parsePos += strlen(self::CONJUNCTION_AND); return self::CONJUNCTION_AND; } if (substr_compare( $this->expression, self::CONJUNCTION_OR, $this->parsePos, strlen(self::CONJUNCTION_OR), true ) === 0) { $this->parsePos += strlen(self::CONJUNCTION_OR); return self::CONJUNCTION_OR; } $this->parsePos++; return self::EXPRESSION; } /** * @param $ex * @return $this */ public function addItem($ex) { $this->items[] = $ex; return $this; } /** * @param $expression * @param array $value * @param null $expressionClass * @return $this */ public function fromString($expression, &$value = array(), $expressionClass = null) { $this->expression = $expression; $this->value = & $value; $this->expressionClass = $expressionClass; $this->tokenize(); return $this; } /** * @param null $expression * @param array $value */ public function __construct($expression = null, &$value = array()) { if ($expression) { $this->fromString($expression, $value); } } /** * @param array $base * @param null $idx * @return array|null */ public function filter(array &$base, &$idx = null) { if ($this->type == self::TYPE_OR) { $idx = array(); foreach ($this->items as &$subFilter) { $baseKeys = array_keys($base); $subFilter->setQuery($this->query); $idx += $subFilter->filter($base, $baseKeys); } } else { if (!$idx) { $idx = array_keys($base); } foreach ($this->items as $subFilter) { $subFilter->setQuery($this->query); $idx = array_intersect($idx, $subFilter->filter($base, $idx)); } } return $idx; } /** * Add additional information about the query this filter belongs to * * @param $query * @return mixed */ public function setQuery($query) { $this->query = $query; } }