I need to implement the following algorithm with SimpleXML:
You can override the text content of a node returned by a SimpleXML XPath query by assigning to $node[0]
, e.g.
foreach ( $sx->xpath('//text()') as $text_node )
{
$text_node[0] = 'Hello';
}
However, beware that SimpleXML does not really have a representation of a text node per se, so this kind of loop will behave oddly if there are both child elements and text within an element.
For instance given the XML foo
, the two text nodes containing foo
and bar
will both be represented in SimpleXML by the first element, the entire contents of which will be replaced by
'Hello'
, twice over, as shown in the below (live demo here). Using a counter variable in the substituted text, we can see clearly what's happening - the desired output would be Hello 1
, but the actual result is Hello 2Hello 3
.
$sx = simplexml_load_string('foo barbaz quux');
$counter = 1;
foreach ( $sx->xpath('//text()') as $text_node )
{
$text_node[0] = 'Hello ' . $counter++;
}
echo $sx->asXML();
This kind of manipulation, at least as you've framed the problem (finding text nodes, rather than iterating, possibly recursively, over a particular set of elements), is much more suited to the DOM API rather than SimpleXML. Bear in mind that there is no performance difference between the two (they are both wrappers around the same XML parser), and that you can combine operations using the two APIs on the same document by using simplexml_import_dom() and dom_import_simplexml(), again without additional overhead as the document doesn't need to be re-parsed.
Here is the above example fixed by using a mixture of SimpleXML and DOM (live demo). If this were the whole code, you could just parse with DOM directly, but this demonstrates how easy they are to mix if you already have other code manipulating this document with SimpleXML. Note that at the end, we output the XML using the original SimpleXML object - we don't need to run simplexml_import_dom($dom)
, because both objects refer to the same parsed "document" in memory.
$sx = simplexml_load_string('foo barbaz quux');
$dom = dom_import_simplexml($sx);
$counter = 1;
$xpath = new DOMXpath($dom->ownerDocument);
foreach ( $xpath->query('//text()') as $text_node )
{
$text_node->nodeValue = 'Hello ' . $counter++;
}
echo $sx->asXML();