Skip to content

Instantly share code, notes, and snippets.

@noah-st-amand
Created September 9, 2020 17:26
Show Gist options
  • Save noah-st-amand/da242eabc2ebca33dd1e32c44bd90594 to your computer and use it in GitHub Desktop.
Save noah-st-amand/da242eabc2ebca33dd1e32c44bd90594 to your computer and use it in GitHub Desktop.
XML to Array (with namespaced elements)
<?php
/**
* Given the URL of an XML file, fetch the contents and pass it to _xml_to_array() to be
* converted to an associative array. The file is read into a string and then parsed via
* simplexml_load_string() (rather than parsed directly with simplexml_load_file()),
* because the "LIBXML_NOCDATA" parameter is required in order for CDATA to persist.
*/
function _xml_to_array_from_file($url) {
$array = NULL;
if ($file = file_get_contents($url)) {
if ($xml = simplexml_load_string($file, 'SimpleXMLElement', LIBXML_NOCDATA)) $array = _xml_to_array($xml);
}
return $array;
}
/**
* Convert XML to an associative array, including all namespaced XML elements. Simplified
* and updated from https://outlandish.com/blog/tutorial/xml-to-json/
*/
function _xml_to_array($xml, $safe_key_prefix = '_xml_to_array') {
$array = [];
$name = $xml->getName();
$namespaces = $xml->getDocNamespaces();
$namespaces[''] = NULL;
// Get attributes from all namespaces.
$attributes = [];
foreach ($namespaces as $prefix => $namespace) {
foreach ($xml->attributes($namespace) as $attribute_name => $attribute) {
if ($prefix) $attribute_name = "{$prefix}:{$attribute_name}";
$attributes[$attribute_name] = (string)$attribute;
}
}
// Get child nodes from all namespaces.
$tags = [];
foreach ($namespaces as $prefix => $namespace) {
foreach ($xml->children($namespace) as $child_xml) {
// Recurse into child nodes.
$child_array = _xml_to_array($child_xml);
$child_name = key($child_array);
$child_properties = current($child_array);
if ($prefix) $child_name = "{$prefix}:{$child_name}";
if (!isset($tags[$child_name])) $tags[$child_name] = $child_properties;
else {
// This is a child element being added to a parent for which at least one simlar
// child element already exists. If only one child already exists, that existing
// child is directly under the parent, and thus needs to be converted to the first
// item in an array of children before the current element can be appended. In
// order to determine if this is the case, check if the key of the first child of
// the parent is "0", which indicates that there is already an array of children.
// If there's not already an array of children, the first key will be the key of
// an element within the lone child (e.g., "['title' => ...]").
if (array_key_first($tags[$child_name]) !== 0) $tags[$child_name] = [0 => $tags[$child_name]];
$tags[$child_name][] = $child_properties;
}
}
}
// Get the content of the node.
$content = [];
$content_string = trim((string)$xml);
if ($content_string !== '') $content["{$safe_key_prefix}_content"] = $content_string;
if (!empty($attributes) || (!empty($tags)) || ($content_string === '')) $properties = array_merge($attributes, $tags, $content);
else $properties = $content_string;
$array[$name] = $properties;
return $array;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment