summaryrefslogtreecommitdiffstats
path: root/library/Director/CustomVariable
diff options
context:
space:
mode:
Diffstat (limited to 'library/Director/CustomVariable')
-rw-r--r--library/Director/CustomVariable/CustomVariable.php286
-rw-r--r--library/Director/CustomVariable/CustomVariableArray.php100
-rw-r--r--library/Director/CustomVariable/CustomVariableBoolean.php53
-rw-r--r--library/Director/CustomVariable/CustomVariableDictionary.php130
-rw-r--r--library/Director/CustomVariable/CustomVariableNull.php52
-rw-r--r--library/Director/CustomVariable/CustomVariableNumber.php73
-rw-r--r--library/Director/CustomVariable/CustomVariableString.php59
-rw-r--r--library/Director/CustomVariable/CustomVariables.php488
8 files changed, 1241 insertions, 0 deletions
diff --git a/library/Director/CustomVariable/CustomVariable.php b/library/Director/CustomVariable/CustomVariable.php
new file mode 100644
index 0000000..98eda84
--- /dev/null
+++ b/library/Director/CustomVariable/CustomVariable.php
@@ -0,0 +1,286 @@
+<?php
+
+namespace Icinga\Module\Director\CustomVariable;
+
+use Exception;
+use Icinga\Module\Director\Db\Cache\PrefetchCache;
+use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
+use Icinga\Module\Director\IcingaConfig\IcingaConfigRenderer;
+use InvalidArgumentException;
+use LogicException;
+
+abstract class CustomVariable implements IcingaConfigRenderer
+{
+ protected $key;
+
+ protected $value;
+
+ protected $storedValue;
+
+ protected $type;
+
+ protected $modified = false;
+
+ protected $loadedFromDb = false;
+
+ protected $deleted = false;
+
+ protected $checksum;
+
+ protected function __construct($key, $value = null)
+ {
+ $this->key = $key;
+ $this->setValue($value);
+ }
+
+ public function is($type)
+ {
+ return $this->getType() === $type;
+ }
+
+ public function getType()
+ {
+ if ($this->type === null) {
+ $parts = explode('\\', get_class($this));
+ $class = end($parts);
+ // strlen('CustomVariable') === 14
+ $this->type = substr($class, 14);
+ }
+
+ return $this->type;
+ }
+
+ // TODO: implement delete()
+ public function hasBeenDeleted()
+ {
+ return $this->deleted;
+ }
+
+ public function delete()
+ {
+ $this->deleted = true;
+ return $this;
+ }
+
+ // TODO: abstract
+ public function getDbValue()
+ {
+ return $this->getValue();
+ }
+
+ public function toJson()
+ {
+ if ($this->getDbFormat() === 'string') {
+ return json_encode($this->getDbValue());
+ } else {
+ return $this->getDbValue();
+ }
+ }
+
+ // TODO: abstract
+ public function getDbFormat()
+ {
+ return 'string';
+ }
+
+ public function getKey()
+ {
+ return $this->key;
+ }
+
+ /**
+ * @param $value
+ * @return $this
+ */
+ abstract public function setValue($value);
+
+ abstract public function getValue();
+
+ /**
+ * @param bool $renderExpressions
+ * @return string
+ */
+ public function toConfigString($renderExpressions = false)
+ {
+ // TODO: this should be an abstract method once we deprecate PHP < 5.3.9
+ throw new LogicException(sprintf(
+ '%s has no toConfigString() implementation',
+ get_class($this)
+ ));
+ }
+
+ public function flatten(array &$flat, $prefix)
+ {
+ $flat[$prefix] = $this->getDbValue();
+ }
+
+ public function render($renderExpressions = false)
+ {
+ return c::renderKeyValue(
+ $this->renderKeyName($this->getKey()),
+ $this->toConfigStringPrefetchable($renderExpressions)
+ );
+ }
+
+ protected function renderKeyName($key)
+ {
+ if (preg_match('/^[a-z][a-z0-9_]*$/i', $key)) {
+ return 'vars.' . c::escapeIfReserved($key);
+ } else {
+ return 'vars[' . c::renderString($key) . ']';
+ }
+ }
+
+ public function checksum()
+ {
+ // TODO: remember checksum, invalidate on change
+ return sha1($this->getKey() . '=' . $this->toJson(), true);
+ }
+
+ public function isNew()
+ {
+ return ! $this->loadedFromDb;
+ }
+
+ public function hasBeenModified()
+ {
+ return $this->modified;
+ }
+
+ public function toConfigStringPrefetchable($renderExpressions = false)
+ {
+ if (PrefetchCache::shouldBeUsed()) {
+ return PrefetchCache::instance()->renderVar($this, $renderExpressions);
+ } else {
+ return $this->toConfigString($renderExpressions);
+ }
+ }
+
+ public function setModified($modified = true)
+ {
+ $this->modified = $modified;
+ if (! $this->modified) {
+ if (is_object($this->value)) {
+ $this->storedValue = clone($this->value);
+ } else {
+ $this->storedValue = $this->value;
+ }
+ }
+
+ return $this;
+ }
+
+ public function setUnmodified()
+ {
+ return $this->setModified(false);
+ }
+
+ public function setLoadedFromDb($loaded = true)
+ {
+ $this->loadedFromDb = $loaded;
+ return $this;
+ }
+
+ abstract public function equals(CustomVariable $var);
+
+ public function differsFrom(CustomVariable $var)
+ {
+ return ! $this->equals($var);
+ }
+
+ protected function setChecksum($checksum)
+ {
+ $this->checksum = $checksum;
+ return $this;
+ }
+
+ public function getChecksum()
+ {
+ return $this->checksum;
+ }
+
+ public static function wantCustomVariable($key, $value)
+ {
+ if ($value instanceof CustomVariable) {
+ return $value;
+ }
+
+ return self::create($key, $value);
+ }
+
+ public static function create($key, $value)
+ {
+ if (is_null($value)) {
+ return new CustomVariableNull($key, $value);
+ }
+
+ if (is_bool($value)) {
+ return new CustomVariableBoolean($key, $value);
+ }
+
+ if (is_int($value) || is_float($value)) {
+ return new CustomVariableNumber($key, $value);
+ }
+
+ if (is_string($value)) {
+ return new CustomVariableString($key, $value);
+ } elseif (is_array($value)) {
+ foreach (array_keys($value) as $k) {
+ if (! (is_int($k) || ctype_digit($k))) {
+ return new CustomVariableDictionary($key, $value);
+ }
+ }
+
+ return new CustomVariableArray($key, array_values($value));
+ } elseif (is_object($value)) {
+ // TODO: check for specific class/stdClass/interface?
+ return new CustomVariableDictionary($key, $value);
+ } else {
+ throw new LogicException(sprintf('WTF (%s): %s', $key, var_export($value, 1)));
+ }
+ }
+
+ public static function fromDbRow($row)
+ {
+ switch ($row->format) {
+ case 'string':
+ $var = new CustomVariableString($row->varname, $row->varvalue);
+ break;
+ case 'json':
+ $var = self::create($row->varname, json_decode($row->varvalue));
+ break;
+ case 'expression':
+ throw new InvalidArgumentException(
+ 'Icinga code expressions are not yet supported'
+ );
+ default:
+ throw new InvalidArgumentException(sprintf(
+ '%s is not a supported custom variable format',
+ $row->format
+ ));
+ }
+ if (property_exists($row, 'checksum')) {
+ $var->setChecksum($row->checksum);
+ }
+
+ $var->loadedFromDb = true;
+ $var->setUnmodified();
+ return $var;
+ }
+
+ public function __toString()
+ {
+ try {
+ return $this->toConfigString();
+ } catch (Exception $e) {
+ trigger_error($e);
+ $previousHandler = set_exception_handler(
+ function () {
+ }
+ );
+ restore_error_handler();
+ call_user_func($previousHandler, $e);
+ die();
+ }
+ }
+}
diff --git a/library/Director/CustomVariable/CustomVariableArray.php b/library/Director/CustomVariable/CustomVariableArray.php
new file mode 100644
index 0000000..7e430a4
--- /dev/null
+++ b/library/Director/CustomVariable/CustomVariableArray.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace Icinga\Module\Director\CustomVariable;
+
+use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
+use Icinga\Module\Director\IcingaConfig\IcingaLegacyConfigHelper as c1;
+
+class CustomVariableArray extends CustomVariable
+{
+ /** @var CustomVariable[] */
+ protected $value;
+
+ public function equals(CustomVariable $var)
+ {
+ if (! $var instanceof CustomVariableArray) {
+ return false;
+ }
+
+ return $var->getDbValue() === $this->getDbValue();
+ }
+
+ public function getValue()
+ {
+ $ret = array();
+ foreach ($this->value as $var) {
+ $ret[] = $var->getValue();
+ }
+
+ return $ret;
+ }
+
+ public function getDbValue()
+ {
+ return json_encode($this->getValue());
+ }
+
+ public function getDbFormat()
+ {
+ return 'json';
+ }
+
+ public function setValue($value)
+ {
+ $new = array();
+
+ foreach ($value as $k => $v) {
+ $new[] = self::wantCustomVariable($k, $v);
+ }
+
+ $equals = true;
+ if (is_array($this->value) && count($new) === count($this->value)) {
+ foreach ($this->value as $k => $v) {
+ if (! $new[$k]->equals($v)) {
+ $equals = false;
+ break;
+ }
+ }
+ } else {
+ $equals = false;
+ }
+
+ if (! $equals) {
+ $this->value = $new;
+ $this->setModified();
+ }
+
+ $this->deleted = false;
+
+ return $this;
+ }
+
+ public function flatten(array &$flat, $prefix)
+ {
+ foreach ($this->value as $k => $v) {
+ $v->flatten($flat, sprintf('%s[%d]', $prefix, $k));
+ }
+ }
+
+ public function toConfigString($renderExpressions = false)
+ {
+ $parts = array();
+ foreach ($this->value as $k => $v) {
+ $parts[] = $v->toConfigString($renderExpressions);
+ }
+
+ return c::renderEscapedArray($parts);
+ }
+
+ public function __clone()
+ {
+ foreach ($this->value as $key => $value) {
+ $this->value[$key] = clone($value);
+ }
+ }
+
+ public function toLegacyConfigString()
+ {
+ return c1::renderArray($this->value);
+ }
+}
diff --git a/library/Director/CustomVariable/CustomVariableBoolean.php b/library/Director/CustomVariable/CustomVariableBoolean.php
new file mode 100644
index 0000000..9953fae
--- /dev/null
+++ b/library/Director/CustomVariable/CustomVariableBoolean.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Icinga\Module\Director\CustomVariable;
+
+use Icinga\Exception\ProgrammingError;
+
+class CustomVariableBoolean extends CustomVariable
+{
+ public function equals(CustomVariable $var)
+ {
+ return $var->getValue() === $this->getValue();
+ }
+
+ public function getDbFormat()
+ {
+ return 'json';
+ }
+
+ public function getDbValue()
+ {
+ return json_encode($this->getValue());
+ }
+
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ public function setValue($value)
+ {
+ if (! is_bool($value)) {
+ throw new ProgrammingError(
+ 'Expected a boolean, got %s',
+ var_export($value, 1)
+ );
+ }
+
+ $this->value = $value;
+ $this->deleted = false;
+
+ return $this;
+ }
+
+ public function toConfigString($renderExpressions = false)
+ {
+ return $this->value ? 'true' : 'false';
+ }
+
+ public function toLegacyConfigString()
+ {
+ return $this->toConfigString();
+ }
+}
diff --git a/library/Director/CustomVariable/CustomVariableDictionary.php b/library/Director/CustomVariable/CustomVariableDictionary.php
new file mode 100644
index 0000000..d84be4f
--- /dev/null
+++ b/library/Director/CustomVariable/CustomVariableDictionary.php
@@ -0,0 +1,130 @@
+<?php
+
+namespace Icinga\Module\Director\CustomVariable;
+
+use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
+use Icinga\Module\Director\IcingaConfig\IcingaLegacyConfigHelper as c1;
+use Countable;
+
+class CustomVariableDictionary extends CustomVariable implements Countable
+{
+ /** @var CustomVariable[] */
+ protected $value;
+
+ public function equals(CustomVariable $var)
+ {
+ if (! $var instanceof CustomVariableDictionary) {
+ return false;
+ }
+
+ $myKeys = $this->listKeys();
+ $foreignKeys = $var->listKeys();
+ if ($myKeys !== $foreignKeys) {
+ return false;
+ }
+
+ foreach ($this->value as $key => $value) {
+ if (! $value->equals($var->getInternalValue($key))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public function getDbFormat()
+ {
+ return 'json';
+ }
+
+ public function getDbValue()
+ {
+ return json_encode($this->getValue());
+ }
+
+ public function setValue($value)
+ {
+ $new = array();
+
+ foreach ($value as $key => $val) {
+ $new[$key] = self::wantCustomVariable($key, $val);
+ }
+
+ $this->deleted = false;
+
+ // WTF?
+ if ($this->value === $new) {
+ return $this;
+ }
+
+ $this->value = $new;
+ $this->setModified();
+
+ return $this;
+ }
+
+ public function getValue()
+ {
+ $ret = (object) array();
+ ksort($this->value);
+
+ foreach ($this->value as $key => $var) {
+ $ret->$key = $var->getValue();
+ }
+
+ return $ret;
+ }
+
+ public function flatten(array &$flat, $prefix)
+ {
+ foreach ($this->value as $k => $v) {
+ $v->flatten($flat, sprintf('%s["%s"]', $prefix, $k));
+ }
+ }
+
+ public function listKeys()
+ {
+ $keys = array_keys($this->value);
+ sort($keys);
+ return $keys;
+ }
+
+ #[\ReturnTypeWillChange]
+ public function count()
+ {
+ return count($this->value);
+ }
+
+ public function __clone()
+ {
+ foreach ($this->value as $key => $value) {
+ $this->value[$key] = clone($value);
+ }
+ }
+
+ public function __get($key)
+ {
+ return $this->value[$key];
+ }
+
+ public function __isset($key)
+ {
+ return array_key_exists($key, $this->value);
+ }
+
+ public function getInternalValue($key)
+ {
+ return $this->value[$key];
+ }
+
+ public function toConfigString($renderExpressions = false)
+ {
+ // TODO
+ return c::renderDictionary($this->value);
+ }
+
+ public function toLegacyConfigString()
+ {
+ return c1::renderDictionary($this->value);
+ }
+}
diff --git a/library/Director/CustomVariable/CustomVariableNull.php b/library/Director/CustomVariable/CustomVariableNull.php
new file mode 100644
index 0000000..f87ccfa
--- /dev/null
+++ b/library/Director/CustomVariable/CustomVariableNull.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace Icinga\Module\Director\CustomVariable;
+
+use Icinga\Exception\ProgrammingError;
+
+class CustomVariableNull extends CustomVariable
+{
+ public function equals(CustomVariable $var)
+ {
+ return $var instanceof CustomVariableNull;
+ }
+
+ public function getValue()
+ {
+ return null;
+ }
+
+ public function getDbValue()
+ {
+ return json_encode($this->getValue());
+ }
+
+ public function getDbFormat()
+ {
+ return 'json';
+ }
+
+ public function setValue($value)
+ {
+ if (! is_null($value)) {
+ throw new ProgrammingError(
+ 'Null can only be null, got %s',
+ var_export($value, 1)
+ );
+ }
+
+ $this->deleted = false;
+
+ return $this;
+ }
+
+ public function toConfigString($renderExpressions = false)
+ {
+ return 'null';
+ }
+
+ public function toLegacyConfigString()
+ {
+ return $this->toConfigString();
+ }
+}
diff --git a/library/Director/CustomVariable/CustomVariableNumber.php b/library/Director/CustomVariable/CustomVariableNumber.php
new file mode 100644
index 0000000..62838a9
--- /dev/null
+++ b/library/Director/CustomVariable/CustomVariableNumber.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Icinga\Module\Director\CustomVariable;
+
+use Icinga\Exception\ProgrammingError;
+
+class CustomVariableNumber extends CustomVariable
+{
+ // Hint: 'F' is intentional, this MUST NOT respect locales
+ const PRECISION = '%.9F';
+
+ public function equals(CustomVariable $var)
+ {
+ if (! $var instanceof CustomVariableNumber) {
+ return false;
+ }
+
+ $cur = $this->getValue();
+ $new = $var->getValue();
+
+ // Be tolerant when comparing floats:
+ if (is_float($cur) || is_float($new)) {
+ return sprintf(self::PRECISION, $cur)
+ === sprintf(self::PRECISION, $new);
+ }
+
+ return $cur === $new;
+ }
+
+ public function getDbFormat()
+ {
+ return 'json';
+ }
+
+ public function getDbValue()
+ {
+ return json_encode($this->getValue());
+ }
+
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ public function setValue($value)
+ {
+ if (! is_int($value) && ! is_float($value)) {
+ throw new ProgrammingError(
+ 'Expected a number, got %s',
+ var_export($value, 1)
+ );
+ }
+
+ $this->value = $value;
+ $this->deleted = false;
+
+ return $this;
+ }
+
+ public function toConfigString($renderExpressions = false)
+ {
+ if (is_int($this->value)) {
+ return (string) $this->value;
+ } else {
+ return sprintf(self::PRECISION, $this->value);
+ }
+ }
+
+ public function toLegacyConfigString()
+ {
+ return $this->toConfigString();
+ }
+}
diff --git a/library/Director/CustomVariable/CustomVariableString.php b/library/Director/CustomVariable/CustomVariableString.php
new file mode 100644
index 0000000..2d50968
--- /dev/null
+++ b/library/Director/CustomVariable/CustomVariableString.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Icinga\Module\Director\CustomVariable;
+
+use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
+use Icinga\Module\Director\IcingaConfig\IcingaLegacyConfigHelper as c1;
+
+class CustomVariableString extends CustomVariable
+{
+ public function equals(CustomVariable $var)
+ {
+ if (! $var instanceof CustomVariableString) {
+ return false;
+ }
+
+ return $var->getValue() === $this->getValue();
+ }
+
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ public function setValue($value)
+ {
+ if (! is_string($value)) {
+ $value = (string) $value;
+ }
+
+ if ($value !== $this->value) {
+ $this->value = $value;
+ $this->setModified();
+ }
+
+ $this->deleted = false;
+
+ return $this;
+ }
+
+ public function flatten(array &$flat, $prefix)
+ {
+ // TODO: we should get rid of type=string and always use JSON
+ $flat[$prefix] = json_encode($this->getValue());
+ }
+
+ public function toConfigString($renderExpressions = false)
+ {
+ if ($renderExpressions) {
+ return c::renderStringWithVariables($this->getValue(), ['config']);
+ } else {
+ return c::renderString($this->getValue());
+ }
+ }
+
+ public function toLegacyConfigString()
+ {
+ return c1::renderString($this->getValue());
+ }
+}
diff --git a/library/Director/CustomVariable/CustomVariables.php b/library/Director/CustomVariable/CustomVariables.php
new file mode 100644
index 0000000..cdcc4bd
--- /dev/null
+++ b/library/Director/CustomVariable/CustomVariables.php
@@ -0,0 +1,488 @@
+<?php
+
+namespace Icinga\Module\Director\CustomVariable;
+
+use Icinga\Module\Director\Db;
+use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
+use Icinga\Module\Director\IcingaConfig\IcingaLegacyConfigHelper as c1;
+use Icinga\Module\Director\IcingaConfig\IcingaConfigRenderer;
+use Icinga\Module\Director\Objects\IcingaObject;
+use Countable;
+use Exception;
+use Iterator;
+
+class CustomVariables implements Iterator, Countable, IcingaConfigRenderer
+{
+ /** @var CustomVariable[] */
+ protected $storedVars = array();
+
+ /** @var CustomVariable[] */
+ protected $vars = array();
+
+ protected $modified = false;
+
+ private $position = 0;
+
+ private $overrideKeyName;
+
+ protected $idx = array();
+
+ protected static $allTables = array(
+ 'icinga_command_var',
+ 'icinga_host_var',
+ 'icinga_notification_var',
+ 'icinga_service_set_var',
+ 'icinga_service_var',
+ 'icinga_user_var',
+ );
+
+ public static function countAll($varname, Db $connection)
+ {
+ $db = $connection->getDbAdapter();
+ $parts = array();
+ $where = $db->quoteInto('varname = ?', $varname);
+ foreach (static::$allTables as $table) {
+ $parts[] = "SELECT COUNT(*) as cnt FROM $table WHERE $where";
+ }
+
+ $sub = implode(' UNION ALL ', $parts);
+ $query = "SELECT SUM(sub.cnt) AS cnt FROM ($sub) sub";
+
+ return (int) $db->fetchOne($query);
+ }
+
+ public static function deleteAll($varname, Db $connection)
+ {
+ $db = $connection->getDbAdapter();
+ $where = $db->quoteInto('varname = ?', $varname);
+ foreach (static::$allTables as $table) {
+ $db->delete($table, $where);
+ }
+ }
+
+ public static function renameAll($oldname, $newname, Db $connection)
+ {
+ $db = $connection->getDbAdapter();
+ $where = $db->quoteInto('varname = ?', $oldname);
+ foreach (static::$allTables as $table) {
+ $db->update($table, ['varname' => $newname], $where);
+ }
+ }
+
+ #[\ReturnTypeWillChange]
+ public function count()
+ {
+ $count = 0;
+ foreach ($this->vars as $var) {
+ if (! $var->hasBeenDeleted()) {
+ $count++;
+ }
+ }
+
+ return $count;
+ }
+
+ #[\ReturnTypeWillChange]
+ public function rewind()
+ {
+ $this->position = 0;
+ }
+
+ #[\ReturnTypeWillChange]
+ public function current()
+ {
+ if (! $this->valid()) {
+ return null;
+ }
+
+ return $this->vars[$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);
+ }
+
+ /**
+ * Generic setter
+ *
+ * @param string $key
+ * @param mixed $value
+ *
+ * @return self
+ */
+ public function set($key, $value)
+ {
+ $key = (string) $key;
+
+ if ($value instanceof CustomVariable) {
+ $value = clone($value);
+ } else {
+ if ($value === null) {
+ $this->__unset($key);
+ return $this;
+ }
+ $value = CustomVariable::create($key, $value);
+ }
+
+ // Hint: isset($this->$key) wouldn't conflict with protected properties
+ if ($this->__isset($key)) {
+ if ($value->equals($this->get($key))) {
+ return $this;
+ } else {
+ if (get_class($this->vars[$key]) === get_class($value)) {
+ $this->vars[$key]->setValue($value->getValue())->setModified();
+ } else {
+ $this->vars[$key] = $value->setLoadedFromDb()->setModified();
+ }
+ }
+ } else {
+ $this->vars[$key] = $value->setModified();
+ }
+
+ $this->modified = true;
+ $this->refreshIndex();
+
+ return $this;
+ }
+
+ protected function refreshIndex()
+ {
+ $this->idx = array();
+ ksort($this->vars);
+ foreach ($this->vars as $name => $var) {
+ if (! $var->hasBeenDeleted()) {
+ $this->idx[] = $name;
+ }
+ }
+ }
+
+ public static function loadForStoredObject(IcingaObject $object)
+ {
+ $db = $object->getDb();
+
+ $query = $db->select()->from(
+ array('v' => $object->getVarsTableName()),
+ array(
+ 'v.varname',
+ 'v.varvalue',
+ 'v.format',
+ )
+ )->where(sprintf('v.%s = ?', $object->getVarsIdColumn()), $object->get('id'));
+
+ $vars = new CustomVariables;
+ foreach ($db->fetchAll($query) as $row) {
+ $vars->vars[$row->varname] = CustomVariable::fromDbRow($row);
+ }
+ $vars->refreshIndex();
+ $vars->setBeingLoadedFromDb();
+ return $vars;
+ }
+
+ public static function forStoredRows($rows)
+ {
+ $vars = new CustomVariables;
+ foreach ($rows as $row) {
+ $vars->vars[$row->varname] = CustomVariable::fromDbRow($row);
+ }
+ $vars->refreshIndex();
+ $vars->setBeingLoadedFromDb();
+
+ return $vars;
+ }
+
+ public function storeToDb(IcingaObject $object)
+ {
+ $db = $object->getDb();
+ $table = $object->getVarsTableName();
+ $foreignColumn = $object->getVarsIdColumn();
+ $foreignId = $object->get('id');
+
+
+ foreach ($this->vars as $var) {
+ if ($var->isNew()) {
+ $db->insert(
+ $table,
+ array(
+ $foreignColumn => $foreignId,
+ 'varname' => $var->getKey(),
+ 'varvalue' => $var->getDbValue(),
+ 'format' => $var->getDbFormat()
+ )
+ );
+ $var->setLoadedFromDb();
+ continue;
+ }
+
+ $where = $db->quoteInto(sprintf('%s = ?', $foreignColumn), (int) $foreignId)
+ . $db->quoteInto(' AND varname = ?', $var->getKey());
+
+ if ($var->hasBeenDeleted()) {
+ $db->delete($table, $where);
+ } elseif ($var->hasBeenModified()) {
+ $db->update(
+ $table,
+ array(
+ 'varvalue' => $var->getDbValue(),
+ 'format' => $var->getDbFormat()
+ ),
+ $where
+ );
+ }
+ }
+
+ $this->setBeingLoadedFromDb();
+ }
+
+ public function get($key)
+ {
+ if (array_key_exists($key, $this->vars)) {
+ return $this->vars[$key];
+ }
+
+ return null;
+ }
+
+ public function hasBeenModified()
+ {
+ if ($this->modified) {
+ return true;
+ }
+
+ foreach ($this->vars as $var) {
+ if ($var->hasBeenModified()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public function setBeingLoadedFromDb()
+ {
+ $this->modified = false;
+ $this->storedVars = array();
+ foreach ($this->vars as $key => $var) {
+ $this->storedVars[$key] = clone($var);
+ $var->setUnmodified();
+ $var->setLoadedFromDb();
+ }
+
+ return $this;
+ }
+
+ public function restoreStoredVar($key)
+ {
+ if (array_key_exists($key, $this->storedVars)) {
+ $this->vars[$key] = clone($this->storedVars[$key]);
+ $this->vars[$key]->setUnmodified();
+ $this->recheckForModifications();
+ $this->refreshIndex();
+ } elseif (array_key_exists($key, $this->vars)) {
+ unset($this->vars[$key]);
+ $this->recheckForModifications();
+ $this->refreshIndex();
+ }
+ }
+
+ protected function recheckForModifications()
+ {
+ $this->modified = false;
+ foreach ($this->vars as $var) {
+ if ($var->hasBeenModified()) {
+ $this->modified = true;
+
+ return;
+ }
+ }
+ }
+
+ public function getOriginalVars()
+ {
+ return $this->storedVars;
+ }
+
+ public function flatten()
+ {
+ $flat = array();
+ foreach ($this->vars as $key => $var) {
+ $var->flatten($flat, $key);
+ }
+
+ return $flat;
+ }
+
+ public function checksum()
+ {
+ $sums = array();
+ foreach ($this->vars as $key => $var) {
+ $sums[] = $key . '=' . $var->checksum();
+ }
+
+ return sha1(implode('|', $sums), true);
+ }
+
+ public function setOverrideKeyName($name)
+ {
+ $this->overrideKeyName = $name;
+ return $this;
+ }
+
+ public function toConfigString($renderExpressions = false)
+ {
+ $out = '';
+
+ foreach ($this as $key => $var) {
+ // TODO: ctype_alnum + underscore?
+ $out .= $this->renderSingleVar($key, $var, $renderExpressions);
+ }
+
+ return $out;
+ }
+
+ public function toLegacyConfigString()
+ {
+ $out = '';
+
+ ksort($this->vars);
+ foreach ($this->vars as $key => $var) {
+ // TODO: ctype_alnum + underscore?
+ // vars with ARGn will be handled by IcingaObject::renderLegacyCheck_command
+ if (substr($key, 0, 3) == 'ARG') {
+ continue;
+ }
+
+ switch ($type = $var->getType()) {
+ case 'String':
+ case 'Number':
+ # TODO: Make Prefetchable
+ $out .= c1::renderKeyValue(
+ '_' . $key,
+ $var->toLegacyConfigString()
+ );
+ break;
+ default:
+ $out .= c1::renderKeyValue(
+ '# _' . $key,
+ sprintf('(unsupported: %s)', $type)
+ );
+ }
+ }
+
+ if ($out !== '') {
+ $out = "\n".$out;
+ }
+
+ return $out;
+ }
+
+ /**
+ * @param string $key
+ * @param CustomVariable $var
+ * @param bool $renderExpressions
+ *
+ * @return string
+ */
+ protected function renderSingleVar($key, $var, $renderExpressions = false)
+ {
+ if ($key === $this->overrideKeyName) {
+ return c::renderKeyOperatorValue(
+ $this->renderKeyName($key),
+ '+=',
+ $var->toConfigStringPrefetchable($renderExpressions)
+ );
+ } else {
+ return c::renderKeyValue(
+ $this->renderKeyName($key),
+ $var->toConfigStringPrefetchable($renderExpressions)
+ );
+ }
+ }
+
+ protected function renderKeyName($key)
+ {
+ if (preg_match('/^[a-z][a-z0-9_]*$/i', $key)) {
+ return 'vars.' . c::escapeIfReserved($key);
+ } else {
+ return 'vars[' . c::renderString($key) . ']';
+ }
+ }
+
+ public function __get($key)
+ {
+ return $this->get($key);
+ }
+
+ /**
+ * Magic setter
+ *
+ * @param string $key Key
+ * @param mixed $val Value
+ *
+ * @return void
+ */
+ public function __set($key, $val)
+ {
+ $this->set($key, $val);
+ }
+
+ /**
+ * Magic isset check
+ *
+ * @param string $key
+ *
+ * @return boolean
+ */
+ public function __isset($key)
+ {
+ return array_key_exists($key, $this->vars);
+ }
+
+ /**
+ * Magic unsetter
+ *
+ * @param string $key
+ *
+ * @return void
+ */
+ public function __unset($key)
+ {
+ if (! array_key_exists($key, $this->vars)) {
+ return;
+ }
+
+ $this->vars[$key]->delete();
+ $this->modified = true;
+
+ $this->refreshIndex();
+ }
+
+ public function __toString()
+ {
+ try {
+ return $this->toConfigString();
+ } catch (Exception $e) {
+ trigger_error($e);
+ $previousHandler = set_exception_handler(
+ function () {
+ }
+ );
+ restore_error_handler();
+ call_user_func($previousHandler, $e);
+ die();
+ }
+ }
+}