Класс SimpleXMLElement

(PHP 5, PHP 7, PHP 8)

Введение

Представляет собой элемент в XML-документе.

Обзор классов

class SimpleXMLElement implements Stringable, Countable, RecursiveIterator {
/* Методы */
public __construct(
    string $data,
    int $options = 0,
    bool $dataIsURL = false,
    string $namespaceOrPrefix = "",
    bool $isPrefix = false
)
public addAttribute(string $qualifiedName, string $value, ?string $namespace = null): void
public addChild(string $qualifiedName, ?string $value = null, ?string $namespace = null): ?SimpleXMLElement
public asXML(?string $filename = null): string|bool
public attributes(?string $namespaceOrPrefix = null, bool $isPrefix = false): ?SimpleXMLElement
public children(?string $namespaceOrPrefix = null, bool $isPrefix = false): ?SimpleXMLElement
public count(): int
public getDocNamespaces(bool $recursive = false, bool $fromRoot = true): array|false
public getName(): string
public getNamespaces(bool $recursive = false): array
public hasChildren(): bool
public key(): string
public next(): void
public registerXPathNamespace(string $prefix, string $namespace): bool
public rewind(): void
public __toString(): string
public valid(): bool
public xpath(string $expression): array|null|false
}

Список изменений

Версия Описание
8.0.0 Класс SimpleXMLElement теперь реализует интерфейсы Stringable, Countable, и RecursiveIterator.

Содержание

add a note add a note

User Contributed Notes 28 notes

up
187
rmirabelle
14 years ago
To further previous comments and drive the point home:

What makes SimpleXMLElement tricky to work with is that it feels and behaves like an object, but is actually a system RESOURCE,  (specifically a libxml resource). 

That's why you can't store a SimpleXMLElement to $_SESSION or perform straight comparison operations on node values without first casting them to some type of object.  $_SESSION expects to store 'an object' and comparison operators expect to compare 2 'objects' and SimpleXMLElements are not objects. 

When you echo or print a node's value, PHP converts the value (a resource) into a string object for you.  It's a time saver for sure, but can fool you into thinking that your SimpleXMLElement is an object. 

Hope this helps clarify
up
69
juanfhj at gmail dot spam dot me dot not dot com
13 years ago
To access the underlying element as a string, it's necessary to make the cast $x = (string)$my_xml_element.
up
39
Anonymous
13 years ago
Warning to anyone trying to parse XML with a key name that includes a hyphen ie.)
<subscribe>
    <callback-url>example url</callback-url>
</subscribe>

In order to access the callback-url you will need to do something like the following:
<?php
$xml
= simplexml_load_string($input);
$callback = $xml->{"callback-url"};
?>
If you attempt to do it without the curly braces and quotes you will find out that you are returned a 0 instead of what you want.
up
25
CameronXie
6 years ago
Map xml to array (with attributes)

<?php declare(strict_types=1);

/**
* @param SimpleXMLElement $xml
* @return array
*/
function xmlToArray(SimpleXMLElement $xml): array
{
   
$parser = function (SimpleXMLElement $xml, array $collection = []) use (&$parser) {
       
$nodes = $xml->children();
       
$attributes = $xml->attributes();

        if (
0 !== count($attributes)) {
            foreach (
$attributes as $attrName => $attrValue) {
               
$collection['attributes'][$attrName] = strval($attrValue);
            }
        }

        if (
0 === $nodes->count()) {
           
$collection['value'] = strval($xml);
            return
$collection;
        }

        foreach (
$nodes as $nodeName => $nodeValue) {
            if (
count($nodeValue->xpath('../' . $nodeName)) < 2) {
               
$collection[$nodeName] = $parser($nodeValue);
                continue;
            }

           
$collection[$nodeName][] = $parser($nodeValue);
        }

        return
$collection;
    };

    return [
       
$xml->getName() => $parser($xml)
    ];
}
up
16
saganmarketing.com
12 years ago
Parsing an invalid XML string through SimpleXML causes the script to crash completely (usually) therefore it is best to make sure the XML is valid before parsing with something like this:

