Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP array_walk_recursive() for SimpleXML objects?

Tags:

php

spl

simplexml

I would like to apply a function to every node in a SimpleXML object.

<api>
   <stuff>ABC</stuff>
   <things>
      <thing>DEF</thing>
      <thing>GHI</thing>
      <thing>JKL</thing>
   </things>
</api>

//function reverseText($str){};

<api>
   <stuff>CBA</stuff>
   <things>
      <thing>FED</thing>
      <thing>IHG</thing>
      <thing>LKJ</thing>
   </things>
</api>

How would I apply reverseText() to every node to get the second XML snippet?

like image 813
yumcha Avatar asked Dec 20 '22 04:12

yumcha


2 Answers

Here the Standard PHP Library can come to the rescue.

One option is to use the (little known) SimpleXMLIterator. It is one of several RecursiveIterators available in PHP and a RecursiveIteratorIterator from the SPL can be used to loop over and change all of the elements' text.

$source = '
<api>
   <stuff>ABC</stuff>
   <things>
      <thing>DEF</thing>
      <thing>GHI</thing>
      <thing>JKL</thing>
   </things>
</api>
';

$xml = new SimpleXMLIterator($source);
$iterator = new RecursiveIteratorIterator($xml);
foreach ($iterator as $element) {
    // Use array-style syntax to write new text to the element
    $element[0] = strrev($element);
}
echo $xml->asXML();

The above example outputs the following:

<?xml version="1.0"?>
<api>
   <stuff>CBA</stuff>
   <things>
      <thing>FED</thing>
      <thing>IHG</thing>
      <thing>LKJ</thing>
   </things>
</api>
like image 87
salathe Avatar answered Dec 31 '22 00:12

salathe


You can create an array of all nodes in the document with the SimpleXMLElement::xpath() method.

Then you can use array_walk on that array. However you don't want to reverse the string of every node, only of those elements which don't have any child elements.

$source = '
<api>
   <stuff>ABC</stuff>
   <things>
      <thing>DEF</thing>
      <thing>GHI</thing>
      <thing>JKL</thing>
   </things>
</api>
';    

$xml = new SimpleXMLElement($source);

array_walk($xml->xpath('//*'), function(&$node) {
    if (count($node)) return;
    $node[0] = strrev($node);
});

echo $xml->asXML();

The above example outputs the following:

<?xml version="1.0"?>
<api>
   <stuff>CBA</stuff>
   <things>
      <thing>FED</thing>
      <thing>IHG</thing>
      <thing>LKJ</thing>
   </things>
</api>

The xpath query allows more control for example with namespaces.

like image 28
hakre Avatar answered Dec 30 '22 22:12

hakre