Fix issue #3: attributes with no namespace prefix not shown
Oddly, the XML namespace spec leaves un-prefixed attributes as undefined behaviour, rather than inheriting the default namespace. While fixing this, I've also made most of the output ignore local aliases, because everyone should be using namespace URIs anyway.
This commit is contained in:
parent
627c7d5685
commit
06bfb14e18
18 changed files with 201 additions and 115 deletions
|
@ -42,23 +42,23 @@ function simplexml_dump(SimpleXMLElement $sxml, $return=false)
|
||||||
$dom_item = dom_import_simplexml($item);
|
$dom_item = dom_import_simplexml($item);
|
||||||
|
|
||||||
// To what namespace does this element or attribute belong? Returns array( alias => URI )
|
// To what namespace does this element or attribute belong? Returns array( alias => URI )
|
||||||
$ns_prefix = $dom_item->prefix;
|
$item_ns_alias = $dom_item->prefix;
|
||||||
$ns_uri = $dom_item->namespaceURI;
|
$item_ns_uri = $dom_item->namespaceURI;
|
||||||
|
|
||||||
if ( $dom_item instanceOf DOMAttr )
|
if ( $dom_item instanceOf DOMAttr )
|
||||||
{
|
{
|
||||||
$dump .= $indent . 'Attribute {' . PHP_EOL;
|
$dump .= $indent . 'Attribute {' . PHP_EOL;
|
||||||
|
|
||||||
if ( ! is_null($ns_uri) )
|
if ( ! is_null($item_ns_uri) )
|
||||||
{
|
{
|
||||||
$dump .= $indent . $indent . 'Namespace: \'' . $ns_uri . '\'' . PHP_EOL;
|
$dump .= $indent . $indent . 'Namespace: \'' . $item_ns_uri . '\'' . PHP_EOL;
|
||||||
if ( $ns_prefix == '' )
|
if ( $item_ns_alias == '' )
|
||||||
{
|
{
|
||||||
$dump .= $indent . $indent . '(Default Namespace)' . PHP_EOL;
|
$dump .= $indent . $indent . '(Default Namespace)' . PHP_EOL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$dump .= $indent . $indent . 'Namespace Alias: \'' . $ns_prefix . '\'' . PHP_EOL;
|
$dump .= $indent . $indent . 'Namespace Alias: \'' . $item_ns_alias . '\'' . PHP_EOL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,16 +71,16 @@ function simplexml_dump(SimpleXMLElement $sxml, $return=false)
|
||||||
{
|
{
|
||||||
$dump .= $indent . 'Element {' . PHP_EOL;
|
$dump .= $indent . 'Element {' . PHP_EOL;
|
||||||
|
|
||||||
if ( ! is_null($ns_uri) )
|
if ( ! is_null($item_ns_uri) )
|
||||||
{
|
{
|
||||||
$dump .= $indent . $indent . 'Namespace: \'' . $ns_uri . '\'' . PHP_EOL;
|
$dump .= $indent . $indent . 'Namespace: \'' . $item_ns_uri . '\'' . PHP_EOL;
|
||||||
if ( $ns_prefix == '' )
|
if ( $item_ns_alias == '' )
|
||||||
{
|
{
|
||||||
$dump .= $indent . $indent . '(Default Namespace)' . PHP_EOL;
|
$dump .= $indent . $indent . '(Default Namespace)' . PHP_EOL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$dump .= $indent . $indent . 'Namespace Alias: \'' . $ns_prefix . '\'' . PHP_EOL;
|
$dump .= $indent . $indent . 'Namespace Alias: \'' . $item_ns_alias . '\'' . PHP_EOL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,33 +93,32 @@ function simplexml_dump(SimpleXMLElement $sxml, $return=false)
|
||||||
// This returns all namespaces used by this node and all its descendants,
|
// This returns all namespaces used by this node and all its descendants,
|
||||||
// whether declared in this node, in its ancestors, or in its descendants
|
// whether declared in this node, in its ancestors, or in its descendants
|
||||||
$all_ns = $item->getNamespaces(true);
|
$all_ns = $item->getNamespaces(true);
|
||||||
// If the default namespace is never declared, it will never show up using the below code
|
$has_default_namespace = isset($all_ns['']);
|
||||||
if ( ! array_key_exists('', $all_ns) )
|
|
||||||
|
// If the default namespace is never declared, we need to add a dummy entry for it
|
||||||
|
// We also need to handle the odd fact that attributes are never assigned to the default namespace
|
||||||
|
// The spec basically leaves their meaning undefined: https://www.w3.org/TR/xml-names/#defaulting
|
||||||
|
if ( ! in_array(null, $all_ns, true) )
|
||||||
{
|
{
|
||||||
$all_ns[''] = NULL;
|
$all_ns[] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ( $all_ns as $ns_alias => $ns_uri )
|
// 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_unique(array_merge(
|
||||||
|
array($item_ns_uri),
|
||||||
|
$all_ns
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach ( $all_ns as $ns_uri )
|
||||||
{
|
{
|
||||||
$children = $item->children($ns_uri);
|
$children = $item->children($ns_uri);
|
||||||
$attributes = $item->attributes($ns_uri);
|
$attributes = $item->attributes($ns_uri);
|
||||||
|
|
||||||
// Somewhat confusingly, in the case where a parent element is missing the xmlns declaration,
|
// Don't show children(null) if we have a default namespace defined
|
||||||
// but a descendant adds it, SimpleXML will look ahead and fill $all_ns[''] incorrectly
|
if ( $has_default_namespace && $ns_uri === null )
|
||||||
if (
|
|
||||||
$ns_alias == ''
|
|
||||||
&&
|
|
||||||
! is_null($ns_uri)
|
|
||||||
&&
|
|
||||||
count($children) == 0
|
|
||||||
&&
|
|
||||||
count($attributes) == 0
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
// Try looking for a default namespace without a known URI
|
$children = array();
|
||||||
$ns_uri = NULL;
|
|
||||||
$children = $item->children($ns_uri);
|
|
||||||
$attributes = $item->attributes($ns_uri);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't show zero-counts, as they're not that useful
|
// Don't show zero-counts, as they're not that useful
|
||||||
|
@ -128,14 +127,11 @@ function simplexml_dump(SimpleXMLElement $sxml, $return=false)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$ns_label = (($ns_alias == '') ? 'Default Namespace' : "Namespace $ns_alias");
|
$ns_label = ($ns_uri === null) ? 'Null Namespace' : "Namespace '$ns_uri'";
|
||||||
$dump .= $indent . $indent . 'Content in ' . $ns_label . PHP_EOL;
|
$dump .= $indent . $indent . 'Content in ' . $ns_label . PHP_EOL;
|
||||||
|
|
||||||
if ( ! is_null($ns_uri) )
|
if ( count($children) > 0 )
|
||||||
{
|
{
|
||||||
$dump .= $indent . $indent . $indent . 'Namespace URI: \'' . $ns_uri . '\'' . PHP_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count occurrence of child element names, rather than listing them all out
|
// Count occurrence of child element names, rather than listing them all out
|
||||||
$child_names = array();
|
$child_names = array();
|
||||||
foreach ( $children as $sx_child )
|
foreach ( $children as $sx_child )
|
||||||
|
@ -160,13 +156,12 @@ function simplexml_dump(SimpleXMLElement $sxml, $return=false)
|
||||||
}
|
}
|
||||||
|
|
||||||
$dump .= $indent . $indent . $indent . 'Children: ' . count($children);
|
$dump .= $indent . $indent . $indent . 'Children: ' . count($children);
|
||||||
// Don't output a trailing " - " if there are no children
|
|
||||||
if ( count($children) > 0 )
|
|
||||||
{
|
|
||||||
$dump .= ' - ' . implode(', ', $child_name_output);
|
$dump .= ' - ' . implode(', ', $child_name_output);
|
||||||
}
|
|
||||||
$dump .= PHP_EOL;
|
$dump .= PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( count($attributes) > 0 )
|
||||||
|
{
|
||||||
// Attributes can't be duplicated, but I'm going to put them in alphabetical order
|
// Attributes can't be duplicated, but I'm going to put them in alphabetical order
|
||||||
$attribute_names = array();
|
$attribute_names = array();
|
||||||
foreach ( $attributes as $sx_attribute )
|
foreach ( $attributes as $sx_attribute )
|
||||||
|
@ -182,6 +177,7 @@ function simplexml_dump(SimpleXMLElement $sxml, $return=false)
|
||||||
}
|
}
|
||||||
$dump .= PHP_EOL;
|
$dump .= PHP_EOL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$dump .= $indent . '}' . PHP_EOL;
|
$dump .= $indent . '}' . PHP_EOL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,32 +145,44 @@ function _simplexml_tree_recursively_process_node($item, $depth, $include_string
|
||||||
// This returns all namespaces used by this node and all its descendants,
|
// This returns all namespaces used by this node and all its descendants,
|
||||||
// whether declared in this node, in its ancestors, or in its descendants
|
// whether declared in this node, in its ancestors, or in its descendants
|
||||||
$all_ns = $item->getNamespaces(true);
|
$all_ns = $item->getNamespaces(true);
|
||||||
// If the default namespace is never declared, it will never show up using the below code
|
$has_default_namespace = isset($all_ns['']);
|
||||||
if ( ! array_key_exists('', $all_ns) )
|
|
||||||
|
// If the default namespace is never declared, we need to add a dummy entry for it
|
||||||
|
// We also need to handle the odd fact that attributes are never assigned to the default namespace
|
||||||
|
// The spec basically leaves their meaning undefined: https://www.w3.org/TR/xml-names/#defaulting
|
||||||
|
if ( ! in_array(null, $all_ns, true) )
|
||||||
{
|
{
|
||||||
$all_ns[''] = NULL;
|
$all_ns[] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prioritise "current" namespace by merging into onto the beginning of the list
|
// Prioritise "current" namespace by merging into onto the beginning of the list
|
||||||
// (it will be added to the beginning and the duplicate entry dropped)
|
// (it will be added to the beginning and the duplicate entry dropped)
|
||||||
$all_ns = array_merge(
|
$all_ns = array_unique(array_merge(
|
||||||
array($item_ns_prefix => $item_ns_uri),
|
array($item_ns_uri),
|
||||||
$all_ns
|
$all_ns
|
||||||
);
|
));
|
||||||
|
|
||||||
foreach ( $all_ns as $ns_alias => $ns_uri )
|
foreach ( $all_ns as $ns_uri )
|
||||||
{
|
{
|
||||||
$children = $item->children($ns_alias, true);
|
$children = $item->children($ns_uri);
|
||||||
$attributes = $item->attributes($ns_alias, true);
|
$attributes = $item->attributes($ns_uri);
|
||||||
|
|
||||||
|
// Don't show children(null) if we have a default namespace defined
|
||||||
|
if ( $has_default_namespace && $ns_uri === null )
|
||||||
|
{
|
||||||
|
$children = array();
|
||||||
|
}
|
||||||
|
|
||||||
// If things are in the current namespace, display them a bit differently
|
// If things are in the current namespace, display them a bit differently
|
||||||
$is_current_namespace = ( $ns_uri == $item_ns_uri );
|
$is_current_namespace = ( $ns_uri == $item_ns_uri );
|
||||||
|
|
||||||
|
$force_attribute_namespace = ($ns_uri === null && $item_ns_uri !== null);
|
||||||
|
|
||||||
$ns_uri_quoted = (strlen($ns_uri) == 0 ? 'null' : "'$ns_uri'");
|
$ns_uri_quoted = (strlen($ns_uri) == 0 ? 'null' : "'$ns_uri'");
|
||||||
|
|
||||||
if ( count($attributes) > 0 )
|
if ( count($attributes) > 0 )
|
||||||
{
|
{
|
||||||
if ( ! $is_current_namespace )
|
if ( ! $is_current_namespace || $force_attribute_namespace )
|
||||||
{
|
{
|
||||||
$dump .= str_repeat($indent, $depth)
|
$dump .= str_repeat($indent, $depth)
|
||||||
. "->attributes($ns_uri_quoted)" . PHP_EOL;
|
. "->attributes($ns_uri_quoted)" . PHP_EOL;
|
||||||
|
@ -179,7 +191,7 @@ function _simplexml_tree_recursively_process_node($item, $depth, $include_string
|
||||||
foreach ( $attributes as $sx_attribute )
|
foreach ( $attributes as $sx_attribute )
|
||||||
{
|
{
|
||||||
// Output the attribute
|
// Output the attribute
|
||||||
if ( $is_current_namespace )
|
if ( $is_current_namespace && ! $force_attribute_namespace )
|
||||||
{
|
{
|
||||||
// In current namespace
|
// In current namespace
|
||||||
// e.g. ['attribName']
|
// e.g. ['attribName']
|
||||||
|
|
|
@ -5,8 +5,7 @@ SimpleXML object (1 item)
|
||||||
String Content: '
|
String Content: '
|
||||||
|
|
||||||
'
|
'
|
||||||
Content in Default Namespace
|
Content in Null Namespace
|
||||||
Children: 1 - 1 'movie'
|
Children: 1 - 1 'movie'
|
||||||
Attributes: 0
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -7,9 +7,7 @@ SimpleXML object (1 item)
|
||||||
String Content: '
|
String Content: '
|
||||||
|
|
||||||
'
|
'
|
||||||
Content in Default Namespace
|
Content in Namespace 'https://github.com/IMSoP/simplexml_debug'
|
||||||
Namespace URI: 'https://github.com/IMSoP/simplexml_debug'
|
|
||||||
Children: 1 - 1 'movie'
|
Children: 1 - 1 'movie'
|
||||||
Attributes: 0
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
13
tests/dump-output/basic-multiple-ns
Normal file
13
tests/dump-output/basic-multiple-ns
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
SimpleXML object (1 item)
|
||||||
|
[
|
||||||
|
Element {
|
||||||
|
Namespace: 'https://github.com/IMSoP/simplexml_debug#movies'
|
||||||
|
Namespace Alias: 'movies'
|
||||||
|
Name: 'movies'
|
||||||
|
String Content: '
|
||||||
|
|
||||||
|
'
|
||||||
|
Content in Namespace 'https://github.com/IMSoP/simplexml_debug#movies'
|
||||||
|
Children: 1 - 1 'movie'
|
||||||
|
}
|
||||||
|
]
|
|
@ -1,13 +1,13 @@
|
||||||
SimpleXML object (1 item)
|
SimpleXML object (1 item)
|
||||||
[
|
[
|
||||||
Element {
|
Element {
|
||||||
|
Namespace: 'https://github.com/IMSoP/simplexml_debug'
|
||||||
|
Namespace Alias: 'test'
|
||||||
Name: 'movies'
|
Name: 'movies'
|
||||||
String Content: '
|
String Content: '
|
||||||
|
|
||||||
'
|
'
|
||||||
Content in Namespace test
|
Content in Namespace 'https://github.com/IMSoP/simplexml_debug'
|
||||||
Namespace URI: 'https://github.com/IMSoP/simplexml_debug'
|
|
||||||
Children: 1 - 1 'movie'
|
Children: 1 - 1 'movie'
|
||||||
Attributes: 0
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -3,9 +3,7 @@ SimpleXML object (1 item)
|
||||||
Element {
|
Element {
|
||||||
Name: 'notinnamespace'
|
Name: 'notinnamespace'
|
||||||
String Content: ''
|
String Content: ''
|
||||||
Content in Namespace test
|
Content in Namespace 'http://example.com'
|
||||||
Namespace URI: 'http://example.com'
|
|
||||||
Children: 0
|
|
||||||
Attributes: 1 - 'isinnamespace'
|
Attributes: 1 - 'isinnamespace'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
15
tests/dump-output/issue-3
Normal file
15
tests/dump-output/issue-3
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
SimpleXML object (1 item)
|
||||||
|
[
|
||||||
|
Element {
|
||||||
|
Namespace: 'http://example.com'
|
||||||
|
(Default Namespace)
|
||||||
|
Name: 'Foo'
|
||||||
|
String Content: '
|
||||||
|
|
||||||
|
'
|
||||||
|
Content in Namespace 'http://example.com'
|
||||||
|
Children: 1 - 1 'MaskingChildElement'
|
||||||
|
Content in Null Namespace
|
||||||
|
Attributes: 1 - 'InvisibleAttribute'
|
||||||
|
}
|
||||||
|
]
|
|
@ -8,9 +8,7 @@ SimpleXML object (1 item)
|
||||||
|
|
||||||
|
|
||||||
'
|
'
|
||||||
Content in Namespace soap
|
Content in Namespace 'http://schemas.xmlsoap.org/soap/envelope/'
|
||||||
Namespace URI: 'http://schemas.xmlsoap.org/soap/envelope/'
|
|
||||||
Children: 2 - 1 'Body', 1 'Header'
|
Children: 2 - 1 'Body', 1 'Header'
|
||||||
Attributes: 0
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
26
tests/input/basic-multiple-ns.xml
Normal file
26
tests/input/basic-multiple-ns.xml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" ?>
|
||||||
|
<movies:movies xmlns:movies="https://github.com/IMSoP/simplexml_debug#movies" xmlns:reviews="https://github.com/IMSoP/simplexml_debug#reviews">
|
||||||
|
<movies:movie>
|
||||||
|
<movies:title>PHP: Behind the Parser</movies:title>
|
||||||
|
<movies:characters>
|
||||||
|
<movies:character>
|
||||||
|
<movies:name>Ms. Coder</movies:name>
|
||||||
|
<movies:actor>Onlivia Actora</movies:actor>
|
||||||
|
</movies:character>
|
||||||
|
<movies:character>
|
||||||
|
<movies:name>Mr. Coder</movies:name>
|
||||||
|
<movies:actor>El ActÓr</movies:actor>
|
||||||
|
</movies:character>
|
||||||
|
</movies:characters>
|
||||||
|
<movies:plot>
|
||||||
|
So, this language. It's like, a programming language.
|
||||||
|
Or is it a scripting language? All is revealed in this
|
||||||
|
thrilling horror spoof of a documentary.
|
||||||
|
</movies:plot>
|
||||||
|
<reviews:great-lines>
|
||||||
|
<reviews:line>PHP solves all my web problems</reviews:line>
|
||||||
|
</reviews:great-lines>
|
||||||
|
<reviews:rating type="thumbs">7</reviews:rating>
|
||||||
|
<reviews:rating type="stars">5</reviews:rating>
|
||||||
|
</movies:movie>
|
||||||
|
</movies:movies>
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" ?>
|
<?xml version="1.0" ?>
|
||||||
<movies xmlns:test="https://github.com/IMSoP/simplexml_debug">
|
<test:movies xmlns:test="https://github.com/IMSoP/simplexml_debug">
|
||||||
<test:movie>
|
<test:movie>
|
||||||
<test:title>PHP: Behind the Parser</test:title>
|
<test:title>PHP: Behind the Parser</test:title>
|
||||||
<test:characters>
|
<test:characters>
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
<test:great-lines>
|
<test:great-lines>
|
||||||
<test:line>PHP solves all my web problems</test:line>
|
<test:line>PHP solves all my web problems</test:line>
|
||||||
</test:great-lines>
|
</test:great-lines>
|
||||||
<test:rating type="thumbs">7</test:rating>
|
<test:rating test:type="thumbs">7</test:rating>
|
||||||
<test:rating type="stars">5</test:rating>
|
<test:rating test:type="stars">5</test:rating>
|
||||||
</test:movie>
|
</test:movie>
|
||||||
</movies>
|
</test:movies>
|
||||||
|
|
4
tests/input/issue-3.xml
Normal file
4
tests/input/issue-3.xml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<Foo xmlns="http://example.com" InvisibleAttribute="You ain't seen me, right?">
|
||||||
|
<MaskingChildElement/>
|
||||||
|
</Foo>
|
|
@ -14,6 +14,8 @@ SimpleXML object (1 item)
|
||||||
->great-lines[0]
|
->great-lines[0]
|
||||||
->line[0]
|
->line[0]
|
||||||
->rating[0]
|
->rating[0]
|
||||||
['type']
|
->attributes(null)
|
||||||
|
->type
|
||||||
->rating[1]
|
->rating[1]
|
||||||
['type']
|
->attributes(null)
|
||||||
|
->type
|
||||||
|
|
22
tests/tree-output/basic-multiple-ns
Normal file
22
tests/tree-output/basic-multiple-ns
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
SimpleXML object (1 item)
|
||||||
|
[0] // <movies:movies>
|
||||||
|
->children('https://github.com/IMSoP/simplexml_debug#movies')
|
||||||
|
->movie[0]
|
||||||
|
->title[0]
|
||||||
|
->characters[0]
|
||||||
|
->character[0]
|
||||||
|
->name[0]
|
||||||
|
->actor[0]
|
||||||
|
->character[1]
|
||||||
|
->name[0]
|
||||||
|
->actor[0]
|
||||||
|
->plot[0]
|
||||||
|
->children('https://github.com/IMSoP/simplexml_debug#reviews')
|
||||||
|
->great-lines[0]
|
||||||
|
->line[0]
|
||||||
|
->rating[0]
|
||||||
|
->attributes(null)
|
||||||
|
->type
|
||||||
|
->rating[1]
|
||||||
|
->attributes(null)
|
||||||
|
->type
|
|
@ -1,5 +1,5 @@
|
||||||
SimpleXML object (1 item)
|
SimpleXML object (1 item)
|
||||||
[0] // <movies>
|
[0] // <test:movies>
|
||||||
->children('https://github.com/IMSoP/simplexml_debug')
|
->children('https://github.com/IMSoP/simplexml_debug')
|
||||||
->movie[0]
|
->movie[0]
|
||||||
->title[0]
|
->title[0]
|
||||||
|
@ -14,8 +14,6 @@ SimpleXML object (1 item)
|
||||||
->great-lines[0]
|
->great-lines[0]
|
||||||
->line[0]
|
->line[0]
|
||||||
->rating[0]
|
->rating[0]
|
||||||
->attributes(null)
|
['type']
|
||||||
->type
|
|
||||||
->rating[1]
|
->rating[1]
|
||||||
->attributes(null)
|
['type']
|
||||||
->type
|
|
||||||
|
|
5
tests/tree-output/issue-3
Normal file
5
tests/tree-output/issue-3
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
SimpleXML object (1 item)
|
||||||
|
[0] // <Foo>
|
||||||
|
['InvisibleAttribute']
|
||||||
|
->children('http://example.com')
|
||||||
|
->MaskingChildElement[0]
|
Loading…
Add table
Add a link
Reference in a new issue