diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 0e02a843e95a..e81a2193a623 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -1107,7 +1107,14 @@ static HashTable *sxe_get_prop_hash(zend_object *object, int is_debug) /* {{{ */ } GET_NODE(sxe, node); - node = php_sxe_get_first_node_non_destructive(sxe, node); + if (sxe->iter.type == SXE_ITER_CHILD) { + /* Start at the first child rather than at the first element child, so that + * non-element nodes (e.g. PIs, comments) before it are not skipped. Like + * sxe_prop_is_empty(), only do so if there is a matching element child. */ + node = php_sxe_get_first_node_non_destructive(sxe, node) ? node->children : NULL; + } else { + node = php_sxe_get_first_node_non_destructive(sxe, node); + } if (node && sxe->iter.type != SXE_ITER_ATTRLIST) { if (node->type == XML_ATTRIBUTE_NODE) { @@ -1128,7 +1135,7 @@ static HashTable *sxe_get_prop_hash(zend_object *object, int is_debug) /* {{{ */ } while (node) { - if (node->children != NULL || node->prev != NULL || node->next != NULL || xmlIsBlankNode(node)) { + if (node->children != NULL || node->prev != NULL || node->next != NULL || xmlIsBlankNode(node) || sxe->iter.type == SXE_ITER_CHILD) { SKIP_TEXT(node); } else { if (node->type == XML_TEXT_NODE) { diff --git a/ext/simplexml/tests/gh12168.phpt b/ext/simplexml/tests/gh12168.phpt new file mode 100644 index 000000000000..3f4cd9bba986 --- /dev/null +++ b/ext/simplexml/tests/gh12168.phpt @@ -0,0 +1,74 @@ +--TEST-- +GH-12168 (Mixing processing instructions and element nodes gives inconsistent results in SimpleXML) +--EXTENSIONS-- +simplexml +--FILE-- +'); +var_dump($sxe->children()); + +$sxe = simplexml_load_string(''); +var_dump($sxe->children()); + +foreach ([ + '', + '', + '', + 'text', +] as $xml) { + $sxe = simplexml_load_string($xml); + echo $xml, ': ', json_encode(array_keys((array) $sxe->children())), "\n"; +} + +/* Non-element nodes only show up alongside a matching element, consistent with (bool) */ +foreach ([ + '', + '', + 't', + ']>&e;', +] as $xml) { + $sxe = simplexml_load_string($xml); + echo $xml, ': ', json_encode(array_keys((array) $sxe->children())), ' ', var_export((bool) $sxe->children(), true), "\n"; +} + +$sxe = simplexml_load_string(''); +var_dump(array_keys((array) $sxe->children('urn:a'))); +$sxe = simplexml_load_string(''); +var_dump(array_keys((array) $sxe->children('urn:a')), (bool) $sxe->children('urn:a')); + +?> +--EXPECTF-- +object(SimpleXMLElement)#%d (2) { + ["x"]=> + object(SimpleXMLElement)#%d (0) { + } + ["hello"]=> + object(SimpleXMLElement)#%d (0) { + } +} +object(SimpleXMLElement)#%d (2) { + ["hello"]=> + object(SimpleXMLElement)#%d (0) { + } + ["x"]=> + object(SimpleXMLElement)#%d (0) { + } +} +: ["comment","x"] +: ["x","comment"] +: ["pi","x","y"] +text: [] +: [] false +: [] false +t: [] false +]>&e;: ["e","x"] true +array(2) { + [0]=> + string(2) "pi" + [1]=> + string(1) "x" +} +array(0) { +} +bool(false)