// Must be tested with ===, as in if(isXML($xml) === true){}
// Returns the error message on improper XML
function isXML($xml){
    libxml_use_internal_errors(true);

    $doc = new DOMDocument('1.0', 'utf-8');
    $doc->loadXML($xml);

    $errors = libxml_get_errors();

    if(empty($errors)){
        return true;
    }

    $error = $errors[0];
    if($error->level < 3){
        return true;
    }

    $explodedxml = explode("r", $xml);
    $badxml = $explodedxml[($error->line)-1];

    $message = $error->message . ' at line ' . $error->line . '. Bad XML: ' . htmlentities($badxml);
    return $message;
}
up
9
php at keith tyler dot com
14 years ago
The root node element of your input XML string is not retrievable as a property.

<?php
$xml
="<foo>bar</foo>";
$sxe=new SimpleXMLElement($xml);
print
$sxe->foo;
?>

prints nothing. You can only get to the root element via the array index method ($sxe[0]).

Also, you may not have two (or more) root elements -- that is apparently not well-formed XML.

<?php
$xml
="<foo/><bar/>";
$sxe=new SimpleXMLElement($xml);
?>

throws an exception. A Q&D is to append an arbitraty root node structure to both ends of the input:

<?php
$xml
="<foo/><bar/>";
$sxe=new SimpleXMLElement("<z>".$xml."</z>");
?>

Doing this also solves the above problem of root node property accessibility. (It may not work if your XML string includes a declaration.)
up
13
heaver
12 years ago
XML to JSON conversion without '@attributes'
<?php
function XML2JSON($xml) {

        function
normalizeSimpleXML($obj, &$result) {
           
$data = $obj;
            if (
is_object($data)) {
               
$data = get_object_vars($data);
            }
            if (
is_array($data)) {
                foreach (
$data as $key => $value) {
                   
$res = null;
                   
normalizeSimpleXML($value, $res);
                    if ((
$key == '@attributes') && ($key)) {
                       
$result = $res;
                    } else {
                       
$result[$key] = $res;
                    }
                }
            } else {
               
$result = $data;
            }
        }
       
normalizeSimpleXML(simplexml_load_string($xml), $result);
        return
json_encode($result);
    }
?>
up
6
brett at brettbrewer dot com
15 years ago
Figuring out how to access the properties of a SimpleXmlElement object was a little tricky for me. In particular, it took a while to discover that I needed to cast my SimpleXmlElement properties to be of type "string" to print them or do comparisons on them. For instance, assuming you already have a string of xml in $xmlstr...

<?php
$sxml
= new SimpleXmlElement($xmlstr);

if ((string)
$sxml->property== "somevalue") {
    echo (string)
$sxml->property;
}
?>
The properties of a SimpleXmlElement object are objects themselves, so you need to put "(string)" before them, which casts their values to a string instead of an object. I assume if you were doing a numeric comparison you'd want to cast to an (int) or something numeric instead.
up
4
cherubangel at gmail dot com
14 years ago
Note that changing attributes from within a foreach loop, especially namespaced attributes, can be very tricky.

For example, when trying to change the value of an existing xlink:href attribute:
<?php
foreach($xmlelement -> attributes('xlink', true) as $attribute => $attribvalue){
   
$attribvalue[0] = 'value'; // Throws an error
   
$attribvalue = 'value'; // Does not change your XML
   
$xmlelement -> addAttribute($attribute, 'value', 'http://www.w3.org/1999/xlink'); // Adds an attribute, does not change existing one.
   
$xmlelement[$attribute] = 'value'; // Adds an attribute, does not change existing one.
}
?>

Instead, you should access the array returned by the attributes() function directly, like this:
<?php
    $xmlelement
-> attributes('xlink', true) -> href = 'value'; // Works!
?>
up
4
ivandosreisandrade at gmail dot com
15 years ago
Hello,

here goes my contribution for those whom are struggling to understand how SimpleXMLElement works.

After some time trying to figure out how this works, I've came up to this small example:

<?php
    $xmlstr
= "<?xml version='1.0' ?>\n".
             
