Sfoglia il codice sorgente

[ZF-6960] Added ZF namespace to Zend_Config_Xml and constant support

git-svn-id: http://framework.zend.com/svn/framework/standard/trunk@16924 44c647ce-9c0f-0410-b52a-842ac1e357ba
dasprid 16 anni fa
parent
commit
0ed4239b07

+ 45 - 0
documentation/manual/en/module_specs/Zend_Config_Xml.xml

@@ -166,6 +166,51 @@ EOT;
 $config = new Zend_Config_Xml($string, 'staging');
 ]]></programlisting>
     </note>
+<note>
+        <title>Zend_Config XML namespace</title>
+        <para>
+            Zend_Config comes with it's own XML namespace, which adds additional
+            functionality to the parsing process. To take advantage of it, you have
+            to define a namespace with the namespace URI
+            <code>http://framework.zend.com/xml/zend-config-xml/1.0/</code> in
+            your config root node.
+        </para>
+        
+        <para>
+            With the namespace enabled, you can now use PHP constants within
+            your configuration files. Additionally, the <code>extends</code>
+            attribute was moved to the new namespace and is deprecated in the
+            NULL namespace. It will be completly removed there in ZF 2.0.
+        </para>
+        <programlisting language="xml"><![CDATA[
+$string = <<<EOT
+<?xml version="1.0"?>
+<config xmlns:zf="http://framework.zend.com/xml/zend-config-xml/1.0/">
+    <production>
+        <includePath><zf:const zf:name="APPLICATION_PATH"/>/library</includePath>
+        <db>
+            <adapter value="pdo_mysql"/>
+            <params>
+                <host value="db.example.com"/>
+            </params>
+        </db>
+    </production>
+    <staging zf:extends="production">
+        <db>
+            <params>
+                <host value="dev.example.com"/>
+            </params>
+        </db>
+    </staging>
+</config>
+EOT;
+
+define('APPLICATION_PATH', dirname(__FILE__));
+$config = new Zend_Config_Xml($string, 'staging');
+
+echo $config->includePath; // Prints "/var/www/something/library"
+]]></programlisting>
+    </note>
 </sect1>
 <!--
 vim:se ts=4 sw=4 et:

+ 7 - 2
library/Zend/Config/Writer/Xml.php

@@ -25,6 +25,11 @@
 require_once 'Zend/Config/Writer.php';
 
 /**
+ * @see Zend_Config_Xml
+ */
+require_once 'Zend/Config/Xml.php';
+
+/**
  * @category   Zend
  * @package    Zend_Config
  * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
@@ -106,7 +111,7 @@ class Zend_Config_Writer_Xml extends Zend_Config_Writer
             throw new Zend_Config_Exception('No config was set');
         }
         
-        $xml         = new SimpleXMLElement('<zend-config/>');
+        $xml         = new SimpleXMLElement('<zend-config xmlns:zf="' . Zend_Config_Xml::XML_NAMESPACE . '"/>');
         $extends     = $this->_config->getExtends();
         $sectionName = $this->_config->getSectionName();
         
@@ -122,7 +127,7 @@ class Zend_Config_Writer_Xml extends Zend_Config_Writer
                     $child = $xml->addChild($sectionName);
                     
                     if (isset($extends[$sectionName])) {
-                        $child->addAttribute('extends', $extends[$sectionName]);
+                        $child->addAttribute('zf:extends', $extends[$sectionName], Zend_Config_Xml::XML_NAMESPACE);
                     }
         
                     $this->_addBranch($data, $child, $xml);

+ 60 - 6
library/Zend/Config/Xml.php

@@ -35,6 +35,11 @@ require_once 'Zend/Config.php';
 class Zend_Config_Xml extends Zend_Config
 {
     /**
+     * XML namespace for ZF-related tags and attributes
+     */
+    const XML_NAMESPACE = 'http://framework.zend.com/xml/zend-config-xml/1.0/';
+    
+    /**
      * Wether to skip extends or not
      *
      * @var boolean
@@ -147,10 +152,11 @@ class Zend_Config_Xml extends Zend_Config
             throw new Zend_Config_Exception("Section '$section' cannot be found");
         }
 
-        $thisSection = $element->$section;
+        $thisSection  = $element->$section;
+        $nsAttributes = $thisSection->attributes(self::XML_NAMESPACE);  
 
-        if (isset($thisSection['extends'])) {
-            $extendedSection = (string) $thisSection['extends'];
+        if (isset($thisSection['extends']) || isset($nsAttributes['extends'])) {
+            $extendedSection = (string) (isset($nsAttributes['extends']) ? $nsAttributes['extends'] : $thisSection['extends']);
             $this->_assertValidExtend($section, $extendedSection);
             
             if (!$this->_skipExtends) {
@@ -172,7 +178,8 @@ class Zend_Config_Xml extends Zend_Config
      */
     protected function _toArray(SimpleXMLElement $xmlObject)
     {
-        $config = array();
+        $config       = array();
+        $nsAttributes = $xmlObject->attributes(self::XML_NAMESPACE);
 
         // Search for parent node values
         if (count($xmlObject->attributes()) > 0) {
@@ -195,10 +202,57 @@ class Zend_Config_Xml extends Zend_Config
             }
         }
 
