hello,

once again a post about the tricky namespaces. I played with the following
functions concerning namespaces:

domdocument->createElementNS
domdocument->createAttributeNS
domelement->setAttributeNS
domelement->getAttributeNS
(domelement->hasAttributeNS)

as i found out the behavior of some functions differs when handling
namespaces. maybe we can start a thread thinking about this behavior - at
the moment i don't have the "enlightened" view into this very complex
theme. would be nice if you could help me/us understanding namespaces a bit
more...

ok here are the things i found out. copy the code to your editor and step
through the examples - the important output is given in the comments.
Thanks for taking time to that :)

/*############################ START ############################*/
<pre>

/* SIMPLE NAMESPACE EXAMPLE */
<?php
        $doc = new DomDocument();
        $xpath = new DomXPath($doc);
        
        /* SIMPLE NAMESPACE EXAMPLE */
        $node1 = $doc->createElementNS("namespaceURI", "nodeName");
        $node1 = $doc->appendChild($node1);
        
        print(htmlspecialchars("\n" . $doc->saveXML() . "\n"));
        // Output
        // > <nodeName xmlns="namespaceURI"/>
        
        $namespaces = $xpath->query("//namespace::*");
        foreach($namespaces as $item) {
                print($item->nodeName . " = " . $item->nodeValue . "\n");
        }
        // Output
        // > xmlns:xml = http://www.w3.org/XML/1998/namespace
        // > xmlns = namespaceURI
        $doc->removeChild($node1);
?>

Right behavior so far!



/* NAMESPACE WITH PREFIX EXAMPLE */
<?php   
        /* NAMESPACE WITH PREFIX EXAMPLE */
        $node2 = $doc->createElementNS("namespaceURI", "prefix:localName");
        $node2 = $doc->appendChild($node2);
        
        print(htmlspecialchars("\n" . $doc->saveXML() . "\n"));
        // Output
        // > <prefix:localName xmlns:prefix="namespaceURI"/>
        
        $namespaces = $xpath->query("//namespace::*");
        foreach($namespaces as $item) {
                print($item->nodeName . " = " . $item->nodeValue . "\n");
        }
        // Output
        // > xmlns:xml = http://www.w3.org/XML/1998/namespace
        // > xmlns:prefix = namespaceURI
        
        // $doc->removeChild($node2);
?>

Still right behavior!


        
/* NAMESPACE WITH EXISTING PREFIX EXAMPLE */
<?php
        /* NAMESPACE WITH EXISTING PREFIX EXAMPLE */
        $node3 = $doc->createElementNS("namespaceURI", "localName");
        $node3 = $node2->appendChild($node3);
        
        print(htmlspecialchars("\n" . $doc->saveXML() . "\n"));
        // Output
        // > <prefix:localName xmlns:prefix="namespaceURI"> ¬
        //              <prefix:localName/> ¬
        //       </prefix:localName>
        
        $namespaces = $xpath->query("//namespace::*");
        foreach($namespaces as $item) {
                print($item->nodeName . " = " . $item->nodeValue . "\n");
        }
        // Output
        // > xmlns:xml = http://www.w3.org/XML/1998/namespace   (node2)
        // > xmlns:prefix = namespaceURI                                               
 (node2)
        // > xmlns:xml = http://www.w3.org/XML/1998/namespace   (node3)
        // > xmlns:prefix = namespaceURI                                               
 (node3)
        
        // $doc->removeChild($node3);
?>

Right behavior, but namespace node will be removed, xpath-expression shows
prefix - thats still right.     



/* IMPORT NAMESPACE NODE WITHOUT CHILDREN EXAMPLE */
<?php
        /* IMPORT NAMESPACE NODE WITHOUT CHILDREN EXAMPLE */
        $newDoc = new DomDocument();
        $newXpath = new DomXPath($newDoc);
        
        $newNode3 = $newDoc->importNode($node3);        // second argument $deep = 
FALSE!!
        $newNode3 = $newDoc->appendChild($newNode3);
        
        print(htmlspecialchars("\n" . $newDoc->saveXML() . "\n"));
        // Output
        // > <localName/>
        
        $newNamespaces = $newXpath->query("//namespace::*");
        foreach($newNamespaces as $item) {
                print($item->nodeName . " = " . $item->nodeValue . "\n");
        }
        // Output
        // > xmlns:xml = http://www.w3.org/XML/1998/namespace   (newNode3)
        
        $newDoc->removeChild($newNode3);
?>

Maybe right behavior, but i think loosing the namespace while importing the 
node without its children is a bit dangerous...



/* IMPORT NAMESPACE NODE EXAMPLE */
<?php
        /* IMPORT NAMESPACE NODE EXAMPLE */     
        $newNode3 = $newDoc->importNode($node3, TRUE);  // second argument $deep =