// optionally you can specify a xml-stylesheet for presenting the results. just uncoment the following line and change the stylesheet name.
              /* "<?xml-stylesheet type='text/xsl' href='xml_style.xsl' ?>\n". */
             
"<book></book>";

   
// create the SimpleXMLElement object with an empty <book> element
   
$xml = new SimpleXMLElement($xmlstr);

   
// add some child nodes
   
$xml->addChild("title", "Title of my book");
   
$xml->addChild("abstract", "My book is about learning to work with SimpleXMLElement");

   
// add some more child nodes
   
$chapter1 = $xml->addChild("chapter_1");
   
// add an attribute to child chapter_1
   
$chapter1->addAttribute("chapter_title", "Introduction to my book");

   
$chapter2 = $xml->addChild("chapter_2");
   
$chapter2->addAttribute("chapter_title", "Development of my book");

   
$chapter3 = $xml->addChild("chapter_3");
   
$chapter3->addAttribute("chapter_title", "Another chapter of my book");

   
$conclusion = $xml->addChild("conclusion", "The ending of my book");

   
// insert the header to tell the browser how to read the document
   
header("Content-type: text/xml");
   
// print the SimpleXMLElement as a XML well-formed string
   
echo $xml->asXML();
?>

With this script you can just copy-paste and try to understand how it works.
I hope it can help somebody :)
up
4
francs at seznam dot cz
14 years ago
Be aware when you trying to cast some attribute to boolean.

(boolean)$xml->attributes()->someAtt;

returns TRUE if attribute is array([0] => 0);

use (boolean)(int) instead.
up
2
triplepoint at gmail dot com
14 years ago
It's occasionally useful to add an XML processing instruction to a SimpleXMLElement (treating it as if it were a full document).
<?php
class SimpleXMLElement_Plus extends SimpleXMLElement {

    public function
addProcessingInstruction( $name, $value )
    {
       
// Create a DomElement from this simpleXML object
       
$dom_sxe = dom_import_simplexml($this);
       
       
// Create a handle to the owner doc of this xml
       
$dom_parent = $dom_sxe->ownerDocument;
       
       
// Find the topmost element of the domDocument
       
$xpath = new DOMXPath($dom_parent);
       
$first_element = $xpath->evaluate('/*[1]')->item(0);
       
       
// Add the processing instruction before the topmost element           
       
$pi = $dom_parent->createProcessingInstruction($name, $value);
       
$dom_parent->insertBefore($pi, $first_element);
    }
}
?>

For example, if you had a simpleXMLElement_Plus object made out of the xml fragment:
<xml><content /></xml>

And you needed the output to be:
<?xml version="1.0"?>
<?xml
-stylesheet type="text/xsl" href="xsl/xsl.xsl"?>
<xml><content/></xml>

you could do (using the class above):
<?php
$xml
= new SimpleXMLElement_Plus('<xml><content /></xml>');
$xml->addProcessingInstruction('xml-stylesheet', 'type="text/xsl" href="xsl/xsl.xsl"');
echo
$xml->asXML();
?>
up
1
joshduck at gmail dot com
14 years ago
You should cast elements to a float (or even string) if you plan on using them in addition, multiplication, etc. If you don't then PHP will incorrectly treat the node values as an integer.

<?php
$obj
= new SimpleXMLElement('<root>
    <a>1.9</a>
    <b>1.9</b>
</root>'
);

var_dump($obj->a + $obj->b);
var_dump((float)$obj->a + (float)$obj->b);
var_dump((string)$obj->a + (string)$obj->b);
?>

The first line gives: int(2)
The second and third give the expected result: float(3.8)
up
1
frame at dynamiccreated dot de
11 years ago
Important note I miss here in the documentation:

SimpleXML supports Array/Iteration-Methods. Therefore it is possible to

add attributes
edit attributes
remove attributes

add nodes
edit nodes
remove nodes

This is the reason why SimpleXML provides only add-methods not deleting- or editing-methods. We also need this methods because SimpleXML acts as a normal class and
new member will not converted to a new node.

Deleting a node seems not to be possible within a foreach-loop. The reason is simple. To do that we need a valid key, but the Iterator only gives us a "understandable feedback" on which node we are working on: the tag name.

So this will not work:

