11

I am creating an Atom feed, when I tried below to add xmlns:i as an attribute -

$node->addAttribute("xmlns:i","http://www.w3.org/2001/XMLSchema-instance"); 

I got this as an output -

i="http://www.w3.org/2001/XMLSchema-instance"

"xmlns:" part was cut off. do I need to escape the :-character? Or is they any other way to add this namespace?

IMSoP
  • 89,526
  • 13
  • 117
  • 169
Sourabh
  • 4,545
  • 11
  • 39
  • 45

3 Answers3

22

If you want to add an attribute from the namespace/prefix i to $node don't bother declaring the namespace beforehand. Just use the third parameter of addAttribute() to provide the namespace uri for the prefix you're using in the first parameter.

$node = new SimpleXMLElement('<root></root>');
$node->addAttribute("i:somename", "somevalue", 'http://www.w3.org/2001/XMLSchema-instance'); 
echo $node->asXml();

prints

<?xml version="1.0"?>
<root xmlns:i="http://www.w3.org/2001/XMLSchema-instance" i:somename="somevalue"/>

If the attribute itself isn't needed, you can then remove it with unset(), leaving the namespace declaration.

unset($node->attributes('i', TRUE)['somename']);
outis
  • 75,655
  • 22
  • 151
  • 221
VolkerK
  • 95,432
  • 20
  • 163
  • 226
  • 4
    This seems like such a waste. It's declaring the namespace on every line instead on just the root. – Nathan H Jun 10 '10 at 21:43
  • 1
    I think he’s adding a dummy attribute to the root, just to convince SimpleXMLElement to add the namespace declaration. Ugly (but PHP’s fault, not VolkerK’s) but it works if you don’t mind the extra attribute. – Olivier 'Ölbaum' Scherler Feb 22 '12 at 08:47
  • 1
    The best solution is actually the one provided by Olivier 'Ölbaum' Scherler –  May 15 '13 at 08:29
  • @NathanH The same prefix can mean different things in different parts of a document, so referencing the namespace URI on each element is the only sure way to indicate the element's namespace. It's then up to the serialization step to remove the redundant `xmlns` declarations, which it does: https://3v4l.org/TSdO5 – IMSoP Apr 22 '17 at 15:03
20

If you don’t want to have to add a dummy attribute to your root element, you can declare the namespace manually on it by adding an xmlns attribute for your i prefix:

<root xmlns:i="http://www.w3.org/2001/XMLSchema-instance">

In order to do so, and as hinted in an existing answer (Unable to add Attribute with Namespace Prefix using PHP Simplexml), you have to prefix the new attribute with xmlns: (since the xmlns: namespace prefic is not declared in your document). And since xmlns: is part of the name of that attribute, you therfore need two occurrences of xmlns:

$uri = 'http://www.w3.org/2001/XMLSchema-instance';

$root = new SimpleXMLElement('<root/>');
$root->addAttribute( 'xmlns:xmlns:i', $uri );
                      ######

$child = $root->addChild('foo');
$child->addAttribute( 'xmlns:i:bar', 'baz');
                       ######

echo $root->asXml();

Gives (formatted manually for readability):

<?xml version="1.0"?>
<root xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <foo i:bar="baz"/>
</root>

So this xmlns: prefixing seems to cheat it. Note that if you reload the element after setting that attribute, it is possible to use the namespace uri as well when adding children, and this without specifying the prefix:

$root = new SimpleXMLElement( $root->asXML() );

$child = $root->addChild('foo');
$child->addAttribute( 'i:bar', 'bazy', $uri );
                                       ####

echo $root->asXml();

Gives (again, formatted manually):

<?xml version="1.0"?>
<root xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <foo i:bar="baz"/>
    <foo i:bar="bazy"/>
</root>

This second example seems to be closer to the intended (or at least expected) use.

Note that the only way to do this properly would be to use the more complete (but unfortunately also more complex and more verbose) DOMDocument classes. This is outlined in How to declare an XML namespace prefix with DOM/PHP?.

Community
  • 1
  • 1
  • 7
    You don't need to prefix with `"xmlns:"`, you can prefix with any old rubbish (including whitespace!) so long as you include _something_ before your first colon `:`, for example `$child->addAttribute( 'HACK:i:bar', 'baz');`. In other words, this isn't some supported yet obscure way of getting an xmlns prefix in place, we're just "tricking" the dodgy parser. So we can't rely on this to work into the future. We should use the DOM for this, until _proper_ ns prefix support is added to SimpleXMLElement. PS same hack works to prefix element names, too. – Sepster May 27 '13 at 10:54
  • 1
    The parser is dodgy? In PHP? I am shocked. Shocked! – Olivier 'Ölbaum' Scherler Jan 17 '14 at 19:16
  • Are you being sarcastic? I can't quite tell? ;-) – Sepster Jan 21 '14 at 01:01
  • Note that this adds the namespace as an attribute, rather than a declaration: it appears in `$root->attributes()`, but not `$root->attributes('xmlns', TRUE)`, `$root->getNamespaces()` or `$root->getDocNamespaces()`. Similarly, 'i:bar' isn't in namespace 'i': compare `$child->attributes()` and `$child->attributes('i', TRUE)`. – outis Sep 15 '16 at 17:43
4

I found this looking for the same thing, and none of the answers really worked for me. So, I tried a different route. If SimpleXML isn't managing namespace correctly, use DOM instead.

So, something like this should work:

$s = new simplexmlelement('<root/>');
$d = dom_import_simplexml($s);
$d->setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:i", "http://www.w3.org/2001/XMLSchema-instance");
$s->addChild("bar", "bazy", "http://www.w3.org/2001/XMLSchema-instance");
$f = $s->addChild("foo", "quux");
$f->addAttribute("i:corge", "grault", "http://www.w3.org/2001/XMLSchema-instance");

That will result in this:

<?xml version="1.0"?>
<root xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
   <i:bar>bazy</i:bar>
   <foo i:corge="grault">quux</foo>
</root>
Dan Jones
  • 1,337
  • 11
  • 22