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