<?php
foreach($doc->seg as $key => $seg)
{
    if((string)
$seg['id'] === 'whatever')
    {
        unset(
$seg); // only clears local copy
       
unset($seg[$key]); // wrong index "seg"
   
}
}
?>

But this, eg. will work:
<?php
unset($doc->seg[2]);
?>

In case of doubt always do a print_r/var_dump(). It nicely shows the real linked indexes.
up
0
ohcc at 163 dot com
1 year ago
You can remove SimpleXMLElement nodes using php's unset() language construct.

<?php
    $xml
= <<<'EOT'
<users>
    <user>
        <name>orange</name>
        <sex>male</sex>
        <homepage>wuxiancheng.cn</homepage>
    </user>
    <user>
        <name>tangerine</name>
        <sex>male</sex>
        <homepage>51-n.com</homepage>
    </user>
</users>
EOT;
   
$sxe = new SimpleXMLElement($xml);
   
// This will remove the first user node
   
unset($sxe->user[0]);
   
// This will remove all user nodes
   
unset($sxe->user);
    echo
$sxe->asXML();
?>
up
1
Patanjali
4 years ago
Further to rmirabelle's comment, to compare two SimpleXML elements, do the comparison between the objects returned by the dom_import_simplexml function, as per:

<?php
if(dom_import_simplexml($simplexml_element_1)===dom_import_simplexml($simplexml_element_2)){
  ...
}
?>

You must ensure that both are SimpleXML elements before conversion to DOM objects.
up
1
guilhermeasn at yahoo dot com dot br
4 years ago
Class with a method to transform array or object to XML:

class MakeXML {

    public static function array_obj_to_xml($data, \SimpleXMLElement &$xmlObj) {
        foreach((array) $data as $key => $value) {
            if(is_numeric($key)) {
                $key = "n" . $key;
            }
            if(is_array($value) or is_object($value)) {
                $subnode = $xmlObj->addChild($key);
                self::array_obj_to_xml($value, $subnode);
            } else {
                $xmlObj->addChild($key, htmlspecialchars($value));
            }
        }
    }

}

Example:

$object = new \stdClass();
$object->example = "Try this class";

$objxml = new \SimpleXMLElement('<API/>');
MakeXML::array_obj_to_xml($objxml, $objxml);
echo $objxml->asXML();
up
0
Budu
6 years ago
public function crearXml(){
        $pruebaXml = <<<XML
<?xml version="1.0" encoding="UTF-8" ?>
<Root></Root>
XML;
$example = new SimpleXMLElement($pruebaXml); //Crea un nuevo objeto SimpleXMLElement
        $example->addAttribute('id', '1');//Añade un elemento hijo al nodo XML

        while( $nodo = $this->iteraciones->iterate()){
            $nodos = $example->addChild('Nodo');
            $nodos->addAttribute('id',$nodo->getId());
            $acierto = $nodos->addChild('nuevoHijo', $nodo->getX());
        }
        $miFichero = $example->asXML();//Retorna un string XML correcto basado en un elemento SimpleXML
        $miArchivo = fopen("xml/example.xml", "w+");//Abre un fichero o un URL
        fwrite($miArchivo, $miFichero);//Escritura archivo
    }
up
1
xxxargonxxx at gmail dot com
7 years ago
How to view, edit and remove elements.
Speaking in terms of CRUD style, there is no methods to read, edit and remove nodes. But it is possible.

2. How child nodes works (understanding)
<?php
       
echo "Exercise 2 \n";
       
       
$rootNode = new SimpleXMLElement('<xml_root>complex node</xml_root>');  // Create root node
       
$childNode1 = $rootNode->addChild('type_one','node 1');                 // Create child node 1 with name 'type_one'
       
$childNode2 = new SimpleXMLElement('<type_one>node 2</type_one>');      // Create child node 2 with name 'type_one'
       
$childNode3 = new SimpleXMLElement('<type_two>node 3</type_two>');      // Create child node 3 with name 'type_two' (different!)
        // Now $rootNode has only one children because other child nodes was created separately
        // Lets paste child node to root node
       
