diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:43:12 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:43:12 +0000 |
commit | cd989f9c3aff968e19a3aeabc4eb9085787a6673 (patch) | |
tree | fbff2135e7013f196b891bbde54618eb050e4aaf /library/Director/CustomVariable | |
parent | Initial commit. (diff) | |
download | icingaweb2-module-director-cd989f9c3aff968e19a3aeabc4eb9085787a6673.tar.xz icingaweb2-module-director-cd989f9c3aff968e19a3aeabc4eb9085787a6673.zip |
Adding upstream version 1.10.2.upstream/1.10.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/Director/CustomVariable')
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(); + } + } +} |