You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
401 lines
9.2 KiB
401 lines
9.2 KiB
<?php
|
|
/**
|
|
* Zend Framework (http://framework.zend.com/)
|
|
*
|
|
* @link http://github.com/zendframework/zf2 for the canonical source repository
|
|
* @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
|
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
|
*/
|
|
|
|
namespace Zend\Config;
|
|
|
|
use ArrayAccess;
|
|
use Countable;
|
|
use Iterator;
|
|
|
|
/**
|
|
* Provides a property based interface to an array.
|
|
* The data are read-only unless $allowModifications is set to true
|
|
* on construction.
|
|
*
|
|
* Implements Countable, Iterator and ArrayAccess
|
|
* to facilitate easy access to the data.
|
|
*/
|
|
class Config implements Countable, Iterator, ArrayAccess
|
|
{
|
|
/**
|
|
* Whether modifications to configuration data are allowed.
|
|
*
|
|
* @var bool
|
|
*/
|
|
protected $allowModifications;
|
|
|
|
/**
|
|
* Number of elements in configuration data.
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $count;
|
|
|
|
/**
|
|
* Data within the configuration.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $data = array();
|
|
|
|
/**
|
|
* Used when unsetting values during iteration to ensure we do not skip
|
|
* the next element.
|
|
*
|
|
* @var bool
|
|
*/
|
|
protected $skipNextIteration;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* Data is read-only unless $allowModifications is set to true
|
|
* on construction.
|
|
*
|
|
* @param array $array
|
|
* @param bool $allowModifications
|
|
*/
|
|
public function __construct(array $array, $allowModifications = false)
|
|
{
|
|
$this->allowModifications = (bool) $allowModifications;
|
|
|
|
foreach ($array as $key => $value) {
|
|
if (is_array($value)) {
|
|
$this->data[$key] = new static($value, $this->allowModifications);
|
|
} else {
|
|
$this->data[$key] = $value;
|
|
}
|
|
|
|
$this->count++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieve a value and return $default if there is no element set.
|
|
*
|
|
* @param string $name
|
|
* @param mixed $default
|
|
* @return mixed
|
|
*/
|
|
public function get($name, $default = null)
|
|
{
|
|
if (array_key_exists($name, $this->data)) {
|
|
return $this->data[$name];
|
|
}
|
|
|
|
return $default;
|
|
}
|
|
|
|
/**
|
|
* Magic function so that $obj->value will work.
|
|
*
|
|
* @param string $name
|
|
* @return mixed
|
|
*/
|
|
public function __get($name)
|
|
{
|
|
return $this->get($name);
|
|
}
|
|
|
|
/**
|
|
* Set a value in the config.
|
|
*
|
|
* Only allow setting of a property if $allowModifications was set to true
|
|
* on construction. Otherwise, throw an exception.
|
|
*
|
|
* @param string $name
|
|
* @param mixed $value
|
|
* @return void
|
|
* @throws Exception\RuntimeException
|
|
*/
|
|
public function __set($name, $value)
|
|
{
|
|
if ($this->allowModifications) {
|
|
|
|
if (is_array($value)) {
|
|
$value = new static($value, true);
|
|
}
|
|
|
|
if (null === $name) {
|
|
$this->data[] = $value;
|
|
} else {
|
|
$this->data[$name] = $value;
|
|
}
|
|
|
|
$this->count++;
|
|
} else {
|
|
throw new Exception\RuntimeException('Config is read only');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deep clone of this instance to ensure that nested Zend\Configs are also
|
|
* cloned.
|
|
*
|
|
* @return void
|
|
*/
|
|
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 an associative array of the stored data.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function toArray()
|
|
{
|
|
$array = array();
|
|
$data = $this->data;
|
|
|
|
/** @var self $value */
|
|
foreach ($data as $key => $value) {
|
|
if ($value instanceof self) {
|
|
$array[$key] = $value->toArray();
|
|
} else {
|
|
$array[$key] = $value;
|
|
}
|
|
}
|
|
|
|
return $array;
|
|
}
|
|
|
|
/**
|
|
* isset() overloading
|
|
*
|
|
* @param string $name
|
|
* @return bool
|
|
*/
|
|
public function __isset($name)
|
|
{
|
|
return isset($this->data[$name]);
|
|
}
|
|
|
|
/**
|
|
* unset() overloading
|
|
*
|
|
* @param string $name
|
|
* @return void
|
|
* @throws Exception\InvalidArgumentException
|
|
*/
|
|
public function __unset($name)
|
|
{
|
|
if (!$this->allowModifications) {
|
|
throw new Exception\InvalidArgumentException('Config is read only');
|
|
} elseif (isset($this->data[$name])) {
|
|
unset($this->data[$name]);
|
|
$this->count--;
|
|
$this->skipNextIteration = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* count(): defined by Countable interface.
|
|
*
|
|
* @see Countable::count()
|
|
* @return int
|
|
*/
|
|
public function count()
|
|
{
|
|
return $this->count;
|
|
}
|
|
|
|
/**
|
|
* current(): defined by Iterator interface.
|
|
*
|
|
* @see Iterator::current()
|
|
* @return mixed
|
|
*/
|
|
public function current()
|
|
{
|
|
$this->skipNextIteration = false;
|
|
return current($this->data);
|
|
}
|
|
|
|
/**
|
|
* key(): defined by Iterator interface.
|
|
*
|
|
* @see Iterator::key()
|
|
* @return mixed
|
|
*/
|
|
public function key()
|
|
{
|
|
return key($this->data);
|
|
}
|
|
|
|
/**
|
|
* next(): defined by Iterator interface.
|
|
*
|
|
* @see Iterator::next()
|
|
* @return void
|
|
*/
|
|
public function next()
|
|
{
|
|
if ($this->skipNextIteration) {
|
|
$this->skipNextIteration = false;
|
|
return;
|
|
}
|
|
|
|
next($this->data);
|
|
}
|
|
|
|
/**
|
|
* rewind(): defined by Iterator interface.
|
|
*
|
|
* @see Iterator::rewind()
|
|
* @return void
|
|
*/
|
|
public function rewind()
|
|
{
|
|
$this->skipNextIteration = false;
|
|
reset($this->data);
|
|
}
|
|
|
|
/**
|
|
* valid(): defined by Iterator interface.
|
|
*
|
|
* @see Iterator::valid()
|
|
* @return bool
|
|
*/
|
|
public function valid()
|
|
{
|
|
return ($this->key() !== null);
|
|
}
|
|
|
|
/**
|
|
* offsetExists(): defined by ArrayAccess interface.
|
|
*
|
|
* @see ArrayAccess::offsetExists()
|
|
* @param mixed $offset
|
|
* @return bool
|
|
*/
|
|
public function offsetExists($offset)
|
|
{
|
|
return $this->__isset($offset);
|
|
}
|
|
|
|
/**
|
|
* offsetGet(): defined by ArrayAccess interface.
|
|
*
|
|
* @see ArrayAccess::offsetGet()
|
|
* @param mixed $offset
|
|
* @return mixed
|
|
*/
|
|
public function offsetGet($offset)
|
|
{
|
|
return $this->__get($offset);
|
|
}
|
|
|
|
/**
|
|
* offsetSet(): defined by ArrayAccess interface.
|
|
*
|
|
* @see ArrayAccess::offsetSet()
|
|
* @param mixed $offset
|
|
* @param mixed $value
|
|
* @return void
|
|
*/
|
|
public function offsetSet($offset, $value)
|
|
{
|
|
$this->__set($offset, $value);
|
|
}
|
|
|
|
/**
|
|
* offsetUnset(): defined by ArrayAccess interface.
|
|
*
|
|
* @see ArrayAccess::offsetUnset()
|
|
* @param mixed $offset
|
|
* @return void
|
|
*/
|
|
public function offsetUnset($offset)
|
|
{
|
|
$this->__unset($offset);
|
|
}
|
|
|
|
/**
|
|
* Merge another Config with this one.
|
|
*
|
|
* For duplicate keys, the following will be performed:
|
|
* - Nested Configs will be recursively merged.
|
|
* - Items in $merge with INTEGER keys will be appended.
|
|
* - Items in $merge with STRING keys will overwrite current values.
|
|
*
|
|
* @param Config $merge
|
|
* @return Config
|
|
*/
|
|
public function merge(Config $merge)
|
|
{
|
|
/** @var Config $value */
|
|
foreach ($merge as $key => $value) {
|
|
if (array_key_exists($key, $this->data)) {
|
|
if (is_int($key)) {
|
|
$this->data[] = $value;
|
|
} elseif ($value instanceof self && $this->data[$key] instanceof self) {
|
|
$this->data[$key]->merge($value);
|
|
} else {
|
|
if ($value instanceof self) {
|
|
$this->data[$key] = new static($value->toArray(), $this->allowModifications);
|
|
} else {
|
|
$this->data[$key] = $value;
|
|
}
|
|
}
|
|
} else {
|
|
if ($value instanceof self) {
|
|
$this->data[$key] = new static($value->toArray(), $this->allowModifications);
|
|
} else {
|
|
$this->data[$key] = $value;
|
|
}
|
|
|
|
$this->count++;
|
|
}
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Prevent any more modifications being made to this instance.
|
|
*
|
|
* Useful after merge() has been used to merge multiple Config objects
|
|
* into one object which should then not be modified again.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setReadOnly()
|
|
{
|
|
$this->allowModifications = false;
|
|
|
|
/** @var Config $value */
|
|
foreach ($this->data as $value) {
|
|
if ($value instanceof self) {
|
|
$value->setReadOnly();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns whether this Config object is read only or not.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function isReadOnly()
|
|
{
|
|
return !$this->allowModifications;
|
|
}
|
|
}
|
|
|