TRUE now!!
        $newNode3 = $newDoc->appendChild($newNode3);
        
        print(htmlspecialchars("\n" . $newDoc->saveXML() . "\n"));
        // Output
        // > <prefix:localName xmlns:prefix="namespaceURI"/>
        
        $newNamespaces = $newXpath->query("//namespace::*");
        foreach($newNamespaces as $item) {
                print($item->nodeName . " = " . $item->nodeValue . "\n");
        }
        // Output
        // > xmlns:xml = http://www.w3.org/XML/1998/namespace   (newNode3)
        // > xmlns:prefix = namespaceURI                                               
 (newNode3)
        
        $newDoc->removeChild($newNode3);
?>

Now the namespace node will be imported as well, but it is not possible 
to import the "namespaced" node without its children...



/* SET A NAMESPACE ATTRIBUTE WITHOUT PREFIX AND UNDEFINED NAMESPACE */
<?php   
        /* SET A NAMESPACE ATTRIBUTE WITHOUT PREFIX AND UNDEFINED NAMESPACE */
        //$node3->setAttributeNS("attrNamespaceURI", "attrName", "attrValue");
        // Output
        // > Fatal error:  Uncaught exception 'domexception' with message
'Namespace Error'
?>

Error message is acceptable because attribute nodes could not have their own 
"xmlns" attributes! They have to be defined with a prefix (if their is no 
prefix defined for this namespace!)... Therefor the folling example should 
not occur an error...

<?php   
        /* SET A NAMESPACE ATTRIBUTE WITH EXISTING NAMESPACE */
        //$node3->setAttributeNS("namespaceURI", "attrName", "attrValue");
        // Output would be
        // > <prefix:localName xmlns:prefix="namespaceURI">
        //              <prefix:localName prefix:attrName="attrValue"/>
        //       </prefix:localName>
?>

And it doesn't. Right! The import of this node will be done correctly!



/* SET A NAMESPACE ATTRIBUTE WITH PREFIX AND UNDEFINED NAMESPACE EXAMPLE */
<?php
        $node3->setAttributeNS("attrNamespaceURI", "prefix:attrName", "attrValue");
        
        print(htmlspecialchars("\n" . $doc->saveXML() . "\n"));
        // Output
        // > <prefix:localName xmlns:prefix="namespaceURI"> ¬
        //              <prefix:localName xmlns:prefix="attrNamespaceURI"
prefix:attrName="attrValue"/> ¬
        //       </prefix:localName>
        
        $namespaces = $xpath->query("//namespace::*");
        foreach($namespaces as $item) {
                print($item->nodeName . " = " . $item->nodeValue . "\n");
        }
        // Output
        // > xmlns:xml = http://www.w3.org/XML/1998/namespace   (node2)
        // > xmlns:prefix = namespaceURI                                               
 (node2)
        // > xmlns:xml = http://www.w3.org/XML/1998/namespace   (node3)
        // > xmlns:prefix = attrNamespaceURI                                    (node3)
        
        // $doc->removeChild($node3);
        print("Node3 namespace: " . $node3->namespaceURI . "\n");
        // Output
        // > Node3 namespace: namespaceURI
?>

Seems confusing but namespace of node is still "namespaceURI"
And it will result in a conflict in the following example...



/* IMPORT NAMESPACE NODE WITH NAMESPACE-PREFIX DEFINITION EXAMPLE */
<?php
        /* IMPORT NAMESPACE NODE EXAMPLE */     
        $newNode3 = $newDoc->importNode($node3, TRUE);  // second argument $deep =
TRUE now!!
        $newNode3 = $newDoc->appendChild($newNode3);
        
        print(htmlspecialchars("\n" . $newDoc->saveXML() . "\n"));
        // Output
        // > <prefix:localName xmlns:prefix="attrNamespaceURI"
prefix:attrName="attrValue"/>
        
        $newNamespaces = $newXpath->query("//namespace::*");
        foreach($newNamespaces as $item) {
                print($item->nodeName . " = " . $item->nodeValue . "\n");
        }
        // Output
        // > xmlns:xml = http://www.w3.org/XML/1998/namespace   (newNode3)
        // > xmlns:prefix = namespaceURI                                               
 (newNode3)
        print("NewNode3 namespace: " . $newNode3->namespaceURI . "\n");
        // Output
        // > NewNode3 namespace: attrNamespaceURI
        
        $newDoc->removeChild($newNode3);
        $node2->removeChild($node3);
?>

WOW!! NamespaceURI has been changed while importing node - i think 
that this behavior isn't right yet. NamespaceURI shouldn't depend on 
xmlns:prefix attribute - instead it simply should loose its prefix and 
set its "xmlns" attribute to its current namespaceURI!



/* SET A NAMESPACE ATTRIBUTE WITH NEW ATTRIBUTE NODE EXAMPLE */
I'm now changing to node2 -  getting to complicated
<?php
        //$attr2 = $doc->createAttributeNS("attrNamespaceURI", "prefix:attrName");
        // Output
        // > Fatal error:  Uncaught exception 'domexception' with message
'Namespace Error'
?>

It seems that the behavior handling namespaces isn't the same in 
DomDocument::createAttributeNS() and DomElement::setAttributeNS(), as you 
can see in the 
SET A NAMESPACE ATTRIBUTE WITH PREFIX AND UNDEFINED NAMESPACE EXAMPLE