echo $childNode1;

       
$rootNode->{'type_one'} = $childNode2;      // Note that the property name is 'type_one'
       
$rootNode->{'type_two'} = $childNode3;      // Note that the property name is 'type_two'
       
var_dump($rootNode);
       
/*
        object(SimpleXMLElement)#68 (2) {
            ["type_one"]=>
            string(6) "node 2"
            ["type_two"]=>
            string(6) "node 3"
        }
        */
        // We see that "node 1" disappeared, that's because assigning one node to some property of another in not a magic.
        // To be clear, the real calls looks like:
       
$rootNode->{'type_one'}[0] = (string) $childNode2;
       
$rootNode->{'type_two'}[0] = (string) $childNode3;
       
// Explanation:
        // A: (string)
        // It means that you can't assign node itself, only its string representation because assigned node will be casted to string anyway.
        // It also means that tag name of assigned node doesn't matter - its name will be whatever name you specified in {} braces.
        // B: [0]
        // The array access operator ([0]) means that each tag name (type_one and type_two) has its own collection with integer indexes.
        // If you want to add another tag with same name, you should increment index:
       
$rootNode->{'type_one'}[1] = $childNode2;
       
var_dump($rootNode);
       
/*
        object(SimpleXMLElement)#68 (2) {
            ["type_one"]=>
            array(2) {
                [0]=>
                string(6) "node 2"
                [1]=>
                string(6) "node 2"
            }
            ["type_two"]=>
            string(6) "node 3"
        }
        */

        // Note 1. The value of $childNode1 has been changed to "node 1" - that's because the child node, returned from addChild() method
        // is referenced with parent node. If you change its content in one side - it changes in other side too:
       
$childNode1->{0} = 'renewed node 1';
       
// But it won't happen with $childNode2 - it is single and has no reference with parent node:
       
$childNode2->{0} = 'renewed node 2';
       
var_dump($rootNode);
       
/*
        object(SimpleXMLElement)#68 (2) {
            ["type_one"]=>
            array(2) {
                [0]=>
                string(14) "renewed node 1"
                [1]=>
                string(6) "node 2"
            }
            ["type_two"]=>
            string(6) "node 3"
        }
        */
       
        // Note 2. When you added child nodes, the parent node's string content is still alive, var_dump doesn't show it,
        // but you can see through echoing XML:
       
echo "--1--\n".$rootNode->asXML();
       
/*
        <xml_root>complex node <--------- here!
                <type_one>renewed node 1</type_one>
                <type_two>node 3</type_two>
                <type_one>node 2</type_one>
        </xml_root>
        */
        // See 'complex node'? Bad news that you can't change string content anymore, if we try to call `$rootNode->{0} = 'something'` -
        // it overrides all content - both string and child nodes! I don't know how to write to string content only in that case.
        // But still you can read string content and remove entire node as described above.

        // So the following statements gives same result, except $result variable value:
       
$someNode = new SimpleXMLElement('<div>text</div>');
       
$result = $rootNode->addChild('div','text');                    // $result is SimpleXMLElement
       
$result = $rootNode->addChild($someNode->getName(),$someNode);  // $result is SimpleXMLElement
       
$result = $rootNode->{'div'}[] = 'text*';                       // $result is string 'text*'
       
$result = $rootNode->{$someNode->getName()}[] = $someNode;      // $result is string 'text'
?>
up
0
xxxargonxxx at gmail dot com
7 years ago
See previous two parts to get better understanding of how these tricks works. Example shown here refers to part two. (Sorry, length limitation)

3. Tricks
<?php
       
// The real profit is in combination of property and array accesses and unset($someNode->{0}) trick -
        // it allows you to remove whatever node you want:
       
unset($rootNode->{'div'}[2]);   // remove node with * mark (text*)
       
        // Another trick is removing child node using child node object
       
$newChildNode = $rootNode->addChild('div','text**');    // Create
       
unset($newChildNode->{0});      // Remove, node removed from parent object too!
       
       
echo "--2--\n".$rootNode->asXML();
       
/*
        <xml_root>complex node
                <type_one>renewed node 1</type_one>
                <type_two>node 3</type_two>
                <type_one>node 2</type_one>
                <div>text</div>
                <div>text</div>
                <div>text</div>
        </xml_root>
        */
