mirror of
https://github.com/php/php-src.git
synced 2024-09-21 18:07:23 +00:00
Apply SimpleXML iterator fixes only on master
Many methods in SimpleXML reset the iterator when called. This has the consequence that mixing these operations with loops can cause infinite loops, or the loss of iteration data. Some people may however rely on the resetting behaviour. To prevent unintended breaks in stable branches, let's only apply the fix to master. This reverts GH-12193, GH-12229, GG-12247 for stable branches while keeping them on master, adding a note in UPGRADING as well.
This commit is contained in:
parent
1f5bea3452
commit
b842ea4fa8
5
NEWS
5
NEWS
@ -39,13 +39,8 @@ PHP NEWS
|
||||
|
||||
- SimpleXML:
|
||||
. Fixed bug GH-12170 (Can't use xpath with comments in SimpleXML). (nielsdos)
|
||||
. Fixed bug GH-12192 (SimpleXML infinite loop when getName() is called
|
||||
within foreach). (nielsdos)
|
||||
. Fixed bug GH-12223 (Entity reference produces infinite loop in
|
||||
var_dump/print_r). (nielsdos)
|
||||
. Fixed bug GH-12208 (SimpleXML infinite loop when a cast is used inside a
|
||||
foreach). (nielsdos)
|
||||
. Fixed bug #55098 (SimpleXML iteration produces infinite loop). (nielsdos)
|
||||
. Fixed bug GH-12167 (Unable to get processing instruction contents in
|
||||
SimpleXML). (nielsdos)
|
||||
. Fixed bug GH-12169 (Unable to get comment contents in SimpleXML).
|
||||
|
@ -78,10 +78,10 @@ static void _node_as_zval(php_sxe_object *sxe, xmlNodePtr node, zval *value, SXE
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static xmlNodePtr php_sxe_get_first_node_non_destructive(php_sxe_object *sxe, xmlNodePtr node)
|
||||
static xmlNodePtr php_sxe_get_first_node(php_sxe_object *sxe, xmlNodePtr node)
|
||||
{
|
||||
if (sxe && sxe->iter.type != SXE_ITER_NONE) {
|
||||
return php_sxe_reset_iterator_no_clear_iter_data(sxe, false);
|
||||
return php_sxe_reset_iterator(sxe, 1);
|
||||
} else {
|
||||
return node;
|
||||
}
|
||||
@ -165,7 +165,7 @@ static xmlNodePtr sxe_get_element_by_name(php_sxe_object *sxe, xmlNodePtr node,
|
||||
if (sxe->iter.type == SXE_ITER_NONE) {
|
||||
sxe->iter.type = SXE_ITER_CHILD;
|
||||
}
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
sxe->iter.type = orgtype;
|
||||
}
|
||||
|
||||
@ -251,11 +251,11 @@ long_dim:
|
||||
if (sxe->iter.type == SXE_ITER_ATTRLIST) {
|
||||
attribs = 1;
|
||||
elements = 0;
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
attr = (xmlAttrPtr)node;
|
||||
test = sxe->iter.name != NULL;
|
||||
} else if (sxe->iter.type != SXE_ITER_CHILD) {
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
attr = node ? node->properties : NULL;
|
||||
test = 0;
|
||||
if (!member && node && node->parent &&
|
||||
@ -303,7 +303,7 @@ long_dim:
|
||||
xmlNodePtr mynode = node;
|
||||
|
||||
if (sxe->iter.type == SXE_ITER_CHILD) {
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
}
|
||||
if (sxe->iter.type == SXE_ITER_NONE) {
|
||||
if (member && Z_LVAL_P(member) > 0) {
|
||||
@ -437,12 +437,12 @@ long_dim:
|
||||
if (sxe->iter.type == SXE_ITER_ATTRLIST) {
|
||||
attribs = 1;
|
||||
elements = 0;
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
attr = (xmlAttrPtr)node;
|
||||
test = sxe->iter.name != NULL;
|
||||
} else if (sxe->iter.type != SXE_ITER_CHILD) {
|
||||
mynode = node;
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
attr = node ? node->properties : NULL;
|
||||
test = 0;
|
||||
if (!member && node && node->parent &&
|
||||
@ -688,7 +688,7 @@ static int sxe_prop_dim_exists(zend_object *object, zval *member, int check_empt
|
||||
attribs = 0;
|
||||
elements = 1;
|
||||
if (sxe->iter.type == SXE_ITER_CHILD) {
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -696,11 +696,11 @@ static int sxe_prop_dim_exists(zend_object *object, zval *member, int check_empt
|
||||
if (sxe->iter.type == SXE_ITER_ATTRLIST) {
|
||||
attribs = 1;
|
||||
elements = 0;
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
attr = (xmlAttrPtr)node;
|
||||
test = sxe->iter.name != NULL;
|
||||
} else if (sxe->iter.type != SXE_ITER_CHILD) {
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
attr = node ? node->properties : NULL;
|
||||
test = 0;
|
||||
}
|
||||
@ -740,7 +740,7 @@ static int sxe_prop_dim_exists(zend_object *object, zval *member, int check_empt
|
||||
if (elements) {
|
||||
if (Z_TYPE_P(member) == IS_LONG) {
|
||||
if (sxe->iter.type == SXE_ITER_CHILD) {
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
}
|
||||
node = sxe_get_element_by_offset(sxe, Z_LVAL_P(member), node, NULL);
|
||||
} else {
|
||||
@ -810,7 +810,7 @@ static void sxe_prop_dim_delete(zend_object *object, zval *member, bool elements
|
||||
attribs = 0;
|
||||
elements = 1;
|
||||
if (sxe->iter.type == SXE_ITER_CHILD) {
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -818,11 +818,11 @@ static void sxe_prop_dim_delete(zend_object *object, zval *member, bool elements
|
||||
if (sxe->iter.type == SXE_ITER_ATTRLIST) {
|
||||
attribs = 1;
|
||||
elements = 0;
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
attr = (xmlAttrPtr)node;
|
||||
test = sxe->iter.name != NULL;
|
||||
} else if (sxe->iter.type != SXE_ITER_CHILD) {
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
attr = node ? node->properties : NULL;
|
||||
test = 0;
|
||||
}
|
||||
@ -859,7 +859,7 @@ static void sxe_prop_dim_delete(zend_object *object, zval *member, bool elements
|
||||
if (elements) {
|
||||
if (Z_TYPE_P(member) == IS_LONG) {
|
||||
if (sxe->iter.type == SXE_ITER_CHILD) {
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
}
|
||||
node = sxe_get_element_by_offset(sxe, Z_LVAL_P(member), node, NULL);
|
||||
if (node) {
|
||||
@ -992,7 +992,7 @@ static int sxe_prop_is_empty(zend_object *object) /* {{{ */
|
||||
}
|
||||
|
||||
if (sxe->iter.type == SXE_ITER_ELEMENT) {
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
}
|
||||
if (!node || node->type != XML_ENTITY_DECL) {
|
||||
attr = node ? (xmlAttrPtr)node->properties : NULL;
|
||||
@ -1006,7 +1006,7 @@ static int sxe_prop_is_empty(zend_object *object) /* {{{ */
|
||||
}
|
||||
|
||||
GET_NODE(sxe, node);
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
is_empty = 1;
|
||||
ZVAL_UNDEF(&iter_data);
|
||||
if (node && sxe->iter.type != SXE_ITER_ATTRLIST) {
|
||||
@ -1101,7 +1101,7 @@ static HashTable *sxe_get_prop_hash(zend_object *object, int is_debug) /* {{{ */
|
||||
}
|
||||
if (is_debug || sxe->iter.type != SXE_ITER_CHILD) {
|
||||
if (sxe->iter.type == SXE_ITER_ELEMENT) {
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
}
|
||||
if (!node || node->type != XML_ENTITY_DECL) {
|
||||
attr = node ? (xmlAttrPtr)node->properties : NULL;
|
||||
@ -1123,7 +1123,7 @@ 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);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
|
||||
if (node && sxe->iter.type != SXE_ITER_ATTRLIST) {
|
||||
if (node->type == XML_ATTRIBUTE_NODE) {
|
||||
@ -1282,7 +1282,7 @@ PHP_METHOD(SimpleXMLElement, xpath)
|
||||
}
|
||||
|
||||
GET_NODE(sxe, nodeptr);
|
||||
nodeptr = php_sxe_get_first_node_non_destructive(sxe, nodeptr);
|
||||
nodeptr = php_sxe_get_first_node(sxe, nodeptr);
|
||||
if (!nodeptr) {
|
||||
return;
|
||||
}
|
||||
@ -1391,7 +1391,7 @@ PHP_METHOD(SimpleXMLElement, asXML)
|
||||
|
||||
sxe = Z_SXEOBJ_P(ZEND_THIS);
|
||||
GET_NODE(sxe, node);
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
|
||||
if (!node) {
|
||||
RETURN_FALSE;
|
||||
@ -1514,7 +1514,7 @@ PHP_METHOD(SimpleXMLElement, getNamespaces)
|
||||
|
||||
sxe = Z_SXEOBJ_P(ZEND_THIS);
|
||||
GET_NODE(sxe, node);
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
|
||||
if (node) {
|
||||
if (node->type == XML_ELEMENT_NODE) {
|
||||
@ -1599,7 +1599,7 @@ PHP_METHOD(SimpleXMLElement, children)
|
||||
}
|
||||
|
||||
GET_NODE(sxe, node);
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
@ -1623,7 +1623,7 @@ PHP_METHOD(SimpleXMLElement, getName)
|
||||
sxe = Z_SXEOBJ_P(ZEND_THIS);
|
||||
|
||||
GET_NODE(sxe, node);
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
if (node) {
|
||||
namelen = xmlStrlen(node->name);
|
||||
RETURN_STRINGL((char*)node->name, namelen);
|
||||
@ -1648,7 +1648,7 @@ PHP_METHOD(SimpleXMLElement, attributes)
|
||||
|
||||
sxe = Z_SXEOBJ_P(ZEND_THIS);
|
||||
GET_NODE(sxe, node);
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
@ -1689,7 +1689,7 @@ PHP_METHOD(SimpleXMLElement, addChild)
|
||||
return;
|
||||
}
|
||||
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
|
||||
if (node == NULL) {
|
||||
php_error_docref(NULL, E_WARNING, "Cannot add child. Parent is not a permanent member of the XML tree");
|
||||
@ -1749,7 +1749,7 @@ PHP_METHOD(SimpleXMLElement, addAttribute)
|
||||
sxe = Z_SXEOBJ_P(ZEND_THIS);
|
||||
GET_NODE(sxe, node);
|
||||
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
node = php_sxe_get_first_node(sxe, node);
|
||||
|
||||
if (node && node->type != XML_ELEMENT_NODE) {
|
||||
node = node->parent;
|
||||
@ -1842,7 +1842,7 @@ static int sxe_object_cast_ex(zend_object *readobj, zval *writeobj, int type)
|
||||
sxe = php_sxe_fetch_object(readobj);
|
||||
|
||||
if (type == _IS_BOOL) {
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, NULL);
|
||||
node = php_sxe_get_first_node(sxe, NULL);
|
||||
if (node) {
|
||||
ZVAL_TRUE(writeobj);
|
||||
} else {
|
||||
@ -1852,7 +1852,7 @@ static int sxe_object_cast_ex(zend_object *readobj, zval *writeobj, int type)
|
||||
}
|
||||
|
||||
if (sxe->iter.type != SXE_ITER_NONE) {
|
||||
node = php_sxe_get_first_node_non_destructive(sxe, NULL);
|
||||
node = php_sxe_get_first_node(sxe, NULL);
|
||||
if (node) {
|
||||
contents = xmlNodeListGetString((xmlDocPtr) sxe->document->ptr, node->children, 1);
|
||||
}
|
||||
@ -2600,7 +2600,7 @@ void *simplexml_export_node(zval *object) /* {{{ */
|
||||
|
||||
sxe = Z_SXEOBJ_P(object);
|
||||
GET_NODE(sxe, node);
|
||||
return php_sxe_get_first_node_non_destructive(sxe, node);
|
||||
return php_sxe_get_first_node(sxe, node);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
@ -1,92 +0,0 @@
|
||||
--TEST--
|
||||
Bug #55098 (SimpleXML iteration produces infinite loop)
|
||||
--EXTENSIONS--
|
||||
simplexml
|
||||
--FILE--
|
||||
<?php
|
||||
$xmlString = "<root><a><b>1</b><b>2</b><b>3</b></a></root>";
|
||||
$xml = simplexml_load_string($xmlString);
|
||||
|
||||
$nodes = $xml->a->b;
|
||||
|
||||
function test($nodes, $name, $callable) {
|
||||
echo "--- $name ---\n";
|
||||
foreach ($nodes as $nodeData) {
|
||||
echo "nodeData: " . $nodeData . "\n";
|
||||
$callable($nodes);
|
||||
}
|
||||
}
|
||||
|
||||
test($nodes, "asXml", fn ($n) => $n->asXml());
|
||||
test($nodes, "attributes", fn ($n) => $n->attributes());
|
||||
test($nodes, "children", fn ($n) => $n->children());
|
||||
test($nodes, "getNamespaces", fn ($n) => $n->getNamespaces());
|
||||
test($nodes, "xpath", fn ($n) => $n->xpath("/root/a/b"));
|
||||
test($nodes, "var_dump", fn ($n) => var_dump($n));
|
||||
test($nodes, "manipulation combined with querying", function ($n) {
|
||||
$n->addAttribute("attr", "value");
|
||||
(bool) $n["attr"];
|
||||
$n->addChild("child", "value");
|
||||
$n->outer[]->inner = "foo";
|
||||
(bool) $n->outer;
|
||||
(bool) $n;
|
||||
isset($n->outer);
|
||||
isset($n["attr"]);
|
||||
unset($n->outer);
|
||||
unset($n["attr"]);
|
||||
unset($n->child);
|
||||
});
|
||||
?>
|
||||
--EXPECT--
|
||||
--- asXml ---
|
||||
nodeData: 1
|
||||
nodeData: 2
|
||||
nodeData: 3
|
||||
--- attributes ---
|
||||
nodeData: 1
|
||||
nodeData: 2
|
||||
nodeData: 3
|
||||
--- children ---
|
||||
nodeData: 1
|
||||
nodeData: 2
|
||||
nodeData: 3
|
||||
--- getNamespaces ---
|
||||
nodeData: 1
|
||||
nodeData: 2
|
||||
nodeData: 3
|
||||
--- xpath ---
|
||||
nodeData: 1
|
||||
nodeData: 2
|
||||
nodeData: 3
|
||||
--- var_dump ---
|
||||
nodeData: 1
|
||||
object(SimpleXMLElement)#3 (3) {
|
||||
[0]=>
|
||||
string(1) "1"
|
||||
[1]=>
|
||||
string(1) "2"
|
||||
[2]=>
|
||||
string(1) "3"
|
||||
}
|
||||
nodeData: 2
|
||||
object(SimpleXMLElement)#3 (3) {
|
||||
[0]=>
|
||||
string(1) "1"
|
||||
[1]=>
|
||||
string(1) "2"
|
||||
[2]=>
|
||||
string(1) "3"
|
||||
}
|
||||
nodeData: 3
|
||||
object(SimpleXMLElement)#3 (3) {
|
||||
[0]=>
|
||||
string(1) "1"
|
||||
[1]=>
|
||||
string(1) "2"
|
||||
[2]=>
|
||||
string(1) "3"
|
||||
}
|
||||
--- manipulation combined with querying ---
|
||||
nodeData: 1
|
||||
nodeData: 2
|
||||
nodeData: 3
|
@ -41,7 +41,7 @@ foreach ($a2->b->c->children() as $key => $value) {
|
||||
var_dump($value);
|
||||
}?>
|
||||
--EXPECT--
|
||||
object(A)#4 (2) {
|
||||
object(A)#2 (2) {
|
||||
["@attributes"]=>
|
||||
array(1) {
|
||||
["attr"]=>
|
||||
@ -50,7 +50,7 @@ object(A)#4 (2) {
|
||||
[0]=>
|
||||
string(10) "Some Value"
|
||||
}
|
||||
object(A)#6 (2) {
|
||||
object(A)#3 (2) {
|
||||
["@attributes"]=>
|
||||
array(1) {
|
||||
["attr"]=>
|
||||
|
@ -1,37 +0,0 @@
|
||||
--TEST--
|
||||
GH-12192 (SimpleXML infinite loop when getName() is called within foreach)
|
||||
--EXTENSIONS--
|
||||
simplexml
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$xml = "<root><a>1</a><a>2</a></root>";
|
||||
$xml = simplexml_load_string($xml);
|
||||
|
||||
$a = $xml->a;
|
||||
|
||||
foreach ($a as $test) {
|
||||
echo "Iteration\n";
|
||||
var_dump($a->key());
|
||||
var_dump($a->getName());
|
||||
var_dump((string) $test);
|
||||
}
|
||||
|
||||
var_dump($a);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Iteration
|
||||
string(1) "a"
|
||||
string(1) "a"
|
||||
string(1) "1"
|
||||
Iteration
|
||||
string(1) "a"
|
||||
string(1) "a"
|
||||
string(1) "2"
|
||||
object(SimpleXMLElement)#2 (2) {
|
||||
[0]=>
|
||||
string(1) "1"
|
||||
[1]=>
|
||||
string(1) "2"
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
--TEST--
|
||||
GH-12208 (SimpleXML infinite loop when a cast is used inside a foreach)
|
||||
--EXTENSIONS--
|
||||
simplexml
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$xml = "<root><a>1</a><a>2</a></root>";
|
||||
$xml = simplexml_load_string($xml);
|
||||
|
||||
$a = $xml->a;
|
||||
|
||||
foreach ($a as $test) {
|
||||
var_dump((string) $a->current());
|
||||
var_dump((string) $a);
|
||||
var_dump((bool) $a);
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
string(1) "1"
|
||||
string(1) "1"
|
||||
bool(true)
|
||||
string(1) "2"
|
||||
string(1) "1"
|
||||
bool(true)
|
Loading…
Reference in New Issue
Block a user