Wednesday, May 02, 2007

Angry at the DOM

This is not a bug, it's a feature. This really frustrates me.

In short, the below code example demonstrates a huge inconsistency between uses of a particularly really poorly documented (in the areas you want to use, anyway) implementation of DOM level 3.

$d = new DOMDocument();
$example = $d->createElementNS('','example');
$example->setAttributeNS('', 'bar:bar',"value");
$example->setAttributeNS('', 'monkey',"value");

$d = new DOMDocument();
$example = $d->createElementNS('','example');
$example->setAttributeNS('', 'bar:bar',"value");
$example->setAttributeNS('', 'monkey',"value");

What the heck am I angry about? You can...
  • $a->setAttribute('href', '');
  • $a->getAttribute('href');
  • $a->getAttributeNS('', 'href');
but only 50% of the time do you get to do
  • $a->setAttributeNS('', 'href', '');
Now the reason for it makes sense. If is not mapped to an xml namespace (xmlns); then how does it add that? Accordingly, you have to add in a qualifiedName

  • $a->setAttributeNS('', 'xhtml:href', '');
Up until this point, I am perfectly fine with how things are implemented, except forgetting to put in a qualified name gives you a "Namespace Error", when it should be telling you that "No qualified xmlns was found". I can deal with that. What I can't deal with is this:
  1. $a->setAttributeNS('', 'xhtml:href', '');
  2. $a->setAttributeNS('', 'href', '');
What threw an exception moments ago no longer does, because doing the first line changed a state internally, and invisibly.

So to get it right all the time, you use the same qualified name prefix, right? 'xhtml:foo'.
  1. $a->setAttributeNS('', 'xhtml:href', '');
  2. $a->setAttributeNS('', 'xhtml:href', '');
But what if someone gives you a document and uses different qualifiers for the same urls?
  1. $a->setAttributeNS('', $a->lookupPrefix('') . ':href', '');
  2. $a->setAttributeNS('', $a->lookupPrefix('') . ':href', '');
But now, you are stuck, because if the namespace isn't declared, lookupPrefix gives you a null. You are suddenly right back at the start, and you get a heck of a lot of exceptions. This should be easy to do. Instead, you have to be careful about doing it, otherwise you end up with a pile of unusable code very quickly.

So the best strategy to deal with this is:

determinePrefix($element, $namespace, $suggestedPrefix = null)
  • Should check for the existence $namespace in $element
  • If no $namespace prefix exists, register the namespace on the element
  • If a $suggestedPrefix is defined, use that
  • If a $namespace prefix is found, return that

... which is clunky and verbose.

Why doesn't the internals of DOMDocument either:
  • Refuse at any time to let you add something without a qualified prefix, and give you methods to add such a prefix - registerNamespace(); adds (xmlns:fish=""/)
  • Or, let you add in all of the unqualified prefixes you like, and give them anonymous names; and if you later append the node to another document or whatever, resolve them then.
Post a Comment