Recently I’ve been fiddeling around with an old project of mine, and decided to extend it into a full-blown framework, just for kicks. Born from GDO, a ORM layer for PHP, it’s supposed to serve no real purpose but personal gratification.
Anyway, parallel with the framework I am building a website to showcase it. I also decided that in this specific case all the HTML documents should be built from either PHPs DOM or SimpleXML libraries, I personally dislike having HTML strewn across the project, so I’ve seperated structures like forms and tables into classes and the idea is that I can simply append them to the current page at a specific node and it’ll render the HTML when everything is added.
The problem
Here in lies the problem, PHPs XML libraries (at least the ones that I tried) require you to drag the top node to which you want to append whatever around in all helper classes and such, allow me to demonstrate.
<?php $document = new DOMDocument(); $table = new DOMElement('table'); $row = new DOMElement('tr'); $row->appendChild(new DOMElement('td', 'test element')); $table->appendChild($row); $document->appendChild($table);
One would expect this all to work out fine and produce the following XML document;
<?xml version='1.0' encoding='utf-8' ?> <table><tr><td>test element</td></tr></table>
In fact, it won’t. It won’t even run. PHP bails out during run time with the error “DOMException: No Modification Allowed Error“. This is because every DOMElement object not created with DOMDocument::createElement() is by default read-only, as you can read in the description of DOMDocument::__construct().
Besides begging the PHP developers to make every DOMElement not read-only on construction, the obvious solution would be to add the DOMElement in question to the DOMDocument and continue building from there, but that’s the problem. It seems counter-intuitive to pass the DOMElement or DOMDocument that you want to append your seperate structure to to every constructor of every XML producing class (or via normal methods of course).
I’ve taken the literal example of PHPs DOM classes, but I’ve tested SimpleXML as well, which provides even less flexibilty regarding node reuse and other fancy stuff one could want to do with XML documents.
Workarounds
There are several solutions to this problem, to name a few:
- Create a singleton class that holds one instance of
DOMDocument, and use a wrapper method somewhere to either get a validDOMElement - Have all classes that produce XML nodes use a fake
DOMDocument, and then useDOMDocument::importNode()at the return-point to import the nodes (very inefficient, especially when using a deep-copy). - Have the classes that produce XML nodes return nested arrays containing all name -> content relations of the nodes and run through them when it’s sensible to do so.
I’ll leave it as an exercise to the reader to pick out the best method. I haven’t found a solution yet, but I hope to contact the DOM XML module maintainer and discuss this with him/her.
Immutable (read-only) data is annoying in many ways, in many libraries and frameworks. The main problem is that once you’ve made something read-only, there’s generally no sane way back. At least if something’s mutable, then you can still choose to not-change it.
I believe that immutability, if necessary at all, should come with an option to either disable it, or to replace the object or value with a copy that can be modified.
Consider what happens when I download an ODT; it’ll open in OOo Writer, which will treat it read-only. This makes sense: from a user perspective, the document is on the web server, on which I don’t have write access. It would only be confusing if I could change it and save that, as I would actually be saving to a temporary file and my work would be lost soon. However, I can save the document in my home directory and then edit it and save the modified copy again. A similar behaviour would be very welcome in some APIs.
I feel the same way about constants too, by the way. Yes, it’s nice if you can indicate that you intended the value to stay the same forever, but more than once I’ve used Perl’s immense flexibility to override this decision, to bolt on extra functionality that the original author probably never thought of when in their infinite wisdom they decided that the value be constant. In other programming languages, or with complexer object oriented frameworks, one may not be so lucky and may not be able to say “(I think) I know what I’m doing, so get out of my way.”
Hmm, you did provide me with an idea, using PHPs Reflection API maybe I could adjust the read-only behaviour in run time, but sadly, it seems that as with SimpleXML, DOM objects are constructed with some kind of black magic that prevents explicit reverse engineering on the classes. There are no properties, static or otherwise, only the methods already described on php.net. Would have been nice though