<?php

namespace {
    /**
     * Zend Framework
     *
     * LICENSE
     *
     * This source file is subject to the new BSD license that is bundled
     * with this package in the file LICENSE.txt.
     * It is also available through the world-wide-web at this URL:
     * http://framework.zend.com/license/new-bsd
     * If you did not receive a copy of the license and are unable to
     * obtain it through the world-wide-web, please send an email
     * to license@zend.com so we can send you a copy immediately.
     *
     * @category   Zend
     * @package    Zend_Config
     * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
     * @license    http://framework.zend.com/license/new-bsd     New BSD License
     * @version    $Id: Config.php 23775 2011-03-01 17:25:24Z ralph $
     */
    /**
     * @category   Zend
     * @package    Zend_Config
     * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
     * @license    http://framework.zend.com/license/new-bsd     New BSD License
     */
    class Zend_Config implements \Countable, \Iterator
    {
        /**
         * Whether in-memory modifications to configuration data are allowed
         *
         * @var boolean
         */
        protected $_allowModifications;
        /**
         * Iteration index
         *
         * @var integer
         */
        protected $_index;
        /**
         * Number of elements in configuration data
         *
         * @var integer
         */
        protected $_count;
        /**
         * Contains array of configuration data
         *
         * @var array
         */
        protected $_data;
        /**
         * Used when unsetting values during iteration to ensure we do not skip
         * the next element
         *
         * @var boolean
         */
        protected $_skipNextIteration;
        /**
         * Contains which config file sections were loaded. This is null
         * if all sections were loaded, a string name if one section is loaded
         * and an array of string names if multiple sections were loaded.
         *
         * @var mixed
         */
        protected $_loadedSection;
        /**
         * This is used to track section inheritance. The keys are names of sections that
         * extend other sections, and the values are the extended sections.
         *
         * @var array
         */
        protected $_extends = array();
        /**
         * Load file error string.
         *
         * Is null if there was no error while file loading
         *
         * @var string
         */
        protected $_loadFileErrorStr = null;
        /**
         * Zend_Config provides a property based interface to
         * an array. The data are read-only unless $allowModifications
         * is set to true on construction.
         *
         * Zend_Config also implements Countable and Iterator to
         * facilitate easy access to the data.
         *
         * @param  array   $array
         * @param  boolean $allowModifications
         * @return void
         */
        public function __construct(array $array, $allowModifications = \false)
        {
            $this->_allowModifications = (bool) $allowModifications;
            $this->_loadedSection = null;
            $this->_index = 0;
            $this->_data = array();
            foreach ($array as $key => $value) {
                if (\is_array($value)) {
                    $this->_data[$key] = new self($value, $this->_allowModifications);
                } else {
                    $this->_data[$key] = $value;
                }
            }
            $this->_count = \count($this->_data);
        }
        /**
         * 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)
        {
            $result = $default;
            if (\array_key_exists($name, $this->_data)) {
                $result = $this->_data[$name];
            }
            return $result;
        }
        /**
         * Magic function so that $obj->value will work.
         *
         * @param string $name
         * @return mixed
         */
        public function __get($name)
        {
            return $this->get($name);
        }
        /**
         * Only allow setting of a property if $allowModifications
         * was set to true on construction. Otherwise, throw an exception.
         *
         * @param  string $name
         * @param  mixed  $value
         * @throws Zend_Config_Exception
         * @return void
         */
        public function __set($name, $value)
        {
            if ($this->_allowModifications) {
                if (\is_array($value)) {
                    $this->_data[$name] = new self($value, \true);
                } else {
                    $this->_data[$name] = $value;
                }
                $this->_count = \count($this->_data);
            } else {
                /** @see Zend_Config_Exception */
                // require_once 'Zend/Config/Exception.php';
                throw new \Zend_Config_Exception('Zend_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 \Zend_Config) {
                    $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;
            foreach ($data as $key => $value) {
                if ($value instanceof \Zend_Config) {
                    $array[$key] = $value->toArray();
                } else {
                    $array[$key] = $value;
                }
            }
            return $array;
        }
        /**
         * Support isset() overloading on PHP 5.1
         *
         * @param string $name
         * @return boolean
         */
        public function __isset($name)
        {
            return isset($this->_data[$name]);
        }
        /**
         * Support unset() overloading on PHP 5.1
         *
         * @param  string $name
         * @throws Zend_Config_Exception
         * @return void
         */
        public function __unset($name)
        {
            if ($this->_allowModifications) {
                unset($this->_data[$name]);
                $this->_count = \count($this->_data);
                $this->_skipNextIteration = \true;
            } else {
                /** @see Zend_Config_Exception */
                // require_once 'Zend/Config/Exception.php';
                throw new \Zend_Config_Exception('Zend_Config is read only');
            }
        }
        /**
         * Defined by Countable interface
         *
         * @return int
         */
        public function count()
        {
            return $this->_count;
        }
        /**
         * Defined by Iterator interface
         *
         * @return mixed
         */
        public function current()
        {
            $this->_skipNextIteration = \false;
            return \current($this->_data);
        }
        /**
         * Defined by Iterator interface
         *
         * @return mixed
         */
        public function key()
        {
            return \key($this->_data);
        }
        /**
         * Defined by Iterator interface
         */
        public function next()
        {
            if ($this->_skipNextIteration) {
                $this->_skipNextIteration = \false;
                return;
            }
            \next($this->_data);
            $this->_index++;
        }
        /**
         * Defined by Iterator interface
         */
        public function rewind()
        {
            $this->_skipNextIteration = \false;
            \reset($this->_data);
            $this->_index = 0;
        }
        /**
         * Defined by Iterator interface
         *
         * @return boolean
         */
        public function valid()
        {
            return $this->_index < $this->_count;
        }
        /**
         * Returns the section name(s) loaded.
         *
         * @return mixed
         */
        public function getSectionName()
        {
            if (\is_array($this->_loadedSection) && \count($this->_loadedSection) == 1) {
                $this->_loadedSection = $this->_loadedSection[0];
            }
            return $this->_loadedSection;
        }
        /**
         * Returns true if all sections were loaded
         *
         * @return boolean
         */
        public function areAllSectionsLoaded()
        {
            return $this->_loadedSection === null;
        }
        /**
         * Merge another Zend_Config with this one. The items
         * in $merge will override the same named items in
         * the current config.
         *
         * @param Zend_Config $merge
         * @return Zend_Config
         */
        public function merge(\Zend_Config $merge)
        {
            foreach ($merge as $key => $item) {
                if (\array_key_exists($key, $this->_data)) {
                    if ($item instanceof \Zend_Config && $this->{$key} instanceof \Zend_Config) {
                        $this->{$key} = $this->{$key}->merge(new \Zend_Config($item->toArray(), !$this->readOnly()));
                    } else {
                        $this->{$key} = $item;
                    }
                } else {
                    if ($item instanceof \Zend_Config) {
                        $this->{$key} = new \Zend_Config($item->toArray(), !$this->readOnly());
                    } else {
                        $this->{$key} = $item;
                    }
                }
            }
            return $this;
        }
        /**
         * Prevent any more modifications being made to this instance. Useful
         * after merge() has been used to merge multiple Zend_Config objects
         * into one object which should then not be modified again.
         */
        public function setReadOnly()
        {
            $this->_allowModifications = \false;
            foreach ($this->_data as $key => $value) {
                if ($value instanceof \Zend_Config) {
                    $value->setReadOnly();
                }
            }
        }
        /**
         * Returns if this Zend_Config object is read only or not.
         *
         * @return boolean
         */
        public function readOnly()
        {
            return !$this->_allowModifications;
        }
        /**
         * Get the current extends
         *
         * @return array
         */
        public function getExtends()
        {
            return $this->_extends;
        }
        /**
         * Set an extend for Zend_Config_Writer
         *
         * @param  string $extendingSection
         * @param  string $extendedSection
         * @return void
         */
        public function setExtend($extendingSection, $extendedSection = null)
        {
            if ($extendedSection === null && isset($this->_extends[$extendingSection])) {
                unset($this->_extends[$extendingSection]);
            } else {
                if ($extendedSection !== null) {
                    $this->_extends[$extendingSection] = $extendedSection;
                }
            }
        }
        /**
         * Throws an exception if $extendingSection may not extend $extendedSection,
         * and tracks the section extension if it is valid.
         *
         * @param  string $extendingSection
         * @param  string $extendedSection
         * @throws Zend_Config_Exception
         * @return void
         */
        protected function _assertValidExtend($extendingSection, $extendedSection)
        {
            // detect circular section inheritance
            $extendedSectionCurrent = $extendedSection;
            while (\array_key_exists($extendedSectionCurrent, $this->_extends)) {
                if ($this->_extends[$extendedSectionCurrent] == $extendingSection) {
                    /** @see Zend_Config_Exception */
                    // require_once 'Zend/Config/Exception.php';
                    throw new \Zend_Config_Exception('Illegal circular inheritance detected');
                }
                $extendedSectionCurrent = $this->_extends[$extendedSectionCurrent];
            }
            // remember that this section extends another section
            $this->_extends[$extendingSection] = $extendedSection;
        }
        /**
         * Handle any errors from simplexml_load_file or parse_ini_file
         *
         * @param integer $errno
         * @param string $errstr
         * @param string $errfile
         * @param integer $errline
         */
        protected function _loadFileErrorHandler($errno, $errstr, $errfile, $errline)
        {
            if ($this->_loadFileErrorStr === null) {
                $this->_loadFileErrorStr = $errstr;
            } else {
                $this->_loadFileErrorStr .= \PHP_EOL . $errstr;
            }
        }
        /**
         * Merge two arrays recursively, overwriting keys of the same name
         * in $firstArray with the value in $secondArray.
         *
         * @param  mixed $firstArray  First array
         * @param  mixed $secondArray Second array to merge into first array
         * @return array
         */
        protected function _arrayMergeRecursive($firstArray, $secondArray)
        {
            if (\is_array($firstArray) && \is_array($secondArray)) {
                foreach ($secondArray as $key => $value) {
                    if (isset($firstArray[$key])) {
                        $firstArray[$key] = $this->_arrayMergeRecursive($firstArray[$key], $value);
                    } else {
                        if ($key === 0) {
                            $firstArray = array(0 => $this->_arrayMergeRecursive($firstArray, $value));
                        } else {
                            $firstArray[$key] = $value;
                        }
                    }
                }
            } else {
                $firstArray = $secondArray;
            }
            return $firstArray;
        }
    }
}
