Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
279 views
in Technique[技术] by (71.8m points)

php - In SimpleXML, how can I add an existing SimpleXMLElement as a child element?

I have a SimpleXMLElement object $child, and a SimpleXMLElement object $parent.

How can I add $child as a child of $parent? Is there any way of doing this without converting to DOM and back?

The addChild() method only seems to allow me to create a new, empty element, but that doesn't help when the element I want to add $child also has children. I'm thinking I might need recursion here.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Unfortunately SimpleXMLElement does not offer anything to bring two elements together. As @nickf wrote, it's more fitting for reading than for manipulation. However, the sister extension DOMDocument is for editing and you can bring both together via dom_import_simplexml(). And @salathe shows in a related answer how this works for specific SimpleXMLElements.

The following shows how this work with input checking and some more options. I do it with two examples. The first example is a function to insert an XML string:

/**
 * Insert XML into a SimpleXMLElement
 *
 * @param SimpleXMLElement $parent
 * @param string $xml
 * @param bool $before
 * @return bool XML string added
 */
function simplexml_import_xml(SimpleXMLElement $parent, $xml, $before = false)
{
    $xml = (string)$xml;

    // check if there is something to add
    if ($nodata = !strlen($xml) or $parent[0] == NULL) {
        return $nodata;
    }

    // add the XML
    $node     = dom_import_simplexml($parent);
    $fragment = $node->ownerDocument->createDocumentFragment();
    $fragment->appendXML($xml);

    if ($before) {
        return (bool)$node->parentNode->insertBefore($fragment, $node);
    }

    return (bool)$node->appendChild($fragment);
}

This exemplary function allows to append XML or insert it before a certain element, including the root element. After finding out if there is something to add, it makes use of DOMDocument functions and methods to insert the XML as a document fragment, it is also outlined in How to import XML string in a PHP DOMDocument. The usage example:

$parent = new SimpleXMLElement('<parent/>');

// insert some XML
simplexml_import_xml($parent, "
  <test><this>now</this></test>
");

// insert some XML before a certain element, here the first <test> element
// that was just added
simplexml_import_xml($parent->test, "<!-- leave a comment -->
  ", $before = true);

// you can place comments above the root element
simplexml_import_xml($parent, "<!-- this works, too -->", $before = true);

// but take care, you can produce invalid XML, too:
// simplexml_add_xml($parent, "<warn><but>take care!</but> you can produce invalid XML, too</warn>", $before = true);

echo $parent->asXML();

This gives the following output:

<?xml version="1.0"?>
<!-- this works, too -->
<parent>
  <!-- leave a comment -->
  <test><this>now</this></test>
</parent>

The second example is inserting a SimpleXMLElement. It makes use of the first function if needed. It basically checks if there is something to do at all and which kind of element is to be imported. If it is an attribute, it will just add it, if it is an element, it will be serialized into XML and then added to the parent element as XML:

/**
 * Insert SimpleXMLElement into SimpleXMLElement
 *
 * @param SimpleXMLElement $parent
 * @param SimpleXMLElement $child
 * @param bool $before
 * @return bool SimpleXMLElement added
 */
function simplexml_import_simplexml(SimpleXMLElement $parent, SimpleXMLElement $child, $before = false)
{
    // check if there is something to add
    if ($child[0] == NULL) {
        return true;
    }

    // if it is a list of SimpleXMLElements default to the first one
    $child = $child[0];

    // insert attribute
    if ($child->xpath('.') != array($child)) {
        $parent[$child->getName()] = (string)$child;
        return true;
    }

    $xml = $child->asXML();

    // remove the XML declaration on document elements
    if ($child->xpath('/*') == array($child)) {
        $pos = strpos($xml, "
");
        $xml = substr($xml, $pos + 1);
    }

    return simplexml_import_xml($parent, $xml, $before);
}

This exemplary function does normalize list of elements and attributes like common in Simplexml. You might want to change it to insert multiple SimpleXMLElements at once, but as the usage example shows below, my example does not support that (see the attributes example):

// append the element itself to itself
simplexml_import_simplexml($parent, $parent);

// insert <this> before the first child element (<test>)
simplexml_import_simplexml($parent->children(), $parent->test->this, true);

// add an attribute to the document element
$test = new SimpleXMLElement('<test attribute="value" />');
simplexml_import_simplexml($parent, $test->attributes());

echo $parent->asXML();

This is a continuation of the first usage-example. Therefore the output now is:

<?xml version="1.0"?>
<!-- this works, too -->
<parent attribute="value">
  <!-- leave a comment -->
  <this>now</this><test><this>now</this></test>
<!-- this works, too -->
<parent>
  <!-- leave a comment -->
  <test><this>now</this></test>
</parent>
</parent>

I hope this is helpful. You can find the code in a gist and as online demo / PHP version overview.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...