web/lib/Zend/Config/Xml.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    12  * obtain it through the world-wide-web, please send an email
       
    13  * to license@zend.com so we can send you a copy immediately.
       
    14  *
       
    15  * @category  Zend
       
    16  * @package   Zend_Config
       
    17  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    18  * @license   http://framework.zend.com/license/new-bsd     New BSD License
       
    19  * @version   $Id: Xml.php 20096 2010-01-06 02:05:09Z bkarwin $
       
    20  */
       
    21 
       
    22 /**
       
    23  * @see Zend_Config
       
    24  */
       
    25 require_once 'Zend/Config.php';
       
    26 
       
    27 /**
       
    28  * XML Adapter for Zend_Config
       
    29  *
       
    30  * @category  Zend
       
    31  * @package   Zend_Config
       
    32  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    33  * @license   http://framework.zend.com/license/new-bsd     New BSD License
       
    34  */
       
    35 class Zend_Config_Xml extends Zend_Config
       
    36 {
       
    37     /**
       
    38      * XML namespace for ZF-related tags and attributes
       
    39      */
       
    40     const XML_NAMESPACE = 'http://framework.zend.com/xml/zend-config-xml/1.0/';
       
    41 
       
    42     /**
       
    43      * Whether to skip extends or not
       
    44      *
       
    45      * @var boolean
       
    46      */
       
    47     protected $_skipExtends = false;
       
    48 
       
    49     /**
       
    50      * Loads the section $section from the config file (or string $xml for
       
    51      * access facilitated by nested object properties.
       
    52      *
       
    53      * Sections are defined in the XML as children of the root element.
       
    54      *
       
    55      * In order to extend another section, a section defines the "extends"
       
    56      * attribute having a value of the section name from which the extending
       
    57      * section inherits values.
       
    58      *
       
    59      * Note that the keys in $section will override any keys of the same
       
    60      * name in the sections that have been included via "extends".
       
    61      *
       
    62      * @param  string  $xml     XML file or string to process
       
    63      * @param  mixed   $section Section to process
       
    64      * @param  boolean $options Whether modifications are allowed at runtime
       
    65      * @throws Zend_Config_Exception When xml is not set or cannot be loaded
       
    66      * @throws Zend_Config_Exception When section $sectionName cannot be found in $xml
       
    67      */
       
    68     public function __construct($xml, $section = null, $options = false)
       
    69     {
       
    70         if (empty($xml)) {
       
    71             require_once 'Zend/Config/Exception.php';
       
    72             throw new Zend_Config_Exception('Filename is not set');
       
    73         }
       
    74 
       
    75         $allowModifications = false;
       
    76         if (is_bool($options)) {
       
    77             $allowModifications = $options;
       
    78         } elseif (is_array($options)) {
       
    79             if (isset($options['allowModifications'])) {
       
    80                 $allowModifications = (bool) $options['allowModifications'];
       
    81             }
       
    82             if (isset($options['skipExtends'])) {
       
    83                 $this->_skipExtends = (bool) $options['skipExtends'];
       
    84             }
       
    85         }
       
    86 
       
    87         set_error_handler(array($this, '_loadFileErrorHandler')); // Warnings and errors are suppressed
       
    88         if (strstr($xml, '<?xml')) {
       
    89             $config = simplexml_load_string($xml);
       
    90         } else {
       
    91             $config = simplexml_load_file($xml);
       
    92         }
       
    93 
       
    94         restore_error_handler();
       
    95         // Check if there was a error while loading file
       
    96         if ($this->_loadFileErrorStr !== null) {
       
    97             require_once 'Zend/Config/Exception.php';
       
    98             throw new Zend_Config_Exception($this->_loadFileErrorStr);
       
    99         }
       
   100 
       
   101         if ($section === null) {
       
   102             $dataArray = array();
       
   103             foreach ($config as $sectionName => $sectionData) {
       
   104                 $dataArray[$sectionName] = $this->_processExtends($config, $sectionName);
       
   105             }
       
   106 
       
   107             parent::__construct($dataArray, $allowModifications);
       
   108         } else if (is_array($section)) {
       
   109             $dataArray = array();
       
   110             foreach ($section as $sectionName) {
       
   111                 if (!isset($config->$sectionName)) {
       
   112                     require_once 'Zend/Config/Exception.php';
       
   113                     throw new Zend_Config_Exception("Section '$sectionName' cannot be found in $xml");
       
   114                 }
       
   115 
       
   116                 $dataArray = array_merge($this->_processExtends($config, $sectionName), $dataArray);
       
   117             }
       
   118 
       
   119             parent::__construct($dataArray, $allowModifications);
       
   120         } else {
       
   121             if (!isset($config->$section)) {
       
   122                 require_once 'Zend/Config/Exception.php';
       
   123                 throw new Zend_Config_Exception("Section '$section' cannot be found in $xml");
       
   124             }
       
   125 
       
   126             $dataArray = $this->_processExtends($config, $section);
       
   127             if (!is_array($dataArray)) {
       
   128                 // Section in the XML file contains just one top level string
       
   129                 $dataArray = array($section => $dataArray);
       
   130             }
       
   131 
       
   132             parent::__construct($dataArray, $allowModifications);
       
   133         }
       
   134 
       
   135         $this->_loadedSection = $section;
       
   136     }
       
   137 
       
   138     /**
       
   139      * Helper function to process each element in the section and handle
       
   140      * the "extends" inheritance attribute.
       
   141      *
       
   142      * @param  SimpleXMLElement $element XML Element to process
       
   143      * @param  string           $section Section to process
       
   144      * @param  array            $config  Configuration which was parsed yet
       
   145      * @throws Zend_Config_Exception When $section cannot be found
       
   146      * @return array
       
   147      */
       
   148     protected function _processExtends(SimpleXMLElement $element, $section, array $config = array())
       
   149     {
       
   150         if (!isset($element->$section)) {
       
   151             require_once 'Zend/Config/Exception.php';
       
   152             throw new Zend_Config_Exception("Section '$section' cannot be found");
       
   153         }
       
   154 
       
   155         $thisSection  = $element->$section;
       
   156         $nsAttributes = $thisSection->attributes(self::XML_NAMESPACE);
       
   157 
       
   158         if (isset($thisSection['extends']) || isset($nsAttributes['extends'])) {
       
   159             $extendedSection = (string) (isset($nsAttributes['extends']) ? $nsAttributes['extends'] : $thisSection['extends']);
       
   160             $this->_assertValidExtend($section, $extendedSection);
       
   161 
       
   162             if (!$this->_skipExtends) {
       
   163                 $config = $this->_processExtends($element, $extendedSection, $config);
       
   164             }
       
   165         }
       
   166 
       
   167         $config = $this->_arrayMergeRecursive($config, $this->_toArray($thisSection));
       
   168 
       
   169         return $config;
       
   170     }
       
   171 
       
   172     /**
       
   173      * Returns a string or an associative and possibly multidimensional array from
       
   174      * a SimpleXMLElement.
       
   175      *
       
   176      * @param  SimpleXMLElement $xmlObject Convert a SimpleXMLElement into an array
       
   177      * @return array|string
       
   178      */
       
   179     protected function _toArray(SimpleXMLElement $xmlObject)
       
   180     {
       
   181         $config       = array();
       
   182         $nsAttributes = $xmlObject->attributes(self::XML_NAMESPACE);
       
   183 
       
   184         // Search for parent node values
       
   185         if (count($xmlObject->attributes()) > 0) {
       
   186             foreach ($xmlObject->attributes() as $key => $value) {
       
   187                 if ($key === 'extends') {
       
   188                     continue;
       
   189                 }
       
   190 
       
   191                 $value = (string) $value;
       
   192 
       
   193                 if (array_key_exists($key, $config)) {
       
   194                     if (!is_array($config[$key])) {
       
   195                         $config[$key] = array($config[$key]);
       
   196                     }
       
   197 
       
   198                     $config[$key][] = $value;
       
   199                 } else {
       
   200                     $config[$key] = $value;
       
   201                 }
       
   202             }
       
   203         }
       
   204 
       
   205         // Search for local 'const' nodes and replace them
       
   206         if (count($xmlObject->children(self::XML_NAMESPACE)) > 0) {
       
   207             if (count($xmlObject->children()) > 0) {
       
   208                 require_once 'Zend/Config/Exception.php';
       
   209                 throw new Zend_Config_Exception("A node with a 'const' childnode may not have any other children");
       
   210             }
       
   211 
       
   212             $dom                 = dom_import_simplexml($xmlObject);
       
   213             $namespaceChildNodes = array();
       
   214 
       
   215             // We have to store them in an array, as replacing nodes will
       
   216             // confuse the DOMNodeList later
       
   217             foreach ($dom->childNodes as $node) {
       
   218                 if ($node instanceof DOMElement && $node->namespaceURI === self::XML_NAMESPACE) {
       
   219                     $namespaceChildNodes[] = $node;
       
   220                 }
       
   221             }
       
   222 
       
   223             foreach ($namespaceChildNodes as $node) {
       
   224                 switch ($node->localName) {
       
   225                     case 'const':
       
   226                         if (!$node->hasAttributeNS(self::XML_NAMESPACE, 'name')) {
       
   227                             require_once 'Zend/Config/Exception.php';
       
   228                             throw new Zend_Config_Exception("Misssing 'name' attribute in 'const' node");
       
   229                         }
       
   230 
       
   231                         $constantName = $node->getAttributeNS(self::XML_NAMESPACE, 'name');
       
   232 
       
   233                         if (!defined($constantName)) {
       
   234                             require_once 'Zend/Config/Exception.php';
       
   235                             throw new Zend_Config_Exception("Constant with name '$constantName' was not defined");
       
   236                         }
       
   237 
       
   238                         $constantValue = constant($constantName);
       
   239 
       
   240                         $dom->replaceChild($dom->ownerDocument->createTextNode($constantValue), $node);
       
   241                         break;
       
   242 
       
   243                     default:
       
   244                         require_once 'Zend/Config/Exception.php';
       
   245                         throw new Zend_Config_Exception("Unknown node with name '$node->localName' found");
       
   246                 }
       
   247             }
       
   248 
       
   249             return (string) simplexml_import_dom($dom);
       
   250         }
       
   251 
       
   252         // Search for children
       
   253         if (count($xmlObject->children()) > 0) {
       
   254             foreach ($xmlObject->children() as $key => $value) {
       
   255                 if (count($value->children()) > 0 || count($value->children(self::XML_NAMESPACE)) > 0) {
       
   256                     $value = $this->_toArray($value);
       
   257                 } else if (count($value->attributes()) > 0) {
       
   258                     $attributes = $value->attributes();
       
   259                     if (isset($attributes['value'])) {
       
   260                         $value = (string) $attributes['value'];
       
   261                     } else {
       
   262                         $value = $this->_toArray($value);
       
   263                     }
       
   264                 } else {
       
   265                     $value = (string) $value;
       
   266                 }
       
   267 
       
   268                 if (array_key_exists($key, $config)) {
       
   269                     if (!is_array($config[$key]) || !array_key_exists(0, $config[$key])) {
       
   270                         $config[$key] = array($config[$key]);
       
   271                     }
       
   272 
       
   273                     $config[$key][] = $value;
       
   274                 } else {
       
   275                     $config[$key] = $value;
       
   276                 }
       
   277             }
       
   278         } else if (!isset($xmlObject['extends']) && !isset($nsAttributes['extends']) && (count($config) === 0)) {
       
   279             // Object has no children nor attributes and doesn't use the extends
       
   280             // attribute: it's a string
       
   281             $config = (string) $xmlObject;
       
   282         }
       
   283 
       
   284         return $config;
       
   285     }
       
   286 }