Lately we came across the problem that the XML/XSL view engine of Stubbles stopped working when upgrading to a newer PHP version which included libxml 2.7 instead of the previous 2.6 version. As of course Stubbles should be able to run with PHP 5.3 which uses libxml 2.7 this had to be fixed. So I tried to investigate the issue, and it took me some hours to find out what the problem was. Once the problem was understood it was very easy to fix. However, to come to the point were it became clear what was wrong was quite disturbing.
But, first to the problem. In the XML/XSL view engine we provide a simple to use XSL template which allows inclusion of XML parts from other XML resource files, so that you can easily share content between different routes, e.g. the header or footer of a website. The template looks like this:
<stub:include href="foo.xml" part="bar"/>
In a first step this will be transformed to something like
<xi:include href="xinc://default/foo.xml?part=bar#xpointer(/parts/part[@name='bar']/node())"><xi:fallback> ... some fallback code ... </xi:fallback></xi:include>
So, what does the href attribute contain? First, the xinc scheme points to our internal stream wrapper which is applied to transform the given XML document first before returning it for inclusion. Second, default/foo.xml gives our stream wrapper the information which xml file from which project to read. The query with the part is only additional information for debug purposes. After this, an XPointer is appended as fragment. The XPointer is used by libxml to include only the part from the returned XML document that can be accessed using the XPath expression given with the XPointer. This works well with libxml 2.6, but stopped to work with 2.7.
First I thought it might be a problem with the stream wrapper. But it was not. Neither was the stream wrapper called nor did libxml complain about an invalid URL. Next step was to try to reproduce the issue using real file URLs instead of the stream wrapper URLs. Still did not work. So, I tried to ask Google. The answers where not really helpful. Last resort: read again in the XInclude standard. After reading the final XInclude 1.0 W3C recommendation I found out that there is a separate xpointer attribute to be used. By changing the generated XInclude statement to
<xi:include href="xinc://default/foo.xml?part=bar" xpointer="xpointer(/parts/part[@name='bar']/node())"><xi:fallback> ... some fallback code ... </xi:fallback></xi:include>
it resumes working as if nothing happened.
Turns out, the XPointer part is the problem with libxml 2.7. With version 2.7 libxml completely refuses to process the XInclude if there is such an XPointer fragment appended to the URL given with the href attribute. No warning or error is thrown for this. Luckily changing the XInclude statement to comply with the final standard works with 2.6 as well.
You may ask why we used a version which was not supported by the final recommendation. Well, it just worked. And as it turns out, the first version was the way to use XPointers in XInclude 1.0 W3C Candidate Recommendation. It seems libxml just dropped support for this with the 2.7 series.
What can we learn from this:
Read the real and final standard recommendations.
Read them again.
Provide users of your software with better hints on what is wrong. I think it would have cost much lesser time to find the problem if there would have been at least a warning about the xpointer fragment used in the href attribute instead of just doing nothing.