Skip to content

Commit fb70460

Browse files
committed
Fix GH-7958: Nested CallbackFilterIterator is leaking memory
We implement `zend_object_iterator_funcs.get_gc` for user iterators to avoid the memory leak. Closes GH-8107.
1 parent 19063a8 commit fb70460

File tree

4 files changed

+54
-1
lines changed

4 files changed

+54
-1
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ PHP NEWS
77
. Fixed bug GH-8059 arginfo not regenerated for extension. (Remi)
88
. Fixed bug GH-8083 Segfault when dumping uncalled fake closure with static
99
variables. (ilutov)
10+
. Fixed bug GH-7958 (Nested CallbackFilterIterator is leaking memory). (cmb)
1011

1112
- GD:
1213
. Fixed libpng warning when loading interlaced images. (Brett)

Zend/tests/gh7958.phpt

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
--TEST--
2+
GH-7958 (Nested CallbackFilterIterator is leaking memory)
3+
--FILE--
4+
<?php
5+
class Action
6+
{
7+
public \Iterator $iterator;
8+
9+
public function __construct(array $data)
10+
{
11+
$this->iterator = new ArrayIterator($data);
12+
echo '-- c ' . spl_object_id($this) . "\n";
13+
}
14+
15+
public function __destruct()
16+
{
17+
echo '-- d ' . spl_object_id($this) . "\n";
18+
}
19+
20+
public function filter()
21+
{
22+
$this->iterator = new \CallbackFilterIterator($this->iterator, fn() => true);
23+
$this->iterator->rewind();
24+
}
25+
}
26+
27+
$action = new Action(['a', 'b']);
28+
$action->filter();
29+
$action->filter();
30+
print_r(iterator_to_array($action->iterator));
31+
$action = null;
32+
gc_collect_cycles();
33+
echo "==DONE==\n";
34+
?>
35+
--EXPECT--
36+
-- c 1
37+
Array
38+
(
39+
[0] => a
40+
[1] => b
41+
)
42+
-- d 1
43+
==DONE==

Zend/zend_interfaces.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,14 @@ ZEND_API void zend_user_it_rewind(zend_object_iterator *_iter)
182182
}
183183
/* }}} */
184184

185+
ZEND_API HashTable *zend_user_it_get_gc(zend_object_iterator *_iter, zval **table, int *n)
186+
{
187+
zend_user_iterator *iter = (zend_user_iterator*)_iter;
188+
*table = &iter->it.data;
189+
*n = 1;
190+
return NULL;
191+
}
192+
185193
static const zend_object_iterator_funcs zend_interface_iterator_funcs_iterator = {
186194
zend_user_it_dtor,
187195
zend_user_it_valid,
@@ -190,7 +198,7 @@ static const zend_object_iterator_funcs zend_interface_iterator_funcs_iterator =
190198
zend_user_it_move_forward,
191199
zend_user_it_rewind,
192200
zend_user_it_invalidate_current,
193-
NULL, /* get_gc */
201+
zend_user_it_get_gc,
194202
};
195203

196204
/* {{{ zend_user_it_get_iterator */

Zend/zend_interfaces.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ ZEND_API void zend_user_it_get_current_key(zend_object_iterator *_iter, zval *ke
5555
ZEND_API zval *zend_user_it_get_current_data(zend_object_iterator *_iter);
5656
ZEND_API void zend_user_it_move_forward(zend_object_iterator *_iter);
5757
ZEND_API void zend_user_it_invalidate_current(zend_object_iterator *_iter);
58+
ZEND_API HashTable *zend_user_it_get_gc(zend_object_iterator *_iter, zval **table, int *n);
5859

5960
ZEND_API void zend_user_it_new_iterator(zend_class_entry *ce, zval *object, zval *iterator);
6061
ZEND_API zend_object_iterator *zend_user_it_get_new_iterator(zend_class_entry *ce, zval *object, int by_ref);

0 commit comments

Comments
 (0)