From 627c7d5685f45bcfd83a45099053cbfface0779c Mon Sep 17 00:00:00 2001 From: Rowan Collins Date: Thu, 9 Nov 2017 23:10:38 +0000 Subject: [PATCH] Fix #11: Use DOM to determine actual namespace of current element --- src/simplexml_dump.php | 28 ++++++++++---------- src/simplexml_tree.php | 53 +++++++++++++++++++++----------------- tests/dump-output/issue-11 | 11 ++++++++ tests/input/issue-11.xml | 2 ++ tests/tree-output/issue-11 | 4 +++ 5 files changed, 62 insertions(+), 36 deletions(-) create mode 100644 tests/dump-output/issue-11 create mode 100644 tests/input/issue-11.xml create mode 100644 tests/tree-output/issue-11 diff --git a/src/simplexml_dump.php b/src/simplexml_dump.php index 28e51b7..3ad21a3 100644 --- a/src/simplexml_dump.php +++ b/src/simplexml_dump.php @@ -39,22 +39,26 @@ function simplexml_dump(SimpleXMLElement $sxml, $return=false) // It's surprisingly hard to find something which behaves consistently differently for an attribute and an element within SimpleXML // The below relies on the fact that the DOM makes a much clearer distinction // Note that this is not an expensive conversion, as we are only swapping PHP wrappers around an existing LibXML resource - if ( dom_import_simplexml($item) instanceOf DOMAttr ) + $dom_item = dom_import_simplexml($item); + + // To what namespace does this element or attribute belong? Returns array( alias => URI ) + $ns_prefix = $dom_item->prefix; + $ns_uri = $dom_item->namespaceURI; + + if ( $dom_item instanceOf DOMAttr ) { $dump .= $indent . 'Attribute {' . PHP_EOL; - // To what namespace does this attribute belong? Returns array( alias => URI ) - $ns = $item->getNamespaces(false); - if ( $ns ) + if ( ! is_null($ns_uri) ) { - $dump .= $indent . $indent . 'Namespace: \'' . reset($ns) . '\'' . PHP_EOL; - if ( key($ns) == '' ) + $dump .= $indent . $indent . 'Namespace: \'' . $ns_uri . '\'' . PHP_EOL; + if ( $ns_prefix == '' ) { $dump .= $indent . $indent . '(Default Namespace)' . PHP_EOL; } else { - $dump .= $indent . $indent . 'Namespace Alias: \'' . key($ns) . '\'' . PHP_EOL; + $dump .= $indent . $indent . 'Namespace Alias: \'' . $ns_prefix . '\'' . PHP_EOL; } } @@ -67,18 +71,16 @@ function simplexml_dump(SimpleXMLElement $sxml, $return=false) { $dump .= $indent . 'Element {' . PHP_EOL; - // To what namespace does this element belong? Returns array( alias => URI ) - $ns = $item->getNamespaces(false); - if ( $ns ) + if ( ! is_null($ns_uri) ) { - $dump .= $indent . $indent . 'Namespace: \'' . reset($ns) . '\'' . PHP_EOL; - if ( key($ns) == '' ) + $dump .= $indent . $indent . 'Namespace: \'' . $ns_uri . '\'' . PHP_EOL; + if ( $ns_prefix == '' ) { $dump .= $indent . $indent . '(Default Namespace)' . PHP_EOL; } else { - $dump .= $indent . $indent . 'Namespace Alias: \'' . key($ns) . '\'' . PHP_EOL; + $dump .= $indent . $indent . 'Namespace Alias: \'' . $ns_prefix . '\'' . PHP_EOL; } } diff --git a/src/simplexml_tree.php b/src/simplexml_tree.php index 518fe31..a80a865 100644 --- a/src/simplexml_tree.php +++ b/src/simplexml_tree.php @@ -37,17 +37,20 @@ function simplexml_tree(SimpleXMLElement $sxml, $include_string_content=false, $ { $root_item = $sxml[$root_item_index]; + // The DOM exposes information which SimpleXML doesn't make easy to find + // Note that this is not an expensive conversion, as we are only swapping PHP wrappers around an existing LibXML resource + $dom_item = dom_import_simplexml($root_item); + + // To what namespace does this element or attribute belong? + $ns_prefix = $dom_item->prefix; + // Special case if the root is actually an attribute // It's surprisingly hard to find something which behaves consistently differently for an attribute and an element within SimpleXML - // The below relies on the fact that the DOM makes a much clearer distinction - // Note that this is not an expensive conversion, as we are only swapping PHP wrappers around an existing LibXML resource - if ( dom_import_simplexml($root_item) instanceOf DOMAttr ) + if ( $dom_item instanceOf DOMAttr ) { - // To what namespace does this attribute belong? Returns array( alias => URI ) - $ns = $root_item->getNamespaces(false); - if ( key($ns) ) + if ( $ns_prefix ) { - $dump .= key($ns) . ':'; + $dump .= $ns_prefix . ':'; } $dump .= $root_item->getName() . '="' . (string)$root_item . '"' . PHP_EOL; } @@ -55,12 +58,10 @@ function simplexml_tree(SimpleXMLElement $sxml, $include_string_content=false, $ { // Display the root node as a numeric key reference, plus a hint as to its tag name // e.g. '[42] // ' - - // To what namespace does this attribute belong? Returns array( alias => URI ) - $ns = $root_item->getNamespaces(false); - if ( key($ns) ) + + if ( $ns_prefix ) { - $root_node_name = key($ns) . ':' . $root_item->getName(); + $root_node_name = $ns_prefix . ':' . $root_item->getName(); } else { @@ -123,19 +124,22 @@ function _simplexml_tree_recursively_process_node($item, $depth, $include_string } } - // To what namespace does this element belong? Returns array( alias => URI ) + // The DOM exposes information which SimpleXML doesn't make easy to find + // Note that this is not an expensive conversion, as we are only swapping PHP wrappers around an existing LibXML resource + $dom_item = dom_import_simplexml($item); + + // To what namespace does this element or attribute belong? + // For top-level elements, cheat, and say they're in the null namespace, to force a ->children() call if ( $depth == 1 ) { - $item_ns = array('' => NULL); + $item_ns_prefix = ''; + $item_ns_uri = null; } else { - $item_ns = $item->getNamespaces(false); - if ( !$item_ns ) - { - $item_ns = array('' => NULL); - } + $item_ns_prefix = $dom_item->prefix; + $item_ns_uri = $dom_item->namespaceURI; } // This returns all namespaces used by this node and all its descendants, @@ -146,18 +150,21 @@ function _simplexml_tree_recursively_process_node($item, $depth, $include_string { $all_ns[''] = NULL; } - + // Prioritise "current" namespace by merging into onto the beginning of the list // (it will be added to the beginning and the duplicate entry dropped) - $all_ns = array_merge($item_ns, $all_ns); - + $all_ns = array_merge( + array($item_ns_prefix => $item_ns_uri), + $all_ns + ); + foreach ( $all_ns as $ns_alias => $ns_uri ) { $children = $item->children($ns_alias, true); $attributes = $item->attributes($ns_alias, true); // If things are in the current namespace, display them a bit differently - $is_current_namespace = ( $ns_uri == reset($item_ns) ); + $is_current_namespace = ( $ns_uri == $item_ns_uri ); $ns_uri_quoted = (strlen($ns_uri) == 0 ? 'null' : "'$ns_uri'"); diff --git a/tests/dump-output/issue-11 b/tests/dump-output/issue-11 new file mode 100644 index 0000000..8a77221 --- /dev/null +++ b/tests/dump-output/issue-11 @@ -0,0 +1,11 @@ +SimpleXML object (1 item) +[ + Element { + Name: 'notinnamespace' + String Content: '' + Content in Namespace test + Namespace URI: 'http://example.com' + Children: 0 + Attributes: 1 - 'isinnamespace' + } +] diff --git a/tests/input/issue-11.xml b/tests/input/issue-11.xml new file mode 100644 index 0000000..896786d --- /dev/null +++ b/tests/input/issue-11.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/tests/tree-output/issue-11 b/tests/tree-output/issue-11 new file mode 100644 index 0000000..df0155f --- /dev/null +++ b/tests/tree-output/issue-11 @@ -0,0 +1,4 @@ +SimpleXML object (1 item) +[0] // + ->attributes('http://example.com') + ->isinnamespace