diff options
Diffstat (limited to 'library/Icinga/Application/Config.php')
-rw-r--r-- | library/Icinga/Application/Config.php | 498 |
1 files changed, 498 insertions, 0 deletions
diff --git a/library/Icinga/Application/Config.php b/library/Icinga/Application/Config.php new file mode 100644 index 0000000..80fe3b8 --- /dev/null +++ b/library/Icinga/Application/Config.php @@ -0,0 +1,498 @@ +<?php +/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Application; + +use Icinga\Exception\NotWritableError; +use Iterator; +use Countable; +use LogicException; +use UnexpectedValueException; +use Icinga\Util\File; +use Icinga\Data\ConfigObject; +use Icinga\Data\Selectable; +use Icinga\Data\SimpleQuery; +use Icinga\File\Ini\IniWriter; +use Icinga\File\Ini\IniParser; +use Icinga\Exception\IcingaException; +use Icinga\Exception\NotReadableError; +use Icinga\Web\Navigation\Navigation; + +/** + * Container for INI like configuration and global registry of application and module related configuration. + */ +class Config implements Countable, Iterator, Selectable +{ + /** + * Configuration directory where ALL (application and module) configuration is located + * + * @var string + */ + public static $configDir; + + /** + * Application config instances per file + * + * @var array + */ + protected static $app = array(); + + /** + * Module config instances per file + * + * @var array + */ + protected static $modules = array(); + + /** + * Navigation config instances per type + * + * @var array + */ + protected static $navigation = array(); + + /** + * The internal ConfigObject + * + * @var ConfigObject + */ + protected $config; + + /** + * The INI file this config has been loaded from or should be written to + * + * @var string + */ + protected $configFile; + + /** + * Create a new config + * + * @param ConfigObject $config The config object to handle + */ + public function __construct(ConfigObject $config = null) + { + $this->config = $config !== null ? $config : new ConfigObject(); + } + + /** + * Return this config's file path + * + * @return string + */ + public function getConfigFile() + { + return $this->configFile; + } + + /** + * Set this config's file path + * + * @param string $filepath The path to the ini file + * + * @return $this + */ + public function setConfigFile($filepath) + { + $this->configFile = $filepath; + return $this; + } + + /** + * Return the internal ConfigObject + * + * @return ConfigObject + */ + public function getConfigObject() + { + return $this->config; + } + + /** + * Provide a query for the internal config object + * + * @return SimpleQuery + */ + public function select() + { + return $this->config->select(); + } + + /** + * Return the count of available sections + * + * @return int + */ + public function count(): int + { + return $this->select()->count(); + } + + /** + * Reset the current position of the internal config object + * + * @return void + */ + public function rewind(): void + { + $this->config->rewind(); + } + + /** + * Return the section of the current iteration + * + * @return ConfigObject + */ + public function current(): ConfigObject + { + return $this->config->current(); + } + + /** + * Return whether the position of the current iteration is valid + * + * @return bool + */ + public function valid(): bool + { + return $this->config->valid(); + } + + /** + * Return the section's name of the current iteration + * + * @return string + */ + public function key(): string + { + return $this->config->key(); + } + + /** + * Advance the position of the current iteration and return the new section + * + * @return void + */ + public function next(): void + { + $this->config->next(); + } + + /** + * Return whether this config has any sections + * + * @return bool + */ + public function isEmpty() + { + return $this->config->isEmpty(); + } + + /** + * Return this config's section names + * + * @return array + */ + public function keys() + { + return $this->config->keys(); + } + + /** + * Return this config's data as associative array + * + * @return array + */ + public function toArray() + { + return $this->config->toArray(); + } + + /** + * Return the value from a section's property + * + * @param string $section The section where the given property can be found + * @param string $key The section's property to fetch the value from + * @param mixed $default The value to return in case the section or the property is missing + * + * @return mixed + * + * @throws UnexpectedValueException In case the given section does not hold any configuration + */ + public function get($section, $key, $default = null) + { + $value = $this->config->$section; + if ($value instanceof ConfigObject) { + $value = $value->$key; + } elseif ($value !== null) { + throw new UnexpectedValueException( + sprintf('Value "%s" is not of type "%s" or a sub-type of it', $value, get_class($this->config)) + ); + } + + if ($value === null && $default !== null) { + $value = $default; + } + + return $value; + } + + /** + * Return the given section + * + * @param string $name The section's name + * + * @return ConfigObject + */ + public function getSection($name) + { + $section = $this->config->get($name); + return $section !== null ? $section : new ConfigObject(); + } + + /** + * Set or replace a section + * + * @param string $name + * @param array|ConfigObject $config + * + * @return $this + */ + public function setSection($name, $config = null) + { + if ($config === null) { + $config = new ConfigObject(); + } elseif (! $config instanceof ConfigObject) { + $config = new ConfigObject($config); + } + + $this->config->$name = $config; + return $this; + } + + /** + * Remove a section + * + * @param string $name + * + * @return $this + */ + public function removeSection($name) + { + unset($this->config->$name); + return $this; + } + + /** + * Return whether the given section exists + * + * @param string $name + * + * @return bool + */ + public function hasSection($name) + { + return isset($this->config->$name); + } + + /** + * Initialize a new config using the given array + * + * The returned config has no file associated to it. + * + * @param array $array The array to initialize the config with + * + * @return Config + */ + public static function fromArray(array $array) + { + return new static(new ConfigObject($array)); + } + + /** + * Load configuration from the given INI file + * + * @param string $file The file to parse + * + * @throws NotReadableError When the file cannot be read + */ + public static function fromIni($file) + { + $emptyConfig = new static(); + + $filepath = realpath($file); + if ($filepath === false) { + $emptyConfig->setConfigFile($file); + } elseif (is_readable($filepath)) { + return IniParser::parseIniFile($filepath); + } elseif (@file_exists($filepath)) { + throw new NotReadableError(t('Cannot read config file "%s". Permission denied'), $filepath); + } + + return $emptyConfig; + } + + /** + * Save configuration to the given INI file + * + * @param string|null $filePath The path to the INI file or null in case this config's path should be used + * @param int $fileMode The file mode to store the file with + * + * @throws LogicException In case this config has no path and none is passed in either + * @throws NotWritableError In case the INI file cannot be written + * + * @todo create basepath and throw NotWritableError in case its not possible + */ + public function saveIni($filePath = null, $fileMode = 0660) + { + if ($filePath === null && $this->configFile) { + $filePath = $this->configFile; + } elseif ($filePath === null) { + throw new LogicException('You need to pass $filePath or set a path using Config::setConfigFile()'); + } + + if (! file_exists($filePath)) { + File::create($filePath, $fileMode); + } + + $this->getIniWriter($filePath, $fileMode)->write(); + } + + /** + * Return a IniWriter for this config + * + * @param string|null $filePath + * @param int $fileMode + * + * @return IniWriter + */ + protected function getIniWriter($filePath = null, $fileMode = null) + { + return new IniWriter($this, $filePath, $fileMode); + } + + /** + * Prepend configuration base dir to the given relative path + * + * @param string $path A relative path + * + * @return string + */ + public static function resolvePath($path) + { + return self::$configDir . DIRECTORY_SEPARATOR . ltrim($path, DIRECTORY_SEPARATOR); + } + + /** + * Retrieve a application config + * + * @param string $configname The configuration name (without ini suffix) to read and return + * @param bool $fromDisk When set true, the configuration will be read from disk, even + * if it already has been read + * + * @return Config The requested configuration + */ + public static function app($configname = 'config', $fromDisk = false) + { + if (! isset(self::$app[$configname]) || $fromDisk) { + self::$app[$configname] = static::fromIni(static::resolvePath($configname . '.ini')); + } + + return self::$app[$configname]; + } + + /** + * Retrieve a module config + * + * @param string $modulename The name of the module where to look for the requested configuration + * @param string $configname The configuration name (without ini suffix) to read and return + * @param bool $fromDisk When set true, the configuration will be read from disk, even + * if it already has been read + * + * @return Config The requested configuration + */ + public static function module($modulename, $configname = 'config', $fromDisk = false) + { + if (! isset(self::$modules[$modulename])) { + self::$modules[$modulename] = array(); + } + + if (! isset(self::$modules[$modulename][$configname]) || $fromDisk) { + self::$modules[$modulename][$configname] = static::fromIni( + static::resolvePath('modules/' . $modulename . '/' . $configname . '.ini') + ); + } + return self::$modules[$modulename][$configname]; + } + + /** + * Retrieve a navigation config + * + * @param string $type The type identifier of the navigation item for which to return its config + * @param string $username A user's name or null if the shared config is desired + * @param bool $fromDisk If true, the configuration will be read from disk + * + * @return Config The requested configuration + */ + public static function navigation($type, $username = null, $fromDisk = false) + { + if (! isset(self::$navigation[$type])) { + self::$navigation[$type] = array(); + } + + $branch = $username ?: 'shared'; + $typeConfigs = self::$navigation[$type]; + if (! isset($typeConfigs[$branch]) || $fromDisk) { + $typeConfigs[$branch] = static::fromIni(static::getNavigationConfigPath($type, $username)); + } + + return $typeConfigs[$branch]; + } + + /** + * Return the path to the configuration file for the given navigation item type and user + * + * @param string $type + * @param string $username + * + * @return string + * + * @throws IcingaException In case the given type is unknown + */ + protected static function getNavigationConfigPath($type, $username = null) + { + $itemTypeConfig = Navigation::getItemTypeConfiguration(); + if (! isset($itemTypeConfig[$type])) { + throw new IcingaException('Invalid navigation item type %s provided', $type); + } + + if (isset($itemTypeConfig[$type]['config'])) { + $filename = $itemTypeConfig[$type]['config'] . '.ini'; + } else { + $filename = $type . 's.ini'; + } + + if ($username) { + $path = static::resolvePath(implode(DIRECTORY_SEPARATOR, array('preferences', $username, $filename))); + if (realpath($path) === false) { + $path = static::resolvePath(implode( + DIRECTORY_SEPARATOR, + array('preferences', strtolower($username), $filename) + )); + } + } else { + $path = static::resolvePath('navigation' . DIRECTORY_SEPARATOR . $filename); + } + return $path; + } + + /** + * Return this config rendered as a INI structured string + * + * @return string + */ + public function __toString() + { + return $this->getIniWriter()->render(); + } +} |