2013-06-07 11:44:37 +02:00
|
|
|
<?php
|
2013-07-10 11:40:48 +02:00
|
|
|
// {{{ICINGA_LICENSE_HEADER}}}
|
|
|
|
// {{{ICINGA_LICENSE_HEADER}}}
|
2013-06-07 11:44:37 +02:00
|
|
|
|
2013-08-12 15:02:25 +02:00
|
|
|
namespace Icinga\Application;
|
2013-06-07 11:44:37 +02:00
|
|
|
|
2014-11-07 13:37:09 +01:00
|
|
|
use Iterator;
|
2014-11-06 15:41:31 +01:00
|
|
|
use Countable;
|
|
|
|
use ArrayAccess;
|
|
|
|
use LogicException;
|
|
|
|
use UnexpectedValueException;
|
2014-02-20 13:53:28 +01:00
|
|
|
use Icinga\Exception\NotReadableError;
|
2013-06-07 11:44:37 +02:00
|
|
|
|
2013-07-10 11:40:48 +02:00
|
|
|
/**
|
2014-11-06 15:41:31 +01:00
|
|
|
* Container for configuration values and global registry of application and module related configuration.
|
2013-07-10 11:40:48 +02:00
|
|
|
*/
|
2014-11-07 13:37:09 +01:00
|
|
|
class Config implements Countable, Iterator, ArrayAccess
|
2013-06-07 11:44:37 +02:00
|
|
|
{
|
2013-07-10 11:40:48 +02:00
|
|
|
/**
|
2013-07-12 15:37:36 +02:00
|
|
|
* Configuration directory where ALL (application and module) configuration is located
|
2013-08-26 16:56:23 +02:00
|
|
|
*
|
2013-07-10 11:40:48 +02:00
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
public static $configDir;
|
2013-06-07 11:44:37 +02:00
|
|
|
|
2014-11-06 15:41:31 +01:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
|
2013-07-10 11:40:48 +02:00
|
|
|
/**
|
2014-08-29 12:23:48 +02:00
|
|
|
* The INI file this configuration has been loaded from or should be written to
|
2013-08-26 16:56:23 +02:00
|
|
|
*
|
2013-07-10 11:40:48 +02:00
|
|
|
* @var string
|
|
|
|
*/
|
2014-08-29 12:23:48 +02:00
|
|
|
protected $configFile;
|
2013-06-07 11:44:37 +02:00
|
|
|
|
2013-07-10 11:40:48 +02:00
|
|
|
/**
|
2014-11-06 15:41:31 +01:00
|
|
|
* Create a new config
|
2013-08-26 16:56:23 +02:00
|
|
|
*
|
2014-11-06 15:41:31 +01:00
|
|
|
* @param array $data The data to initialize the new config with
|
2013-07-10 11:40:48 +02:00
|
|
|
*/
|
2014-11-06 15:41:31 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-07-10 11:40:48 +02:00
|
|
|
|
|
|
|
/**
|
2014-11-06 15:41:31 +01:00
|
|
|
* Return this config's file path
|
2013-08-26 16:56:23 +02:00
|
|
|
*
|
2014-11-06 15:41:31 +01:00
|
|
|
* @return string
|
2013-07-10 11:40:48 +02:00
|
|
|
*/
|
2014-11-06 15:41:31 +01:00
|
|
|
public function getConfigFile()
|
|
|
|
{
|
|
|
|
return $this->configFile;
|
|
|
|
}
|
2013-06-07 11:44:37 +02:00
|
|
|
|
2013-07-10 11:40:48 +02:00
|
|
|
/**
|
2014-11-06 15:41:31 +01:00
|
|
|
* Set this config's file path
|
2014-08-29 12:23:48 +02:00
|
|
|
*
|
2014-11-06 15:41:31 +01:00
|
|
|
* @param string $filepath The path to the config file
|
2013-07-10 11:40:48 +02:00
|
|
|
*
|
2014-11-06 15:41:31 +01:00
|
|
|
* @return self
|
2013-07-10 11:40:48 +02:00
|
|
|
*/
|
2014-11-06 15:41:31 +01:00
|
|
|
public function setConfigFile($filepath)
|
2013-06-07 11:44:37 +02:00
|
|
|
{
|
2014-11-06 15:41:31 +01:00
|
|
|
$this->configFile = $filepath;
|
|
|
|
return $this;
|
|
|
|
}
|
2014-08-29 12:23:48 +02:00
|
|
|
|
2014-11-06 15:41:31 +01:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-11-07 13:37:09 +01:00
|
|
|
* Reset the current position of $this->data
|
2014-11-06 15:41:31 +01:00
|
|
|
*
|
2014-11-07 13:37:09 +01:00
|
|
|
* @return mixed
|
2014-11-06 15:41:31 +01:00
|
|
|
*/
|
2014-11-07 13:37:09 +01:00
|
|
|
public function rewind()
|
2014-11-06 15:41:31 +01:00
|
|
|
{
|
2014-11-07 13:37:09 +01:00
|
|
|
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);
|
2014-11-06 15:41:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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);
|
2014-03-11 15:43:41 +01:00
|
|
|
} else {
|
2014-11-06 15:41:31 +01:00
|
|
|
$this->data[$key] = $value;
|
2014-03-11 15:43:41 +01:00
|
|
|
}
|
2014-11-06 15:41:31 +01:00
|
|
|
}
|
2014-08-29 12:23:48 +02:00
|
|
|
|
2014-11-06 15:41:31 +01:00
|
|
|
/**
|
|
|
|
* Remove the given property or section
|
|
|
|
*
|
|
|
|
* @param string $key The property or section to remove
|
|
|
|
*/
|
|
|
|
public function __unset($key)
|
|
|
|
{
|
|
|
|
unset($this->data[$key]);
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
|
|
|
|
2013-07-10 11:40:48 +02:00
|
|
|
/**
|
2014-11-06 15:41:31 +01:00
|
|
|
* Return whether the given section or property is set
|
2013-07-10 11:40:48 +02:00
|
|
|
*
|
2014-11-06 15:41:31 +01:00
|
|
|
* @param string $key The name of the section or property
|
2013-08-26 16:56:23 +02:00
|
|
|
*
|
2014-11-06 15:41:31 +01:00
|
|
|
* @return bool
|
2013-07-10 11:40:48 +02:00
|
|
|
*/
|
2014-11-06 15:41:31 +01:00
|
|
|
public function offsetExists($key)
|
2013-06-07 11:44:37 +02:00
|
|
|
{
|
2014-11-06 15:41:31 +01:00
|
|
|
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
|
|
|
}
|
2014-11-06 15:41:31 +01:00
|
|
|
|
|
|
|
$this->$key = $value;
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
|
|
|
|
2014-09-01 15:53:32 +02:00
|
|
|
/**
|
2014-11-06 15:41:31 +01:00
|
|
|
* Remove the given property or section
|
2014-09-01 15:53:32 +02:00
|
|
|
*
|
2014-11-06 15:41:31 +01:00
|
|
|
* @param string $key The property or section to remove
|
2014-09-01 15:53:32 +02:00
|
|
|
*/
|
2014-11-06 15:41:31 +01:00
|
|
|
public function offsetUnset($key)
|
2014-09-01 15:53:32 +02:00
|
|
|
{
|
2014-11-06 15:41:31 +01:00
|
|
|
unset($this->$key);
|
2014-09-01 15:53:32 +02:00
|
|
|
}
|
|
|
|
|
2013-07-10 11:40:48 +02:00
|
|
|
/**
|
2014-11-06 15:41:31 +01:00
|
|
|
* Return whether this config has any data
|
2013-07-10 11:40:48 +02:00
|
|
|
*
|
2014-11-06 15:41:31 +01:00
|
|
|
* @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
|
2013-08-28 13:31:18 +02:00
|
|
|
*
|
2014-11-06 15:41:31 +01:00
|
|
|
* @return mixed
|
2013-07-10 11:40:48 +02:00
|
|
|
*/
|
2014-11-06 15:41:31 +01:00
|
|
|
public function get($key, $default = null)
|
2013-06-07 11:44:37 +02:00
|
|
|
{
|
2014-11-06 15:41:31 +01:00
|
|
|
$value = $this->$key;
|
|
|
|
if ($default !== null && $value === null) {
|
|
|
|
$value = $default;
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
2014-11-06 15:41:31 +01:00
|
|
|
|
|
|
|
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)
|
2014-03-11 15:43:41 +01:00
|
|
|
);
|
2013-07-10 11:40:48 +02:00
|
|
|
}
|
2014-11-06 15:41:31 +01:00
|
|
|
|
2014-11-11 15:51:54 +01:00
|
|
|
if ($value === null && $default !== null) {
|
2014-11-06 15:41:31 +01:00
|
|
|
$value = $default;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $value;
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
|
|
|
|
2013-07-10 11:40:48 +02:00
|
|
|
/**
|
2014-11-06 15:41:31 +01:00
|
|
|
* Load configuration from the given INI file
|
2013-07-10 11:40:48 +02:00
|
|
|
*
|
2014-11-06 15:41:31 +01:00
|
|
|
* @param string $file The file to parse
|
|
|
|
*
|
|
|
|
* @throws NotReadableError When the file does not exist or cannot be read
|
2013-07-10 11:40:48 +02:00
|
|
|
*/
|
2014-11-06 15:41:31 +01:00
|
|
|
public static function fromIni($file)
|
2013-06-07 11:44:37 +02:00
|
|
|
{
|
2014-11-06 15:41:31 +01:00
|
|
|
$config = new static();
|
|
|
|
|
|
|
|
$filepath = realpath($file);
|
|
|
|
if ($filepath === false) {
|
|
|
|
$config->setConfigFile($file);
|
|
|
|
} elseif (is_readable($filepath)) {
|
|
|
|
$config->setConfigFile($filepath);
|
2014-11-07 13:38:35 +01:00
|
|
|
$config->merge(parse_ini_file($filepath, true));
|
2013-07-10 11:40:48 +02:00
|
|
|
} else {
|
2014-11-06 15:41:31 +01:00
|
|
|
throw new NotReadableError(t('Cannot read config file "%s". Permission denied'), $filepath);
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
2014-11-06 15:41:31 +01:00
|
|
|
|
|
|
|
return $config;
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|
2013-08-06 11:53:42 +02:00
|
|
|
|
2013-08-27 17:17:09 +02:00
|
|
|
/**
|
2014-11-06 15:41:31 +01:00
|
|
|
* Prepend configuration base dir to the given relative path
|
|
|
|
*
|
|
|
|
* @param string $path A relative path
|
2013-08-27 17:17:09 +02:00
|
|
|
*
|
2014-08-29 12:23:48 +02:00
|
|
|
* @return string
|
2013-08-27 17:17:09 +02:00
|
|
|
*/
|
2014-11-06 15:41:31 +01:00
|
|
|
public static function resolvePath($path)
|
2013-08-06 11:53:42 +02:00
|
|
|
{
|
2014-11-06 15:41:31 +01:00
|
|
|
return self::$configDir . DIRECTORY_SEPARATOR . ltrim($path, DIRECTORY_SEPARATOR);
|
2013-08-06 11:53:42 +02:00
|
|
|
}
|
2013-08-27 17:17:09 +02:00
|
|
|
|
2014-08-29 12:23:48 +02:00
|
|
|
/**
|
2014-11-06 15:41:31 +01:00
|
|
|
* Retrieve a application config
|
2014-08-29 12:23:48 +02:00
|
|
|
*
|
2014-11-06 15:41:31 +01:00
|
|
|
* @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
|
2014-08-29 12:23:48 +02:00
|
|
|
*
|
2014-11-06 15:41:31 +01:00
|
|
|
* @return Config The requested configuration
|
2014-08-29 12:23:48 +02:00
|
|
|
*/
|
2014-11-06 15:41:31 +01:00
|
|
|
public static function app($configname = 'config', $fromDisk = false)
|
2014-08-29 12:23:48 +02:00
|
|
|
{
|
2014-11-06 15:41:31 +01:00
|
|
|
if (!isset(self::$app[$configname]) || $fromDisk) {
|
|
|
|
self::$app[$configname] = static::fromIni(static::resolvePath($configname . '.ini'));
|
|
|
|
}
|
|
|
|
|
|
|
|
return self::$app[$configname];
|
2014-08-29 12:23:48 +02:00
|
|
|
}
|
|
|
|
|
2013-08-27 17:17:09 +02:00
|
|
|
/**
|
2014-11-06 15:41:31 +01:00
|
|
|
* 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
|
2013-08-27 17:17:09 +02:00
|
|
|
*
|
2014-11-06 15:41:31 +01:00
|
|
|
* @return Config The requested configuration
|
2013-08-27 17:17:09 +02:00
|
|
|
*/
|
2014-11-06 15:41:31 +01:00
|
|
|
public static function module($modulename, $configname = 'config', $fromDisk = false)
|
2013-08-27 17:17:09 +02:00
|
|
|
{
|
2014-11-06 15:41:31 +01:00
|
|
|
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];
|
2013-08-27 17:17:09 +02:00
|
|
|
}
|
2013-06-07 11:44:37 +02:00
|
|
|
}
|