* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 * @author Icinga Development Team */ // {{{ICINGA_LICENSE_HEADER}}} namespace Icinga\Protocol\Statusdat; use Icinga\Protocol; use Icinga\Data\BaseQuery; /** * Class Query * @package Icinga\Protocol\Statusdat */ class Query extends BaseQuery { /** * @var array */ public static $VALID_TARGETS = array( "hosts" => array("host"), "services" => array("service"), "downtimes" => array("hostdowntime", "servicedowntime"), "hostdowntimes" => array("hostdowntime"), "servicedowntimes" => array("servicedowntime"), "hostgroups" => array("hostgroup"), "servicegroups" => array("servicegroup"), "comments" => array("servicecomment", "hostcomment"), "hostcomments" => array("hostcomment"), "servicecomments" => array("servicecomment") ); /** * @var IReader|null */ private $reader = null; /** * @var string */ private $source = ""; /** * @var null */ private $limit = null; /** * @var int */ private $offset = 0; /** * @var array */ protected $orderColumns = array(); /** * @var array */ private $groupColumns = array(); /** * @var null */ private $groupByFn = null; /** * @var array */ private $filter = array(); /** * */ const FN_SCOPE = 0; /** * */ const FN_NAME = 1; /** * @return bool */ public function hasOrder() { return !empty($this->orderColumns); } /** * @return bool */ public function hasColumns() { return !empty($this->columns); } /** * @return array */ public function getColumns() { return $this->columns; } /** * @return bool */ public function hasLimit() { return $this->limit !== false; } /** * @return bool */ public function hasOffset() { return $this->offset !== false; } /** * @return null */ public function getLimit() { return $this->limit; } /** * @return int|null */ public function getOffset() { return $this->offset; } /** * @param $key * @param null $val * @return $this */ public function where($key, $val = null) { $this->filter[] = array($key, $val); return $this; } /** * @param $columns * @param null $dir * @return $this */ public function order($columns, $dir = null) { if ($dir && strtolower($dir) == "desc") { $dir = self::SORT_DESC; } else { $dir = self::SORT_ASC; } if (!is_array($columns)) { $columns = array($columns); } foreach ($columns as $col) { if (($pos = strpos($col, ' ')) !== false) { $dir = strtoupper(substr($col, $pos + 1)); if ($dir === 'DESC') { $dir = self::SORT_DESC; } else { $dir = self::SORT_ASC; } $col = substr($col, 0, $pos); } else { $col = $col; } $this->orderColumns[] = array($col, $dir); } return $this; } /** * @param null $count * @param int $offset * @return $this * @throws Exception */ public function limit($count = null, $offset = 0) { if ((is_null($count) || is_integer($count)) && (is_null($offset) || is_integer($offset))) { $this->offset = $offset; $this->limit = $count; } else { throw new Exception("Got invalid limit $count, $offset"); } return $this; } /** * @param $table * @param null $columns * @return $this * @throws \Exception */ public function from($table, $columns = null) { if (isset(self::$VALID_TARGETS[$table])) { $this->source = $table; } else { throw new \Exception("Unknown from target for status.dat :" . $table); } return $this; } /** * * @throws Exception */ private function getFilteredIndices($classType = "\Icinga\Protocol\Statusdat\Query\Group") { $baseGroup = null; if (!empty($this->filter)) { $baseGroup = new $classType(); foreach ($this->filter as $values) { $baseGroup->addItem(new $classType($values[0], $values[1])); } } $state = $this->ds->getObjects(); $result = array(); $source = self::$VALID_TARGETS[$this->source]; foreach ($source as $target) { $indexes = array_keys($state[$target]); if ($baseGroup) { $indexes = $baseGroup->filter($state[$target]); } if (!isset($result[$target])) { $result[$target] = $indexes; } else { array_merge($result[$target], $indexes); } } return $result; } /** * @param array $indices */ private function orderIndices(array &$indices) { if (!empty($this->orderColumns)) { foreach ($indices as $type => &$subindices) { $this->currentType = $type; // we're singlethreaded, so let's do it a bit dirty usort($subindices, array($this, "orderResult")); } } } /** * @param $a * @param $b * @return int */ private function orderResult($a, $b) { $o1 = $this->ds->getObjectByName($this->currentType, $a); $o2 = $this->ds->getObjectByName($this->currentType, $b); $result = 0; foreach ($this->orderColumns as $col) { $result += $col[1] * strnatcasecmp($o1->{$col[0]}, $o2->{$col[0]}); } if ($result > 0) { return 1; } if ($result < 0) { return -1; } return 0; } /** * @param array $indices */ private function limitIndices(array &$indices) { foreach ($indices as $type => $subindices) { $indices[$type] = array_slice($subindices, $this->offset, $this->limit); } } /** * @param $fn * @param null $scope * @return $this */ public function groupByFunction($fn, $scope = null) { $this->groupByFn = array($scope ? $scope : $this, $fn); return $this; } /** * @param $columns * @return $this */ public function groupByColumns($columns) { if (!is_array($columns)) { $columns = array($columns); } $this->groupColumns = $columns; $this->groupByFn = array($this, "columnGroupFn"); return $this; } /** * @param array $indices * @return array */ private function columnGroupFn(array &$indices) { $cols = $this->groupColumns; $result = array(); foreach ($indices as $type => $subindices) { foreach ($subindices as $objectIndex) { $r = $this->ds->getObjectByName($type, $objectIndex); $hash = ""; $cols = array(); foreach ($this->groupColumns as $col) { $hash = md5($hash . $r->$col); $cols[$col] = $r->$col; } if (!isset($result[$hash])) { $result[$hash] = (object)array( "columns" => (object)$cols, "count" => 0 ); } $result[$hash]->count++; } } return array_values($result); } /** * @return array */ public function getResult() { $indices = $this->getFilteredIndices(); $this->orderIndices($indices); if ($this->groupByFn) { $scope = $this->groupByFn[self::FN_SCOPE]; $fn = $this->groupByFn[self::FN_NAME]; return $scope->$fn($indices); } $this->limitIndices($indices); $result = array(); $state = $this->ds->getObjects(); foreach ($indices as $type => $subindices) { foreach ($subindices as $index) { $result[] = & $state[$type][$index]; } } return $result; } }