+        // Search for local 'const' nodes and replace them
+        if (count($xmlObject->children(self::XML_NAMESPACE)) > 0) {
+            if (count($xmlObject->children()) > 0) {
+                require_once 'Zend/Config/Exception.php';
+                throw new Zend_Config_Exception("A node with a 'const' childnode may not have any other children");                 
+            }
+            
+            $dom                 = dom_import_simplexml($xmlObject);
+            $namespaceChildNodes = array();
+
+            // We have to store them in an array, as replacing nodes will
+            // confuse the DOMNodeList later
+            foreach ($dom->childNodes as $node) {
+                if ($node instanceof DOMElement && $node->namespaceURI === self::XML_NAMESPACE) {
+                    $namespaceChildNodes[] = $node;
+                }
+            }
+            
+            foreach ($namespaceChildNodes as $node) {
+                switch ($node->localName) {
+                    case 'const':
+                        if (!$node->hasAttributeNS(self::XML_NAMESPACE, 'name')) {
+                            require_once 'Zend/Config/Exception.php';
+                            throw new Zend_Config_Exception("Misssing 'name' attribute in 'const' node");                            
+                        }
+                        
+                        $constantName = $node->getAttributeNS(self::XML_NAMESPACE, 'name');
+                        
+                        if (!defined($constantName)) {
+                            require_once 'Zend/Config/Exception.php';
+                            throw new Zend_Config_Exception("Constant with name '$constantName' was not defined");                            
+                        }
+                        
+                        $constantValue = constant($constantName);
+                        
+                        $dom->replaceChild($dom->ownerDocument->createTextNode($constantValue), $node);
+                        break;
+                        
+                    default:
+                        require_once 'Zend/Config/Exception.php';
+                        throw new Zend_Config_Exception("Unknown node with name '$node->localName' found");  
+                }
+            }
+            
+            return (string) simplexml_import_dom($dom);
+        }
+
         // Search for children
         if (count($xmlObject->children()) > 0) {
             foreach ($xmlObject->children() as $key => $value) {
-                if (count($value->children()) > 0) {
+                if (count($value->children()) > 0 || count($value->children(self::XML_NAMESPACE)) > 0) {
                     $value = $this->_toArray($value);
                 } else if (count($value->attributes()) > 0) {
                     $attributes = $value->attributes();
@@ -221,7 +275,7 @@ class Zend_Config_Xml extends Zend_Config
                     $config[$key] = $value;
                 }
             }
-        } else if (!isset($xmlObject['extends']) && (count($config) === 0)) {
+        } else if (!isset($xmlObject['extends']) && !isset($nsAttributes['extends']) && (count($config) === 0)) {
             // Object has no children nor attributes and doesn't use the extends
             // attribute: it's a string
             $config = (string) $xmlObject;

+ 58 - 0
tests/Zend/Config/XmlTest.php

@@ -237,6 +237,64 @@ class Zend_Config_XmlTest extends PHPUnit_Framework_TestCase
         $this->assertEquals('live', $config->db->name);
         $this->assertEquals('multi', $config->one->two->three);
     }
+    
+    public function testConstants()
+    {
+        if (!defined('ZEND_CONFIG_XML_TEST_CONSTANT')) {
+            define('ZEND_CONFIG_XML_TEST_CONSTANT', 'test');
+        }
+        
+        $string = <<<EOT
+<?xml version="1.0"?>
+<config xmlns:zf="http://framework.zend.com/xml/zend-config-xml/1.0/">
+    <all>
+        <foo>foo-<zf:const zf:name="ZEND_CONFIG_XML_TEST_CONSTANT"/>-bar-<zf:const zf:name="ZEND_CONFIG_XML_TEST_CONSTANT"/></foo>
+        <bar><const name="ZEND_CONFIG_XML_TEST_CONSTANT"/></bar>
+    </all>
+</config>
+EOT;
+        
+        $config = new Zend_Config_Xml($string, 'all');
+        
+        $this->assertEquals('foo-test-bar-test', $config->foo);
+        $this->assertEquals('ZEND_CONFIG_XML_TEST_CONSTANT', $config->bar->const->name);
+    }
+    
+    public function testNonExistentConstant()
+    {
+        $string = <<<EOT
+<?xml version="1.0"?>
+<config xmlns:zf="http://framework.zend.com/xml/zend-config-xml/1.0/">
+    <all>
+        <foo>foo-<zf:const zf:name="ZEND_CONFIG_XML_TEST_NON_EXISTENT_CONSTANT"/></foo>
+    </all>
+</config>
+EOT;
+        
+        try {
+            $config = new Zend_Config_Xml($string, 'all');
+            $this->fail('An expected Zend_Config_Exception was not raised');
+        } catch (Zend_Config_Exception $e) {
+            $this->assertEquals("Constant with name 'ZEND_CONFIG_XML_TEST_NON_EXISTENT_CONSTANT' was not defined", $e->getMessage());
+        }
+    }
+    
+    public function testNamespacedExtends()
+    {
+        $string = <<<EOT
+<?xml version="1.0"?>
+<config xmlns:zf="http://framework.zend.com/xml/zend-config-xml/1.0/">
+    <all>
+        <foo>bar</foo>
+    </all>
+    <staging zf:extends="all"/>
+</config>
+EOT;
+        
+        $config = new Zend_Config_Xml($string);
+
+        $this->assertEquals('bar', $config->staging->foo);
+    }
 
     /*
      * @group 3702