icingaweb2/library/Icinga/Application/Config.php

468 lines
12 KiB
PHP
Raw Normal View History

<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Application;
use Iterator;
use Countable;
use ArrayAccess;
use LogicException;
use UnexpectedValueException;
use Icinga\Exception\NotReadableError;
/**
* Container for configuration values and global registry of application and module related configuration.
*/
class Config implements Countable, Iterator, ArrayAccess
{
/**
2013-07-12 15:37:36 +02:00
* 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();
/**
* This config's data
*
* @var array
*/
protected $data;
/**
* The INI file this configuration has been loaded from or should be written to
*
* @var string
*/
protected $configFile;
/**
* Create a new config
*
* @param array $data The data to initialize the new config with
*/
public function __construct(array $data = array())
{
$this->data = array();
foreach ($data as $key => $value) {
if (is_array($value)) {
$this->data[$key] = new static($value);
} else {
$this->data[$key] = $value;
}
}
}
/**
* 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 config file
*
* @return self
*/
public function setConfigFile($filepath)
{
$this->configFile = $filepath;
return $this;
}
/**
* Deep clone this config
*/
public function __clone()
{
$array = array();
foreach ($this->data as $key => $value) {
if ($value instanceof self) {
$array[$key] = clone $value;
} else {
$array[$key] = $value;
}
}
$this->data = $array;
}
/**
* Return the count of available sections and properties
*
* @return int
*/
public function count()
{
return count($this->data);
}
/**
* Reset the current position of $this->data
*
* @return mixed
*/
public function rewind()
{
return reset($this->data);
}
/**
* Return the section's or property's value of the current iteration
*
* @return mixed
*/
public function current()
{
return current($this->data);
}
/**
* Return whether the position of the current iteration is valid
*
* @return bool
*/
public function valid()
{
return key($this->data) !== null;
}
/**
* Return the section's or property's name of the current iteration
*
* @return mixed
*/
public function key()
{
return key($this->data);
}
/**
* Advance the position of the current iteration and return the new section's or property's value
*
* @return mixed
*/
public function next()
{
return next($this->data);
}
/**
* Return whether the given section or property is set
*
* @param string $key The name of the section or property
*
* @return bool
*/
public function __isset($key)
{
return isset($this->data[$key]);
}
/**
* Return the value for the given property or the config for the given section
*
* @param string $key The name of the property or section
*
* @return mixed|NULL The value or NULL in case $key does not exist
*/
public function __get($key)
{
if (array_key_exists($key, $this->data)) {
return $this->data[$key];
}
}
/**
* Add a new property or section
*
* @param string $key The name of the new property or section
* @param mixed $value The value to set for the new property or section
*/
public function __set($key, $value)
{
if (is_array($value)) {
$this->data[$key] = new static($value);
} else {
$this->data[$key] = $value;
}
}
/**
* Remove the given property or section
*
* @param string $key The property or section to remove
*/
public function __unset($key)
{
unset($this->data[$key]);
}
/**
* Return whether the given section or property is set
*
* @param string $key The name of the section or property
*
* @return bool
*/
public function offsetExists($key)
{
return isset($this->$key);
}
/**
* Return the value for the given property or the config for the given section
*
* @param string $key The name of the property or section
*
* @return mixed|NULL The value or NULL in case $key does not exist
*/
public function offsetGet($key)
{
return $this->$key;
}
/**
* Add a new property or section
*
* @param string $key The name of the new property or section
* @param mixed $value The value to set for the new property or section
*/
public function offsetSet($key, $value)
{
if ($key === null) {
throw new LogicException('Appending values without an explicit key is not supported');
2013-07-12 12:11:59 +02:00
}
$this->$key = $value;
}
/**
* Remove the given property or section
*
* @param string $key The property or section to remove
*/
public function offsetUnset($key)
{
unset($this->$key);
}
/**
* Return whether this config has any data
*
* @return bool
*/
public function isEmpty()
{
return $this->count() === 0;
}
/**
* Return the value for the given property or the config for the given section
*
* @param string $key The name of the property or section
* @param mixed $default The value to return in case the property or section is missing
*
* @return mixed
*/
public function get($key, $default = null)
{
$value = $this->$key;
if ($default !== null && $value === null) {
$value = $default;
}
return $value;
}
/**
* Return all section and property names
*
* @return array
*/
public function keys()
{
return array_keys($this->data);
}
/**
* Return this config's data as associative array
*
* @return array
*/
public function toArray()
{
$array = array();
foreach ($this->data as $key => $value) {
if ($value instanceof self) {
$array[$key] = $value->toArray();
} else {
$array[$key] = $value;
}
}
return $array;
}
/**
* Merge the given data with this config
*
* @param array|Config $data An array or a config
*
* @return self
*/
public function merge($data)
{
if ($data instanceof self) {
$data = $data->toArray();
}
foreach ($data as $key => $value) {
if (array_key_exists($key, $this->data)) {
if (is_array($value)) {
if ($this->data[$key] instanceof self) {
$this->data[$key]->merge($value);
} else {
$this->data[$key] = new static($value);
}
} else {
$this->data[$key] = $value;
}
} else {
$this->data[$key] = is_array($value) ? new static($value) : $value;
}
}
}
/**
* 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 fromSection($section, $key, $default = null)
{
$value = $this->$section;
if ($value instanceof self) {
$value = $value->$key;
} elseif ($value !== null) {
throw new UnexpectedValueException(
sprintf('Value "%s" is not of type "Config" or a sub-type of it', $value)
);
}
if ($value === null && $default !== null) {
$value = $default;
}
return $value;
}
/**
* Load configuration from the given INI file
*
* @param string $file The file to parse
*
* @throws NotReadableError When the file does not exist or cannot be read
*/
public static function fromIni($file)
{
$config = new static();
$filepath = realpath($file);
if ($filepath === false) {
$config->setConfigFile($file);
} elseif (is_readable($filepath)) {
$config->setConfigFile($filepath);
$config->merge(parse_ini_file($filepath, true));
} else {
throw new NotReadableError(t('Cannot read config file "%s". Permission denied'), $filepath);
}
return $config;
}
/**
* 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];
}
}