?>
up
0
Yukull
11 years ago
xml to object conversion function :
<?php
/**
    @param:
        $xml: SimpleXMLElement
        $force: set to true to always create 'text', 'attribute', and 'children' even if empty
    @return
        object with attributs:
            (string) name: XML tag name
            (string) text: text content of the attribut name
            (array) attributes: array witch keys are attribute key and values are attribute value
            (array) children: array of objects made with xml2obj() on each child
**/
function xml2obj($xml,$force = false){

   
$obj = new StdClass();   

   
$obj->name = $xml->getName();
   
   
$text = trim((string)$xml);
   
$attributes = array();
   
$children = array();
   
    foreach(
$xml->attributes() as $k => $v){
       
$attributes[$k]  = (string)$v;
    }
   
    foreach(
$xml->children() as $k => $v){
       
$children[] = xml2obj($v,$force);
    }
   
   
    if(
$force or $text !== '')
       
$obj->text = $text;
       
    if(
$force or count($attributes) > 0)
       
$obj->attributes = $attributes;
       
    if(
$force or count($children) > 0)
       
$obj->children = $children;
       
       
    return
$obj;
}
?>
up
0
demian dot katz at villanova dot edu
12 years ago
Here is a more namespace-aware version of the earlier function for attaching one SimpleXMLElement to another.  I'm sure it could still be further improved (right now it generates some redundant xmlns definitions), but it seems to be working well enough for my purposes so far.

function SimpleXMLElement_append($parent, $child)
{
    // get all namespaces for document
    $namespaces = $child->getNamespaces(true);

    // check if there is a default namespace for the current node
    $currentNs = $child->getNamespaces();
    $defaultNs = count($currentNs) > 0 ? current($currentNs) : null;
    $prefix = (count($currentNs) > 0) ? current(array_keys($currentNs)) : '';
    $childName = strlen($prefix) > 1
        ? $prefix . ':' . $child->getName() : $child->getName();

    // check if the value is string value / data
    if (trim((string) $child) == '') {
        $element = $parent->addChild($childName, null, $defaultNs);
    } else {
        $element = $parent->addChild(
            $childName, htmlspecialchars((string)$child), $defaultNs
        );
    }

    foreach ($child->attributes() as $attKey => $attValue) {
        $element->addAttribute($attKey, $attValue);
    }
    foreach ($namespaces as $nskey => $nsurl) {
        foreach ($child->attributes($nsurl) as $attKey => $attValue) {
            $element->addAttribute($nskey . ':' . $attKey, $attValue, $nsurl);
        }
    }

    // add children -- try with namespaces first, but default to all children
    // if no namespaced children are found.
    $children = 0;
    foreach ($namespaces as $nskey => $nsurl) {
        foreach ($child->children($nsurl) as $currChild) {
            SimpleXMLElement_append($element, $currChild);
            $children++;
        }
    }
    if ($children == 0) {
        foreach ($child->children() as $currChild) {
            SimpleXMLElement_append($element, $currChild);
        }
    }
}
up
0
ms dot n at 163 dot com
13 years ago
Adds a new function for SimpleXMLElement class, in order to output HTML code.

<?php
class CeiXML extends SimpleXMLElement{
public function
asHTML(){
$ele=dom_import_simplexml($this);
$dom = new DOMDocument('1.0', 'utf-8');
$element=$dom->importNode($ele,true);
$dom->appendChild($element);
return
$dom->saveHTML();
}
}
?>
up
-1
kweij at lsg dot nl
14 years ago
I'm using SimpleXML for, ofcourse, it's simplicity, however I did wanted to manipulate the xml and combining one SimpleXMLElement with any other, so I wrote this function to add a SimpleXMLElement-child.

