Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
18 changes: 15 additions & 3 deletions ext/simplexml/simplexml.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
/* }}} */
Expand Down Expand Up @@ -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,
};
/* }}} */

Expand Down Expand Up @@ -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;
Expand Down
67 changes: 67 additions & 0 deletions ext/simplexml/tests/sxe_gc.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
--TEST--
SimpleXMLElement reports its temporary and iterator values to the cycle collector
--EXTENSIONS--
simplexml
--FILE--
<?php

function collected(WeakReference $w): bool
{
gc_collect_cycles();
return $w->get() === null;
}

$sxe = new SimpleXMLElement('<root/>');
$w = WeakReference::create($sxe);
$r =& $sxe->missing;
$r = $sxe;
unset($r, $sxe);
echo "missing property reference: ";
var_dump(collected($w));

$sxe = new SimpleXMLElement('<root><a/></root>');
$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('<root><a><b/></a></root>');
$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('<root><a/></root>');
$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)
Loading