diff --git a/NEWS b/NEWS
index 4b34324de64b..a4cbe0255842 100644
--- a/NEWS
+++ b/NEWS
@@ -56,6 +56,10 @@ PHP NEWS
. Fixed a crash in readline_info() when completion_append_character
contains a non-ASCII byte. (Weilin Du)
+- SimpleXML:
+ . Fixed memory leak of cycles through SimpleXMLElement temporary property
+ references and iterators. (Ilia Alshanetsky)
+
- SOAP:
. Fixed use of uninitialized func in do_request() on OOM bailout.
(David Carlier)
diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c
index 9e5f293fe787..a8edf8c60a30 100644
--- a/ext/simplexml/simplexml.c
+++ b/ext/simplexml/simplexml.c
@@ -51,6 +51,7 @@ static zval *php_sxe_iterator_current_data(zend_object_iterator *iter);
static void php_sxe_iterator_current_key(zend_object_iterator *iter, zval *key);
static void php_sxe_iterator_move_forward(zend_object_iterator *iter);
static void php_sxe_iterator_rewind(zend_object_iterator *iter);
+static HashTable *php_sxe_iterator_get_gc(zend_object_iterator *iter, zval **table, int *n);
static zend_result sxe_object_cast_ex(zend_object *readobj, zval *writeobj, int type);
static void sxe_unlink_node(xmlNodePtr node)
@@ -1195,10 +1196,14 @@ static HashTable *sxe_get_prop_hash(zend_object *object, int is_debug) /* {{{ */
static HashTable *sxe_get_gc(zend_object *object, zval **table, int *n) /* {{{ */ {
php_sxe_object *sxe;
+ zend_get_gc_buffer *gc_buffer;
+
sxe = php_sxe_fetch_object(object);
+ gc_buffer = zend_get_gc_buffer_create();
+ zend_get_gc_buffer_add_zval(gc_buffer, &sxe->iter.data);
+ zend_get_gc_buffer_add_zval(gc_buffer, &sxe->tmp);
+ zend_get_gc_buffer_use(gc_buffer, table, n);
- *table = NULL;
- *n = 0;
return sxe->properties;
}
/* }}} */
@@ -2372,7 +2377,7 @@ static const zend_object_iterator_funcs php_sxe_iterator_funcs = { /* {{{ */
php_sxe_iterator_move_forward,
php_sxe_iterator_rewind,
NULL,
- NULL, /* get_gc */
+ php_sxe_iterator_get_gc,
};
/* }}} */
@@ -2579,6 +2584,13 @@ static void php_sxe_iterator_rewind(zend_object_iterator *iter) /* {{{ */
}
/* }}} */
+static HashTable *php_sxe_iterator_get_gc(zend_object_iterator *iter, zval **table, int *n)
+{
+ *table = &iter->data;
+ *n = 1;
+ return NULL;
+}
+
void *simplexml_export_node(zval *object) /* {{{ */
{
php_sxe_object *sxe;
diff --git a/ext/simplexml/tests/sxe_gc.phpt b/ext/simplexml/tests/sxe_gc.phpt
new file mode 100644
index 000000000000..778804518e23
--- /dev/null
+++ b/ext/simplexml/tests/sxe_gc.phpt
@@ -0,0 +1,67 @@
+--TEST--
+SimpleXMLElement reports its temporary and iterator values to the cycle collector
+--EXTENSIONS--
+simplexml
+--FILE--
+get() === null;
+}
+
+$sxe = new SimpleXMLElement('');
+$w = WeakReference::create($sxe);
+$r =& $sxe->missing;
+$r = $sxe;
+unset($r, $sxe);
+echo "missing property reference: ";
+var_dump(collected($w));
+
+$sxe = new SimpleXMLElement('');
+$w = WeakReference::create($sxe);
+$sxe->rewind();
+$current = $sxe->current();
+$r =& $current->missing;
+$r = $sxe;
+unset($r, $current, $sxe);
+echo "current element: ";
+var_dump(collected($w));
+
+$sxe = new SimpleXMLElement('');
+$w = WeakReference::create($sxe);
+$sxe->rewind();
+$child = $sxe->current();
+$child->rewind();
+$grandchild = $child->current();
+$r =& $grandchild->missing;
+$r = $sxe;
+unset($r, $grandchild, $child, $sxe);
+echo "nested current element: ";
+var_dump(collected($w));
+
+function iterate(SimpleXMLElement $sxe): Generator
+{
+ foreach ($sxe as $element) {
+ unset($sxe, $element);
+ yield;
+ }
+}
+
+$sxe = new SimpleXMLElement('');
+$w = WeakReference::create($sxe);
+$r =& $sxe->missing;
+$gen = iterate($sxe);
+$gen->current();
+$r = $gen;
+unset($r, $gen, $sxe);
+echo "foreach iterator in generator: ";
+var_dump(collected($w));
+
+?>
+--EXPECT--
+missing property reference: bool(true)
+current element: bool(true)
+nested current element: bool(true)
+foreach iterator in generator: bool(true)