492 lines
13 KiB
PHP
492 lines
13 KiB
PHP
<?php
|
|
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
|
|
|
namespace Icinga\Application;
|
|
|
|
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()
|
|
{
|
|
return $this->select()->count();
|
|
}
|
|
|
|
/**
|
|
* Reset the current position of the internal config object
|
|
*
|
|
* @return ConfigObject
|
|
*/
|
|
public function rewind()
|
|
{
|
|
return $this->config->rewind();
|
|
}
|
|
|
|
/**
|
|
* Return the section of the current iteration
|
|
*
|
|
* @return ConfigObject
|
|
*/
|
|
public function current()
|
|
{
|
|
return $this->config->current();
|
|
}
|
|
|
|
/**
|
|
* Return whether the position of the current iteration is valid
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function valid()
|
|
{
|
|
return $this->config->valid();
|
|
}
|
|
|
|
/**
|
|
* Return the section's name of the current iteration
|
|
*
|
|
* @return string
|
|
*/
|
|
public function key()
|
|
{
|
|
return $this->config->key();
|
|
}
|
|
|
|
/**
|
|
* Advance the position of the current iteration and return the new section
|
|
*
|
|
* @return ConfigObject
|
|
*/
|
|
public function next()
|
|
{
|
|
return $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 string $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();
|
|
}
|
|
|
|
$moduleConfigs = self::$modules[$modulename];
|
|
if (! isset($moduleConfigs[$configname]) || $fromDisk) {
|
|
$moduleConfigs[$configname] = static::fromIni(
|
|
static::resolvePath('modules/' . $modulename . '/' . $configname . '.ini')
|
|
);
|
|
}
|
|
return $moduleConfigs[$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';
|
|
}
|
|
|
|
return static::resolvePath(
|
|
($username ? 'preferences' . DIRECTORY_SEPARATOR . $username : 'navigation')
|
|
. DIRECTORY_SEPARATOR
|
|
. $filename
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Return this config rendered as a INI structured string
|
|
*
|
|
* @return string
|
|
*/
|
|
public function __toString()
|
|
{
|
|
return $this->getIniWriter()->render();
|
|
}
|
|
}
|