icingaweb2-module-director/library/Director/CustomVariable/CustomVariable.php

287 lines
6.9 KiB
PHP

<?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();
}
}
}