Location: PHPKode > scripts > XML Helper > XmlHelper.class.php
<?php
/**
 * Class of utility functions for XML manipulation.
 * 
 * $array = xmlToArray($xmlString) Convert an XML string to an equivalent array.
 * $xmlString = arrayToXml($array) Convert an array to an equivalent XML string.
 *
 * See the accompanying doc file for details of use.
 * See the accompanying phpunit test file for examples of use.
 *
 * Please report any bugs to richard at roguewavelimited.com.  If possible include
 * a sample data file and description of the bug.
 *
 * If you have any suggestions on how to improve this class, let me know.
 * 
 * You are free to use this class in any way you see fit.
 *
 * Richard Williams
 * Rogue Wave Limited
 * Jan 2, 2011
 */

class XmlHelper {

    // Name to use as name of attribute array.
    private $attributesArrayName = 'attributes';
    
    // Case folding  (uppercasing of names) is on by default (just like normal parser)
    private $foldCase = true;

    // Turn off values returned as arrays and ignore attributes.
    // Attributes and value arrays are on by default.
    private $noAttributes = false;

    // If true, then leaing and trailing whitespace is removed from attributes and values.
    // Off by default.
    private $trimText = false;

    // Vals returned by DOM parser.
    private $vals;

    // Name to use as name of value array.
    private $valueArrayName = 'value';

    /**
     * Create XML from an array.
     *
     * @param <type> $array
     * @param <type> $numericName Name to be used for numeric array indexes.
     */
    function arrayToXml ($array) {
        $dom = new DOMDocument();
        $dom->encoding = "ISO-8859-1";
//        $dom->encoding = "UTF-8";

        $each = each($array);
        $root = $each[0];
        $array = $each[1];

        $rootNode = $dom->appendChild($dom->createElement($root));
        $this->putChildren($dom, $array, $rootNode);

        return $dom->saveXML();
    }
    

    private function putChildren(DOMDocument &$dom, array $arr, DOMElement $node) {

        /*
         * Each node can be one of three types.  'value', an array indicating a sub-node
         * 'and 'attributes' indicating an array of attributes.
         */

        $arrayParent = null;
        foreach($arr as $name => $content) {

            if (strtoupper($name) == 'ATTRIBUTES') {
                foreach($content as $n => $v) {
                    $node->setAttribute($n, $v);
                }
            
            } else if (strtoupper($name) == 'VALUE') {
                $node->appendChild($dom->createTextNode($content));

            } else {

                // An integer index means that this starts a set
                // of elements with the same name.

                if (is_integer($name)) {

                    // Get the parent node and remove the integer element.

                    $child = $node;
                    if (is_null($arrayParent)) {
                        $arrayParent = $node->parentNode;
                        $arrayParent->removeChild($child);

                    } else {
                        $child = $dom->createElement($node->tagName, (is_array($content) ? null : htmlspecialchars($content)));
                    }

                    $arrayParent->appendChild($child);

                } else {

                    $child = $dom->createElement($name, (is_array($content) ? null : htmlspecialchars($content)));
                    $node->appendChild($child);
                }
                
                if (is_array($content)) {
                    self::putChildren($dom, $content, $child);
                }
            }
        }
    }



    function xmlToArray ($xmldata) {

        $parser = xml_parser_create ('ISO-8859-1');
//        $parser = xml_parser_create ('UTF-8');
        xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, $this->foldCase);

        $vals = array();
        if (!xml_parse_into_struct ($parser, $xmldata, $vals)) {
            throw new Exception(sprintf("XML error: %s at line %d",
                            xml_error_string(xml_get_error_code($parser)),
                            xml_get_current_line_number($parser)));        }
        $folding = xml_parser_get_option($parser, XML_OPTION_CASE_FOLDING);
        xml_parser_free ($parser);

        $this->vals = $vals;

        if ($folding) {
            $this->valueArrayName = strtoupper($this->valueArrayName);
            $this->attributesArrayName = strtoupper($this->attributesArrayName);
        }

        // Trim attributes and values if option set.

        if ($this->trimText) {
            foreach($vals as &$val) {
                if (count($val['attributes']) > 0) {
                    foreach($val['attributes'] as $name => $att) {
                        if ($this->trimText) $val['attributes'][$name] = trim($att);
                    }
                }
                if (isset($val['value'])) {
                    if ($this->trimText) $val['value'] = trim($val['value']);
                }
            }
        }

        $i = 0;
        $children = $this->getChildren ($vals, $i, $vals[$i]['type']);

        // Add value and attributes to if present.

        $valatt = $this->addAttributesAndValue($vals, 0);
        if (! empty($valatt)) $children = array_merge($valatt, $children);

        $result [$vals [$i]['tag']] = $children;
        return $result;
    }


    /*
     * Called recursively to parse the vals array.
     */
    private function getChildren ($vals, &$i, $type) {

        // If the type is 'complete' it means that there are no children of this element.

        $children = array ();

        if ($type != 'complete') {

            while ($vals [++$i]['type'] != 'close') {
                $type = $vals [$i]['type'];
                $tag = $vals [$i]['tag'];

                $valatt = $this->addAttributesAndValue($vals, $i);

                // Check if we already have an element with this name and need to create an array

                if (isset ($children [$tag])) {

                    $temp = array_keys ($children [$tag]);

                    if (is_string ($temp [0])) {
                        $a = $children [$tag];
                        unset ($children [$tag]);
                        $children [$tag][0] = $a;
                    }

                    $child = $this->getChildren($vals, $i, $type);
                    if (! empty($valatt)) $child = array_merge($valatt, $child);
                    $children [$tag][] = $child;

                } else {

                    $children [$tag] = $this->getChildren ($vals, $i, $type);

                    // If a scalar is returned from addAttributeAndValue just set that as the return
                    // otherwise merge it with the existing children.

                    if (! is_array($valatt)) {
                        $children[$tag] = $valatt;
                    } else {
                        if (! empty($valatt)) $children[$tag] = array_merge($valatt, $children[$tag]);
                    }
                }
            }
        }

        return $children;
    }

    /**
     * Add any attributes or values from parser to the output array.
     *
     * @param array $vals Parser vals.
     * @param int $i Nesting level.
     * @return array.
     */
    private function addAttributesAndValue($vals, $i) {

        $array = array();
        if ($this->noAttributes) {
            if (isset ($vals[$i]['value'])) $array = $vals [$i]['value'];

        } else {
            if (isset ($vals[$i]['value'])) $array = array($this->valueArrayName => $vals [$i]['value']);
            if (isset ($vals[$i]['attributes']))
                $array = array_merge($array, array($this->attributesArrayName => $vals[$i]['attributes']));
        }
        return $array;
    }

    /**
     * Provides access to the parser's vals array.
     * @return array of parser vals
     */
    function getParserVals() {
        return $this->vals;
    }

    /**
     * If true then values are returned as raw values instead of as a 'VALUE' array.
     * Also attributes are ignored.
     * @param boolean $switch
     */
    function setNoAttributes($switch) {
        $this->noAttributes = $switch;
    }

    /**
     * Set case folding (uppercasing) of all returned array names.
     * This includes any value and attribute arrays.
     * @param boolean $switch
     */
    function setCaseFolding($switch) {
        $this->foldCase = $switch;
    }

    /**
     * If set, trims leading and trailing white space from attribute value and value text.
     * @param boolean $switch
     */
    function setTrimText($switch) {
        $this->trimText = $switch;
    }
}
?>
Return current item: XML Helper