But using a new namespace-prefix it will work correctly.
<?php
        $attr2 = $doc->createAttributeNS("attrNamespaceURI", "newPrefix:attrName");
        // Output:
        // > <prefix:localName xmlns:prefix="namespaceURI"
xmlns:newPrefix="attrNamespaceURI"/>
?>

Note: the "xmlns:newPrefix" declaration will be appended to the root 
node of the document on creation of this NS attribute. This could 
result in trouble while cloning/importing/deleting a node holding an 
"newPrefix"ed attribute!?

<?php
        $node3 = $doc->createElementNS("namespaceURI", "nodeName");
        $node3 = $node2->appendChild($node3);
        $node3->appendChild($attr2);
        
        print(htmlspecialchars("\n" . $doc->saveXML() . "\n"));
        // Output
        // > <prefix:localName xmlns:prefix="namespaceURI"
xmlns:newPrefix="attrNamespaceURI">
        //              <prefix:nodeName newPrefix:attrName=""/>
        //       </prefix:localName>
        
        $namespaces = $xpath->query("//namespace::*");
        foreach($namespaces as $item) {
                print($item->nodeName . " = " . $item->nodeValue . "\n");
        }
        // Output
        // > xmlns:xml = http://www.w3.org/XML/1998/namespace   (node2)
        // > xmlns:newprefix = attrNamespaceURI                                 (node2)
        // > xmlns:prefix = namespaceURI                                               
 (node2)
        // > xmlns:xml = http://www.w3.org/XML/1998/namespace   (node3)
        // > xmlns:newprefix = attrNamespaceURI                                 (node3)
        // > xmlns:prefix = namespaceURI                                               
 (node3)
        $node2->removeChild($node3);
        // Output
        // > <prefix:localName xmlns:prefix="namespaceURI"
xmlns:newPrefix="attrNamespaceURI"/>
?>
As you can see the "xmlns:newPrefix" attribute is still appended to the root 
node of the document. Because of the lack of a (reasonable) possibility to 
remove an "xmlns" declaration the reset of an attribute using this prefix 
will result in an error:

<?php
        // $attr2 = $doc->createAttributeNS("newAttrNamespaceURI",
"newPrefix:attrName");
        // Output
        // > Fatal error:  Uncaught exception 'domexception' with message
'Namespace Error'
?>

I think it would be easier to append the "xmlns" declarations on those nodes 
which have an attribute/child of this namespace.



/* SET AN XMLNS NAMESPACE ATTRIBUTE EXAMPLE */
As I said before there is no (reasonable) possiblity to remove an "xmlns" 
declaration (you could do this by cloning nodes in the hope for a bug in the 
source leading to the loss of this declaration... very uncomforable:).

But there is a possibility to check if a xmlns is defined using xpath (I use 
this xpath in this document all the time).

I found out that you also can set an "xmlns" declaration using the right 
namespaces (which you can find in the sources of libxml and ext/dom).
Have a look:

<?php
        // xmlns Namespace: "http://www.w3.org/2000/xmlns/";
        // xml Namespace: "http://www.w3.org/XML/1998/namespace";
        $node2->setAttributeNS("http://www.w3.org/2000/xmlns/";,
"xmlns:anotherNewPrefix", "anotherNewNamespace");
        
        print(htmlspecialchars("\n" . $doc->saveXML() . "\n"));
        // Output
        // > <prefix:localName xmlns:prefix="namespaceURI"
xmlns:newPrefix="attrNamespaceURI"
xmlns:anotherNewPrefix="anotherNewNamespace"/>
        
        $namespaces = $xpath->query("//namespace::*");
        foreach($namespaces as $item) {
                print($item->nodeName . " = " . $item->nodeValue . "\n");
        }
        // Output
        // > xmlns:xml = http://www.w3.org/XML/1998/namespace   (node2)
        // > xmlns:anotherNewPrefix = anotherNewNamespace               (node2)
        // > xmlns:newprefix = attrNamespaceURI                                 (node2)
        // > xmlns:prefix = namespaceURI                                               
 (node2)
?>

This is very fine but the behavior (in this case) isn't very logic: setting 
an elements'/attributes' namespace the intepreter checks whether a prefix is 
defined for this namespace or not. Though the following expression should
not 
result in an error while a prefix for the used namespace is defined already:

<?php
        $node2->setAttributeNS("http://www.w3.org/2000/xmlns/";,
"justAnotherNewPrefix", "justAnotherNewNamespace");
        // Output:
        // > Fatal error:  Uncaught exception 'domexception' with message
'Namespace Error'
?>

But unforunately it does! You could solve this problem by simply setting the 
"http://www.w3.org/2000/xmlns/"; namespace as a default namespace like 
"http://www.w3.org/XML/1998/namespace";. Then the behavior would be more 
consequent...

</pre>

/*############################ END ############################*/

Ok I think this is enough about namespaces for today. I hope i could help 
someone understanding namespaces and the problems with it.

Any comments would be appreciated.

Thanks in advance.

vivi

-- 
PHP General Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to