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)