File indexing completed on 2024-12-22 05:37:15

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Config
0017  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0018  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0019  * @version    $Id$
0020  */
0021 
0022 
0023 /**
0024  * @category   Zend
0025  * @package    Zend_Config
0026  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0027  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0028  */
0029 class Zend_Config implements Countable, Iterator
0030 {
0031     /**
0032      * Whether in-memory modifications to configuration data are allowed
0033      *
0034      * @var boolean
0035      */
0036     protected $_allowModifications;
0037 
0038     /**
0039      * Iteration index
0040      *
0041      * @var integer
0042      */
0043     protected $_index;
0044 
0045     /**
0046      * Number of elements in configuration data
0047      *
0048      * @var integer
0049      */
0050     protected $_count;
0051 
0052     /**
0053      * Contains array of configuration data
0054      *
0055      * @var array
0056      */
0057     protected $_data;
0058 
0059     /**
0060      * Used when unsetting values during iteration to ensure we do not skip
0061      * the next element
0062      *
0063      * @var boolean
0064      */
0065     protected $_skipNextIteration;
0066 
0067     /**
0068      * Contains which config file sections were loaded. This is null
0069      * if all sections were loaded, a string name if one section is loaded
0070      * and an array of string names if multiple sections were loaded.
0071      *
0072      * @var mixed
0073      */
0074     protected $_loadedSection;
0075 
0076     /**
0077      * This is used to track section inheritance. The keys are names of sections that
0078      * extend other sections, and the values are the extended sections.
0079      *
0080      * @var array
0081      */
0082     protected $_extends = array();
0083 
0084     /**
0085      * Load file error string.
0086      *
0087      * Is null if there was no error while file loading
0088      *
0089      * @var string
0090      */
0091     protected $_loadFileErrorStr = null;
0092 
0093     /**
0094      * Zend_Config provides a property based interface to
0095      * an array. The data are read-only unless $allowModifications
0096      * is set to true on construction.
0097      *
0098      * Zend_Config also implements Countable and Iterator to
0099      * facilitate easy access to the data.
0100      *
0101      * @param  array   $array
0102      * @param  boolean $allowModifications
0103      * @return void
0104      */
0105     public function __construct(array $array, $allowModifications = false)
0106     {
0107         $this->_allowModifications = (boolean) $allowModifications;
0108         $this->_loadedSection = null;
0109         $this->_index = 0;
0110         $this->_data = array();
0111         foreach ($array as $key => $value) {
0112             if (is_array($value)) {
0113                 $this->_data[$key] = new self($value, $this->_allowModifications);
0114             } else {
0115                 $this->_data[$key] = $value;
0116             }
0117         }
0118         $this->_count = count($this->_data);
0119     }
0120 
0121     /**
0122      * Retrieve a value and return $default if there is no element set.
0123      *
0124      * @param string $name
0125      * @param mixed $default
0126      * @return mixed
0127      */
0128     public function get($name, $default = null)
0129     {
0130         $result = $default;
0131         if (array_key_exists($name, $this->_data)) {
0132             $result = $this->_data[$name];
0133         }
0134         return $result;
0135     }
0136 
0137     /**
0138      * Magic function so that $obj->value will work.
0139      *
0140      * @param string $name
0141      * @return mixed
0142      */
0143     public function __get($name)
0144     {
0145         return $this->get($name);
0146     }
0147 
0148     /**
0149      * Only allow setting of a property if $allowModifications
0150      * was set to true on construction. Otherwise, throw an exception.
0151      *
0152      * @param  string $name
0153      * @param  mixed  $value
0154      * @throws Zend_Config_Exception
0155      * @return void
0156      */
0157     public function __set($name, $value)
0158     {
0159         if ($this->_allowModifications) {
0160             if (is_array($value)) {
0161                 $this->_data[$name] = new self($value, true);
0162             } else {
0163                 $this->_data[$name] = $value;
0164             }
0165             $this->_count = count($this->_data);
0166         } else {
0167             /** @see Zend_Config_Exception */
0168             // require_once 'Zend/Config/Exception.php';
0169             throw new Zend_Config_Exception('Zend_Config is read only');
0170         }
0171     }
0172 
0173     /**
0174      * Deep clone of this instance to ensure that nested Zend_Configs
0175      * are also cloned.
0176      *
0177      * @return void
0178      */
0179     public function __clone()
0180     {
0181       $array = array();
0182       foreach ($this->_data as $key => $value) {
0183           if ($value instanceof Zend_Config) {
0184               $array[$key] = clone $value;
0185           } else {
0186               $array[$key] = $value;
0187           }
0188       }
0189       $this->_data = $array;
0190     }
0191 
0192     /**
0193      * Return an associative array of the stored data.
0194      *
0195      * @return array
0196      */
0197     public function toArray()
0198     {
0199         $array = array();
0200         $data = $this->_data;
0201         foreach ($data as $key => $value) {
0202             if ($value instanceof Zend_Config) {
0203                 $array[$key] = $value->toArray();
0204             } else {
0205                 $array[$key] = $value;
0206             }
0207         }
0208         return $array;
0209     }
0210 
0211     /**
0212      * Support isset() overloading on PHP 5.1
0213      *
0214      * @param string $name
0215      * @return boolean
0216      */
0217     public function __isset($name)
0218     {
0219         return isset($this->_data[$name]);
0220     }
0221 
0222     /**
0223      * Support unset() overloading on PHP 5.1
0224      *
0225      * @param  string $name
0226      * @throws Zend_Config_Exception
0227      * @return void
0228      */
0229     public function __unset($name)
0230     {
0231         if ($this->_allowModifications) {
0232             unset($this->_data[$name]);
0233             $this->_count = count($this->_data);
0234             $this->_skipNextIteration = true;
0235         } else {
0236             /** @see Zend_Config_Exception */
0237             // require_once 'Zend/Config/Exception.php';
0238             throw new Zend_Config_Exception('Zend_Config is read only');
0239         }
0240 
0241     }
0242 
0243     /**
0244      * Defined by Countable interface
0245      *
0246      * @return int
0247      */
0248     public function count()
0249     {
0250         return $this->_count;
0251     }
0252 
0253     /**
0254      * Defined by Iterator interface
0255      *
0256      * @return mixed
0257      */
0258     public function current()
0259     {
0260         $this->_skipNextIteration = false;
0261         return current($this->_data);
0262     }
0263 
0264     /**
0265      * Defined by Iterator interface
0266      *
0267      * @return mixed
0268      */
0269     public function key()
0270     {
0271         return key($this->_data);
0272     }
0273 
0274     /**
0275      * Defined by Iterator interface
0276      *
0277      */
0278     public function next()
0279     {
0280         if ($this->_skipNextIteration) {
0281             $this->_skipNextIteration = false;
0282             return;
0283         }
0284         next($this->_data);
0285         $this->_index++;
0286     }
0287 
0288     /**
0289      * Defined by Iterator interface
0290      *
0291      */
0292     public function rewind()
0293     {
0294         $this->_skipNextIteration = false;
0295         reset($this->_data);
0296         $this->_index = 0;
0297     }
0298 
0299     /**
0300      * Defined by Iterator interface
0301      *
0302      * @return boolean
0303      */
0304     public function valid()
0305     {
0306         return $this->_index < $this->_count;
0307     }
0308 
0309     /**
0310      * Returns the section name(s) loaded.
0311      *
0312      * @return mixed
0313      */
0314     public function getSectionName()
0315     {
0316         if(is_array($this->_loadedSection) && count($this->_loadedSection) == 1) {
0317             $this->_loadedSection = $this->_loadedSection[0];
0318         }
0319         return $this->_loadedSection;
0320     }
0321 
0322     /**
0323      * Returns true if all sections were loaded
0324      *
0325      * @return boolean
0326      */
0327     public function areAllSectionsLoaded()
0328     {
0329         return $this->_loadedSection === null;
0330     }
0331 
0332 
0333     /**
0334      * Merge another Zend_Config with this one. The items
0335      * in $merge will override the same named items in
0336      * the current config.
0337      *
0338      * @param Zend_Config $merge
0339      * @return Zend_Config
0340      */
0341     public function merge(Zend_Config $merge)
0342     {
0343         foreach($merge as $key => $item) {
0344             if(array_key_exists($key, $this->_data)) {
0345                 if($item instanceof Zend_Config && $this->$key instanceof Zend_Config) {
0346                     $this->$key = $this->$key->merge(new Zend_Config($item->toArray(), !$this->readOnly()));
0347                 } else {
0348                     $this->$key = $item;
0349                 }
0350             } else {
0351                 if($item instanceof Zend_Config) {
0352                     $this->$key = new Zend_Config($item->toArray(), !$this->readOnly());
0353                 } else {
0354                     $this->$key = $item;
0355                 }
0356             }
0357         }
0358 
0359         return $this;
0360     }
0361 
0362     /**
0363      * Prevent any more modifications being made to this instance. Useful
0364      * after merge() has been used to merge multiple Zend_Config objects
0365      * into one object which should then not be modified again.
0366      *
0367      */
0368     public function setReadOnly()
0369     {
0370         $this->_allowModifications = false;
0371         foreach ($this->_data as $key => $value) {
0372             if ($value instanceof Zend_Config) {
0373                 $value->setReadOnly();
0374             }
0375         }
0376     }
0377 
0378     /**
0379      * Returns if this Zend_Config object is read only or not.
0380      *
0381      * @return boolean
0382      */
0383     public function readOnly()
0384     {
0385         return !$this->_allowModifications;
0386     }
0387 
0388     /**
0389      * Get the current extends
0390      *
0391      * @return array
0392      */
0393     public function getExtends()
0394     {
0395         return $this->_extends;
0396     }
0397 
0398     /**
0399      * Set an extend for Zend_Config_Writer
0400      *
0401      * @param  string $extendingSection
0402      * @param  string $extendedSection
0403      * @return void
0404      */
0405     public function setExtend($extendingSection, $extendedSection = null)
0406     {
0407         if ($extendedSection === null && isset($this->_extends[$extendingSection])) {
0408             unset($this->_extends[$extendingSection]);
0409         } else if ($extendedSection !== null) {
0410             $this->_extends[$extendingSection] = $extendedSection;
0411         }
0412     }
0413 
0414     /**
0415      * Throws an exception if $extendingSection may not extend $extendedSection,
0416      * and tracks the section extension if it is valid.
0417      *
0418      * @param  string $extendingSection
0419      * @param  string $extendedSection
0420      * @throws Zend_Config_Exception
0421      * @return void
0422      */
0423     protected function _assertValidExtend($extendingSection, $extendedSection)
0424     {
0425         // detect circular section inheritance
0426         $extendedSectionCurrent = $extendedSection;
0427         while (array_key_exists($extendedSectionCurrent, $this->_extends)) {
0428             if ($this->_extends[$extendedSectionCurrent] == $extendingSection) {
0429                 /** @see Zend_Config_Exception */
0430                 // require_once 'Zend/Config/Exception.php';
0431                 throw new Zend_Config_Exception('Illegal circular inheritance detected');
0432             }
0433             $extendedSectionCurrent = $this->_extends[$extendedSectionCurrent];
0434         }
0435         // remember that this section extends another section
0436         $this->_extends[$extendingSection] = $extendedSection;
0437     }
0438 
0439     /**
0440      * Handle any errors from simplexml_load_file or parse_ini_file
0441      *
0442      * @param integer $errno
0443      * @param string $errstr
0444      * @param string $errfile
0445      * @param integer $errline
0446      */
0447     public function _loadFileErrorHandler($errno, $errstr, $errfile, $errline)
0448     {
0449         if ($this->_loadFileErrorStr === null) {
0450             $this->_loadFileErrorStr = $errstr;
0451         } else {
0452             $this->_loadFileErrorStr .= (PHP_EOL . $errstr);
0453         }
0454     }
0455 
0456     /**
0457      * Merge two arrays recursively, overwriting keys of the same name
0458      * in $firstArray with the value in $secondArray.
0459      *
0460      * @param  mixed $firstArray  First array
0461      * @param  mixed $secondArray Second array to merge into first array
0462      * @return array
0463      */
0464     protected function _arrayMergeRecursive($firstArray, $secondArray)
0465     {
0466         if (is_array($firstArray) && is_array($secondArray)) {
0467             foreach ($secondArray as $key => $value) {
0468                 if (isset($firstArray[$key])) {
0469                     $firstArray[$key] = $this->_arrayMergeRecursive($firstArray[$key], $value);
0470                 } else {
0471                     if($key === 0) {
0472                         $firstArray= array(0=>$this->_arrayMergeRecursive($firstArray, $value));
0473                     } else {
0474                         $firstArray[$key] = $value;
0475                     }
0476                 }
0477             }
0478         } else {
0479             $firstArray = $secondArray;
0480         }
0481 
0482         return $firstArray;
0483     }
0484 }