diff options
Diffstat (limited to 'library/Director/Objects/IcingaObjectImports.php')
-rw-r--r-- | library/Director/Objects/IcingaObjectImports.php | 439 |
1 files changed, 439 insertions, 0 deletions
diff --git a/library/Director/Objects/IcingaObjectImports.php b/library/Director/Objects/IcingaObjectImports.php new file mode 100644 index 0000000..384fa1c --- /dev/null +++ b/library/Director/Objects/IcingaObjectImports.php @@ -0,0 +1,439 @@ +<?php + +namespace Icinga\Module\Director\Objects; + +use Countable; +use Exception; +use Icinga\Exception\NotFoundError; +use Iterator; +use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c; +use Icinga\Module\Director\IcingaConfig\IcingaConfigRenderer; +use Icinga\Module\Director\IcingaConfig\IcingaLegacyConfigHelper as c1; +use Icinga\Module\Director\Repository\IcingaTemplateRepository; +use RuntimeException; + +class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer +{ + protected $storedNames = []; + + /** @var array A list of our imports, key and value are the import name */ + protected $imports = []; + + /** @var IcingaObject[] A list of all objects we have seen, referred by name */ + protected $objects = []; + + protected $modified = false; + + /** @var IcingaObject The parent object */ + protected $object; + + private $position = 0; + + protected $idx = []; + + public function __construct(IcingaObject $object) + { + $this->object = $object; + } + + #[\ReturnTypeWillChange] + public function count() + { + return count($this->imports); + } + + #[\ReturnTypeWillChange] + public function rewind() + { + $this->position = 0; + } + + public function setModified() + { + $this->modified = true; + return $this; + } + + public function hasBeenModified() + { + return $this->modified; + } + + /** + * @return IcingaObject|null + * @throws \Icinga\Exception\NotFoundError + */ + #[\ReturnTypeWillChange] + public function current() + { + if (! $this->valid()) { + return null; + } + + return $this->getObject( + $this->imports[$this->idx[$this->position]] + ); + } + + #[\ReturnTypeWillChange] + public function key() + { + return $this->idx[$this->position]; + } + + #[\ReturnTypeWillChange] + public function next() + { + ++$this->position; + } + + #[\ReturnTypeWillChange] + public function valid() + { + return array_key_exists($this->position, $this->idx); + } + + /** + * @param $key + * @return IcingaObject|null + * @throws \Icinga\Exception\NotFoundError + */ + public function get($key) + { + if (array_key_exists($key, $this->imports)) { + return $this->getObject($this->imports[$key]); + } + + return null; + } + + public function set($import) + { + if (empty($import)) { + if (empty($this->imports)) { + return $this; + } else { + return $this->clear(); + } + } + + if (! is_array($import)) { + $import = [$import]; + } + + $existing = $this->listImportNames(); + $new = $this->listNamesForGivenImports($import); + + if ($existing === $new) { + return $this; + } + + $this->imports = []; + return $this->add($import); + } + + protected function listNamesForGivenImports($imports) + { + $list = []; + $class = $this->getImportClass(); + + foreach ($imports as $i) { + if ($i instanceof $class) { + $list[] = $i->object_name; + } else { + $list[] = $i; + } + } + + return $list; + } + + /** + * Magic isset check + * + * @param string $import + * + * @return boolean + */ + public function __isset($import) + { + return array_key_exists($import, $this->imports); + } + + public function clear() + { + if ($this->imports === []) { + return $this; + } + + $this->imports = []; + $this->modified = true; + + return $this->refreshIndex(); + } + + public function remove($import) + { + if (array_key_exists($import, $this->imports)) { + unset($this->imports[$import]); + } + + $this->modified = true; + + return $this->refreshIndex(); + } + + protected function refreshIndex() + { + $this->idx = array_keys($this->imports); + // $this->object->templateResolver()->refreshObject($this->object); + + return $this; + } + + public function add($import) + { + $class = $this->getImportClass(); + + if (is_array($import)) { + foreach ($import as $i) { + // Gracefully ignore null members or empty strings + if (! $i instanceof $class && ($i === null || strlen($i) === 0)) { + continue; + } + + $this->add($i); + } + + return $this; + } + + if ($import instanceof $class) { + $name = $import->object_name; + if (array_key_exists($name, $this->imports)) { + return $this; + } + + $this->imports[$name] = $name; + $this->objects[$name] = $import; + } elseif (is_string($import)) { + if (array_key_exists($import, $this->imports)) { + return $this; + } + + $this->imports[$import] = $import; + } + + $this->modified = true; + $this->refreshIndex(); + + return $this; + } + + /** + * @return IcingaObject[] + * @throws \Icinga\Exception\NotFoundError + */ + public function getObjects() + { + $list = []; + foreach ($this->listImportNames() as $name) { + $name = (string) $name; + $list[$name] = $this->getObject($name); + } + + return $list; + } + + /** + * @param $name + * @return IcingaObject + * @throws \Icinga\Exception\NotFoundError + */ + protected function getObject($name) + { + if (array_key_exists($name, $this->objects)) { + return $this->objects[$name]; + } + + $connection = $this->object->getConnection(); + /** @var IcingaObject $class */ + $class = $this->getImportClass(); + try { + if (is_array($this->object->getKeyName())) { + // Services only + $import = $class::load([ + 'object_name' => $name, + 'object_type' => 'template' + ], $connection); + } else { + $import = $class::load($name, $connection); + } + } catch (NotFoundError $e) { + throw new NotFoundError(sprintf( + 'Unable to load parent referenced from %s "%s", %s', + $this->object->getShortTableName(), + $this->object->getObjectName(), + lcfirst($e->getMessage()) + ), $e->getCode(), $e); + } + + return $this->objects[$import->getObjectName()] = $import; + } + + protected function getImportTableName() + { + return $this->object->getTableName() . '_inheritance'; + } + + public function listImportNames() + { + return array_keys($this->imports); + } + + public function listOriginalImportNames() + { + return $this->storedNames; + } + + public function getType() + { + return $this->object->getShortTableName(); + } + + protected function loadFromDb() + { + // $resolver = $this->object->templateResolver(); + // $this->objects = $resolver->fetchParents(); + $this->objects = IcingaTemplateRepository::instanceByObject($this->object) + ->getTemplatesIndexedByNameFor($this->object); + if (empty($this->objects)) { + $this->imports = []; + } else { + $keys = array_keys($this->objects); + $this->imports = array_combine($keys, $keys); + } + + $this->setBeingLoadedFromDb(); + return $this; + } + + /** + * @return bool + * @throws \Zend_Db_Adapter_Exception + * @throws \Icinga\Exception\NotFoundError + */ + public function store() + { + if (! $this->hasBeenModified()) { + return true; + } + + $objectId = $this->object->get('id'); + if ($objectId === null) { + throw new RuntimeException( + 'Cannot store imports for unstored object with no ID' + ); + } else { + $objectId = (int) $objectId; + } + + $type = $this->getType(); + + $objectCol = $type . '_id'; + $importCol = 'parent_' . $type . '_id'; + $table = $this->getImportTableName(); + $db = $this->object->getDb(); + + if ($this->object->hasBeenLoadedFromDb()) { + $db->delete( + $table, + $objectCol . ' = ' . $objectId + ); + } + + $weight = 1; + foreach ($this->getObjects() as $import) { + $db->insert($table, [ + $objectCol => $objectId, + $importCol => $import->get('id'), + 'weight' => $weight++ + ]); + } + + $this->setBeingLoadedFromDb(); + + return true; + } + + public function setBeingLoadedFromDb() + { + $this->storedNames = $this->listImportNames(); + $this->modified = false; + } + + protected function getImportClass() + { + return get_class($this->object); + } + + public static function loadForStoredObject(IcingaObject $object) + { + $obj = new static($object); + return $obj->loadFromDb(); + } + + public function toConfigString() + { + $ret = ''; + + foreach ($this->listImportNames() as $name) { + $ret .= ' import ' . c::renderString($name) . "\n"; + } + + if ($ret !== '') { + $ret .= "\n"; + } + return $ret; + } + + public function toLegacyConfigString() + { + $ret = ''; + + foreach ($this->listImportNames() as $name) { + $ret .= c1::renderKeyValue('use', c1::renderString($name)) . "\n"; + } + + if ($ret !== '') { + $ret .= "\n"; + } + return $ret; + } + + public function __toString() + { + try { + return $this->toConfigString(); + } catch (Exception $e) { + trigger_error($e); + $previousHandler = set_exception_handler( + function () { + } + ); + restore_error_handler(); + if ($previousHandler !== null) { + call_user_func($previousHandler, $e); + die(); + } else { + die($e->getMessage()); + } + } + } + + public function __destruct() + { + unset($this->object); + unset($this->objects); + } +} |