<?php
function SimpleXMLElement_append($key, $value) {
   
// check class
   
if ((get_class($key) == 'SimpleXMLElement') && (get_class($value) == 'SimpleXMLElement')) {
       
// check if the value is string value / data
       
if (trim((string) $value) == '') {
           
// add element and attributes
           
$element = $key->addChild($value->getName());
            foreach (
$value->attributes() as $attKey => $attValue) {
               
$element->addAttribute($attKey, $attValue);
            }
           
// add children
           
foreach ($value->children() as $child) {
               
SimpleXMLElement_append($element, $child);
            }
        } else {
           
// set the value of this item
           
$element = $key->addChild($value->getName(), trim((string) $value));
        }
    } else {
       
// throw an error
       
throw new Exception('Wrong type of input parameters, expected SimpleXMLElement');
    }
}
?>

I'd recommend SimpleXMLElement to extend it's addChild() function with the functionalitity above.
up
-1
kurtbr at gmail dot com
7 years ago
Here is a helper class to convert a PHP array to XML
You would call it doing the following:

<?php
$xml
= new Xml();
$xml-> generateXmlFromArray($array, 'entities', 'entity');
?>

<?php
class Xml
{
    public function
getXmlFromArray($value, \SimpleXMLElement &$xmlElement, $entity, $starting = null)
    {

       
$handleValue = function($value){
            if(
is_string($value)){
               
$value = htmlspecialchars($value);
            }
            return
$value;
        };
       
$addChild = function($name, $value, &$subNode = null)use(&$xmlElement, $handleValue, $entity){
            if(
is_array($value)){
                if(!
$subNode instanceof \SimpleXMLElement){
                   
$currentKey = key($value);
                   
$initialValue = null;
                    if(
is_numeric($currentKey)){
                        if(!
is_array($value[$currentKey])){
                           
$initialValue = $value[$currentKey];
                            unset(
$value[$currentKey]);
                        }
                    }
                   
$subNode = $xmlElement->addChild($name, $initialValue);
                }
               
$this->getXmlFromArray($handleValue($value), $subNode, $name);
            } else {
               
$xmlElement->addChild($name, $handleValue($value));
            }
        };

        if(
is_array($value))
        {
            if(
is_numeric(key($value))){
               
$setSubNodePrimitiveValue = function($value)use(&$xmlElement, $entity, $handleValue){
                   
$value = $handleValue($value);
                   
$children = $xmlElement->children();
                   
$children[] = $value;
                };
                foreach (
$value as $item)
                {
                    if(!
is_array($item)){
                       
$setSubNodePrimitiveValue($item);
                    } else {
                        if(
$starting === true){
                           
$addChild($entity, $item);
                        } else {
                           
$addChild($entity, $item, $xmlElement);
                        }
                    }
                }
            } else {
                foreach (
$value as $subEntity => $subEntityItem)
                {
                   
$addChild($subEntity, $subEntityItem);
                }
            }
        } else {
           
$xmlElement->addChild($entity, $handleValue($value));
        }
    }

   
/**
     * @param array $array
     * @param string $openingTag
     * @param string $entity
     * @param string $nameSpace
     * @param bool $isPrefixed
     * @return \SimpleXMLElement
     */
   
public function generateXmlFromArray(array $array, string $openingTag, string $entity, $nameSpace = '', $isPrefixed = false)
    {
       
$xmlString = '<'.$openingTag.'></'.$openingTag.'>';
       
$xml = new \SimpleXMLElement($xmlString, LIBXML_NOERROR, false, $nameSpace, $isPrefixed);
       
$this->getXmlFromArray($array, $xml, $entity, true);
        return
$xml;
    }

   
/**
     * @param string $xml
     * @return bool
     */
   
public function validateXml(string $xml)
    {
       
$dom = new \DOMDocument();
       
$dom->loadXML($xml);
        return
$dom->validate();
    }

    public function
loadXmlPathAsArray(string $xmlPath)
    {
       
$xml   = simplexml_load_file($xmlPath);
       
$array = json_decode(json_encode($xml), TRUE);
        return (array)
$array;
    }

   
/**
     * @param string $xmlString
     * @return array
     */
   
public function loadXmlStringAsArray(string $xmlString)
    {
       
$array = (array) @simplexml_load_string($xmlString);
        if(!
$array){
           
$array = (array) @json_decode($xmlString, true);
        } else{
           
$array = (array)@json_decode(json_encode($array), true);
        }
        return
$array;
    }

   
/**
     * @param string $xmlString
     * @return \SimpleXMLElement
     */
   
public function loadXmlString(string $xmlString)
    {
        return @
simplexml_load_string($xmlString);
    }
}
?>
up
-3
Pavel Musil pavel dot musil at gmail dot com
13 years ago
Simple recursive function to append XML object into SimpleXMLElement node:

