<?php
/*
* Queasy PHP Framework - Configuration
*
* (c) Vitaly Demyanenko <vitaly_demyanenko@yahoo.com>
*
* For the full copyright and license information, please view the LICENSE file that was distributed with this source code.
*/
namespace queasy\config;
use queasy\helper\Arrays;
use queasy\helper\Strings;
use queasy\config\loader\ConfigLoaderException;
use queasy\config\loader\LoaderFactory;
/**
* Main configuration class
*/
class Config extends AbstractConfig
{
const DEFAULT_PATH = 'queasy-config.php';
const QUEASY_MARKER = '@queasy:';
/**
* Constructor.
*
* @param string|array|null $data Array with configuration data, or path to a config file, or null to load config from path
* specified by QUEASY_CONFIG_PATH constant if present, or from default path
*
* @throws InvalidPathException When configuration load attempt fails, in case of missing or corrupted (doesn't returning an array) file
*/
public function __construct($data = null, ConfigInterface $parent = null)
{
if (null === $data) {
$data = (defined('QUEASY_CONFIG_PATH'))
? QUEASY_CONFIG_PATH
: static::DEFAULT_PATH;
if (!is_file($data) && !is_link($data)) {
$data = array();
}
} else if (!is_string($data)
&& !is_array($data)) {
throw new InvalidPathException(gettype($data));
}
parent::__construct($data, $parent);
}
/**
* Get an option value from configuration by option name provided like a object property.
*
* @param string $name Config option name
*
* @return mixed|null Option value or null if $name is missing in config
*
* @throws ConfigLoaderException When configuration load attempt fails, in case of missing or corrupted (doesn't returning an array) file
*/
public function __get($name)
{
return $this->get($name);
}
/**
* Not implemented.
*
* @throws ReadOnlyException
*/
public function __set($name, $value)
{
throw new ReadOnlyException(__METHOD__);
}
/**
* Check if $name option is present.
*
* @param string $name Config option name
*
* @return boolean True if present, false if not
*
* @throws ConfigLoaderException When configuration load attempt fails, in case of missing or corrupted (doesn't returning an array) file
*/
public function __isset($name)
{
return $this->offsetExists($name);
}
/**
* Not implemented.
*
* @throws ReadOnlyException
*/
public function __unset($name)
{
throw new ReadOnlyException(__METHOD__);
}
/**
* Get an option value from configuration by option name or return default value if provided or return null.
*
* @param string $name Config option name
* @param mixed $default Value to be returned if $name option is missing
*
* @return mixed Option value or $default if $name option is missing in config
*
* @throws ConfigLoaderException When configuration load attempt fails, in case of missing or corrupted (doesn't returning an array) file
*/
public function get($name, $default = null)
{
if (isset($this[$name])) {
return $this[$name];
} elseif (is_array($default)) {
$className = get_class($this);
return new $className($default, $this);
}
return $default;
}
/**
* Get an option value from configuration by option name.
*
* @param string $name Config option name
*
* @return mixed Option value
*
* @throws ConfigLoaderException When configuration load attempt fails, in case of missing or corrupted (doesn't returning an array) file
* @throws MissingOptionException When $name option is missing
*/
public function need($name)
{
if (isset($this[$name])) {
return $this[$name];
} else {
throw new MissingOptionException($name);
}
}
/**
* Move config array pointer to the beginning.
*
* @throws ConfigLoaderException When configuration load attempt fails, in case of missing or corrupted (doesn't returning an array) file
*/
public function rewind()
{
$data = &$this->data();
reset($data);
}
/**
* Get current config array item.
*
* @throws ConfigLoaderException When configuration load attempt fails, in case of missing or corrupted (doesn't returning an array) file
*/
public function current()
{
$data = &$this->data();
return $this->item(current($data));
}
/**
* Move to the next config array item and return it.
*
* @throws ConfigLoaderException When configuration load attempt fails, in case of missing or corrupted (doesn't returning an array) file
*/
public function next()
{
$data = &$this->data();
return $this->item(next($data));
}
/**
* Return current config array key.
*
* @throws ConfigLoaderException When configuration load attempt fails, in case of missing or corrupted (doesn't returning an array) file
*/
public function key()
{
$data = &$this->data();
return key($data);
}
/**
* Validate current config array key.
*
* @throws ConfigLoaderException When configuration load attempt fails, in case of missing or corrupted (doesn't returning an array) file
*/
public function valid()
{
$data = &$this->data();
$key = key($data);
return ($key !== null)
&& ($key !== false);
}
/**
* Return number of items in a current configuration level.
*
* @return int Number of items
*
* @throws ConfigLoaderException When configuration load attempt fails, in case of missing or corrupted (doesn't returning an array) file
*/
public function count()
{
return count($this->data());
}
/**
* Check if $name option is present.
*
* @param string $name Config option name
*
* @return boolean True if present, false if not
*
* @throws ConfigLoaderException When configuration load attempt fails, in case of missing or corrupted (doesn't returning an array) file
*/
public function offsetExists($name)
{
$data = &$this->data();
$parent = $this->parent();
if (isset($data[$name]) || array_key_exists($name, $data)) {
return true;
} elseif (null === $parent) {
return false;
} else {
return $parent->offsetExists($name);
}
}
/**
* Get an option value from config by option $name or throw ConfigException if option is missing.
*
* @param string $name Config option name
*
* @return mixed Config option value
*
* @throws ConfigLoaderException When configuration load attempt fails, in case of missing or corrupted (doesn't returning an array) file
*/
public function offsetGet($name)
{
if ($this->offsetExists($name)) {
$data = &$this->data();
if (isset($data[$name]) || array_key_exists($name, $data)) {
return $this->item($data[$name]);
} else {
$parent = $this->parent();
return (null === $parent)? null: $parent[$name];
}
} else {
return null;
}
}
/**
* Not implemented.
*
* @throws BadMethodCallException
*/
public function offsetUnset($name)
{
throw new ReadOnlyException(__METHOD__);
}
/**
* Not implemeted.
*
* @throws BadMethodCallException
*/
public function offsetSet($name, $value)
{
throw new ReadOnlyException(__METHOD__);
}
/**
* Search for config keys using regular expression.
*
* @param string $regex Regular expression
*
* @return ConfigInterface Config instance containing key/option pairs found.
*/
public function regex($regex)
{
$data = &$this->data();
$options = array();
foreach ($data as $key => $value) {
if (preg_match($regex, $key)) {
$options[$key] = $value;
}
}
$className = get_class($this);
return new $className($options);
}
public function merge($array)
{
$data = Arrays::merge($this->data(), $array);
$this->setData($data);
}
/**
* Convert all configuration structure into a regular array.
*
* @return array Configuration represented as a regular array
*
* @throws ConfigLoaderException When any of included configuration files are missing or corrupted (doesn't return an array)
*/
public function toArray()
{
$result = array();
foreach ($this->data() as $key => $item) {
$item = $this->item($item);
$result[$key] = (is_object($item) && ($item instanceof ConfigInterface))
? $item->toArray()
: $result[$key] = $item;
}
return $result;
}
/**
* Check if data is loaded, and try to load it using loader if not.
*
* @return &array A reference to array containing config
*
* @throws ConfigLoaderException When configuration load attempt fails,
* in case of missing or corrupted (doesn't returning an array) file
*/
protected function &data()
{
$data = &parent::data();
if (is_string($data)) {
$loader = LoaderFactory::create($data);
$data = $loader();
$this->setData($data);
}
return $data;
}
/**
* Check if $item is an array and if yes return ConfigInterface instance that encapsulates this array,
* if $item is instance of ConfigInterface, set $this as his parent, in other way return $item as is.
* If $item starts with "@queasy:" then run eval() for the rest of the string (to support multi-file configs
* for formats other than PHP)
*
* @param mixed $item An item to check
*
* @return ConfigInterface|mixed ConfigInterface instance if $item was an array, or $item as is
*/
protected function item($item)
{
if (is_array($item)) {
$className = get_class($this);
$item = new $className($item, $this);
} elseif ($item instanceof ConfigInterface) {
$item->setParent($this);
} elseif (is_string($item) && Strings::startsWith($tritem = trim($item), self::QUEASY_MARKER)) {
$item = eval('return ' . substr($tritem, strlen(self::QUEASY_MARKER)) . (Strings::endsWith($tritem, ';')? '': ';'));
}
return $item;
}
}
|