<?php
// root: parent element - SimpleXMLElement instance
// append: XML object from simplexml_load_string

function xml_join($root, $append) {
    if (
$append) {
        if (
strlen(trim((string) $append))==0) {
           
$xml = $root->addChild($append->getName());
            foreach(
$append->children() as $child) {
               
xml_join($xml, $child);
            }
        } else {
           
$xml = $root->addChild($append->getName(), (string) $append);
        }
        foreach(
$append->attributes() as $n => $v) {
           
$xml->addAttribute($n, $v);
        }
    }
}

$xml_append = simplexml_load_string('<data><items><item id="1">value</item></items></data>');
$xml_root = new SimpleXMLElement('<result></result>');

$cxml = $xml_root->addChild('clone');
xml_join($cxml, $xml_append->items->item[0]);

print
$xml_root->asXML();
?>

Result:
<?xml version="1.0"?>
<result>
    <clone>
        <item id="1">
            value
        </item>
    </clone>
</result>
up
-8
dans at dansheps dot com
13 years ago
I created a little function to parse the simple XML into a structure which is easier to iterate(I believe anyways) than the simpleXML node structure.

<?php
function parseSimpleXML($xmldata)
    {
       
$childNames = array();
       
$children = array();

        if(
$xmldata->count() !== 0 )
        {
            foreach(
$xmldata->children() AS $child )
            {
               
$name = $child->getName();

                if( !isset(
$childNames[$name]) )
                {
                   
$childNames[$name] = 0;
                }

               
$childNames[$name]++;
               
$children[$name][] = $this->parseSimpleXML($child);
            }
        }

       
$returndata = new XMLNode();
        if(
$xmldata->attributes()->count() > 0 )
        {
           
$returndata->{'@attributes'} = new XMLAttribute();
            foreach(
$xmldata->attributes() AS $name => $attrib )
            {
               
$returndata->{'@attributes'}->{$name} = (string)$attrib;
            }
        }

        if(
count($childNames) > 0 )
        {
            foreach(
$childNames AS $name => $count )
            {
                if(
$count === 1 )
                {
                   
$returndata->{$name} = $children[$name][0];
                }
                else
                {
                   
$returndata->{$name} = new XMLMultiNode();
                   
$counter = 0;
                    foreach(
$children[$name] AS $data )
                    {
                       
$returndata->{$name}->{$counter} = $data;
                       
$counter++;
                    }
                }
            }
        }
        else
        {
            if( (string)
$xmldata !== '' )
            {
               
$returndata->{'@innerXML'} = (string)$xmldata;
            }
        }
        return
$returndata;
    }
?>
up
-3
xxxargonxxx at gmail dot com
7 years ago
How to view, edit and remove elements.
Speaking in terms of CRUD style, there is no methods to read, edit and remove nodes. But it is possible.

1. How to work with single node and its value (basics)
<?php
       
echo "Exercise 1 \n";
       
// Create
       
$someNode = new SimpleXMLElement('<xml_root>simple node</xml_root>');
       
// Read
       
echo $someNode->{0} ."\n";      // Echoes "simple node"
       
echo $someNode ."\n";           // Echoes "simple node", might be used for read case too
        // Edit
       
$someNode->{0} = 'new value';
        echo
$someNode->{0} ."\n";      // Echoes "new value", value is changed!
        // Remove
       
unset($someNode->{0});

       
// Don't touch this node anymore otherwise it will throw a warning:
        // "Warning:  SimpleXMLElement::asXML(): Node no longer exists in <...> on line <...>"
        // echo $someNode ."\n";      // Throws the warning
       
        // Recap: property access to zero property ($someNode->{0}) is the way to read and edit string content of the node AND
        // to remove the node itself
?>
To Top