]> granicus.if.org Git - php/commitdiff
Fix bug #65387
authorNikita Popov <nikita.ppv@gmail.com>
Thu, 1 Oct 2020 14:12:56 +0000 (16:12 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Thu, 1 Oct 2020 14:12:56 +0000 (16:12 +0200)
Add GC support to dual_it. This is still missing AppendIterator
support.

NEWS
ext/spl/spl_iterators.c
ext/spl/tests/bug65387.phpt [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index b5f816a055e57c0d7a864a9ae7e9e959155e641a..57892d8bb6dbfeecce27a4f9844344e92983bc00 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,9 @@ PHP                                                                        NEWS
 |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
 ?? ??? ????, PHP 8.0.0rc2
 
+- SPL.
+  . Fixed bug #65387 (Circular references in SPL iterators are not garbage
+    collected). (Nikita)
 
 01 Oct 2020, PHP 8.0.0rc1
 
index 4735a257a89e7e6090c973d476150372df41caf9..c73134e9ae06e0da0930ed96e844df02e38bb8d3 100644 (file)
@@ -2119,6 +2119,52 @@ static void spl_dual_it_free_storage(zend_object *_object)
 }
 /* }}} */
 
+static HashTable *spl_dual_it_get_gc(zend_object *obj, zval **table, int *n)
+{
+       spl_dual_it_object *object = spl_dual_it_from_obj(obj);
+       zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
+
+       if (!Z_ISUNDEF(object->inner.zobject)) {
+               zend_get_gc_buffer_add_zval(gc_buffer, &object->inner.zobject);
+       }
+
+       switch (object->dit_type) {
+               case DIT_Unknown:
+               case DIT_Default:
+               case DIT_IteratorIterator:
+               case DIT_NoRewindIterator:
+               case DIT_InfiniteIterator:
+               case DIT_LimitIterator:
+               case DIT_RegexIterator:
+               case DIT_RecursiveRegexIterator:
+                       /* Nothing to do */
+                       break;
+               case DIT_AppendIterator:
+                       // TODO
+                       /*zend_get_gc_buffer_add_obj(gc_buffer, &object->u.append.iterator->std);
+                       if (Z_TYPE(object->u.append.zarrayit) != IS_UNDEF) {
+                               zend_get_gc_buffer_add_zval(gc_buffer, &object->u.append.zarrayit);
+                       }*/
+                       break;
+               case DIT_CachingIterator:
+               case DIT_RecursiveCachingIterator:
+                       zend_get_gc_buffer_add_zval(gc_buffer, &object->u.caching.zcache);
+                       break;
+               case DIT_CallbackFilterIterator:
+               case DIT_RecursiveCallbackFilterIterator:
+                       if (object->u.cbfilter) {
+                               zend_get_gc_buffer_add_zval(gc_buffer, &object->u.cbfilter->fci.function_name);
+                               if (object->u.cbfilter->fci.object) {
+                                       zend_get_gc_buffer_add_obj(gc_buffer, object->u.cbfilter->fci.object);
+                               }
+                       }
+                       break;
+       }
+
+       zend_get_gc_buffer_use(gc_buffer, table, n);
+       return zend_std_get_properties(obj);
+}
+
 /* {{{ spl_dual_it_new */
 static zend_object *spl_dual_it_new(zend_class_entry *class_type)
 {
@@ -3191,6 +3237,7 @@ PHP_MINIT_FUNCTION(spl_iterators)
        spl_handlers_dual_it.clone_obj = NULL;
        spl_handlers_dual_it.dtor_obj = spl_dual_it_dtor;
        spl_handlers_dual_it.free_obj = spl_dual_it_free_storage;
+       spl_handlers_dual_it.get_gc = spl_dual_it_get_gc;
 
        spl_ce_RecursiveIteratorIterator->get_iterator = spl_recursive_it_get_iterator;
 
diff --git a/ext/spl/tests/bug65387.phpt b/ext/spl/tests/bug65387.phpt
new file mode 100644 (file)
index 0000000..7567825
--- /dev/null
@@ -0,0 +1,54 @@
+--TEST--
+Bug #67387: Circular references in SPL iterators are not garbage collected
+--FILE--
+<?php
+
+$it = new ArrayIterator([1, 2, 3]);
+// Inner.
+$it[] = $it;
+
+// Callback
+$it2 = new CallbackFilterIterator($it, function($elem) use(&$it2) {
+    return true;
+});
+
+// Callback object
+new class {
+    public function __construct() {
+        $it = new ArrayIterator([1, 2, 3]);
+        $this->it = new CallbackFilterIterator($it, function($elem) {
+            return true;
+        });
+    }
+};
+
+// Recursive callback
+$it = new RecursiveArrayIterator([1, 2, 3]);
+$it2 = new RecursiveCallbackFilterIterator($it, function($elem) use(&$it2) {
+    return true;
+});
+
+// Cache
+$it = new ArrayIterator();
+$it2 = new CachingIterator($it, CachingIterator::FULL_CACHE);
+$it2[] = $it2;
+$it2->next();
+
+// Recursive cache
+$it = new RecursiveArrayIterator();
+$it2 = new RecursiveCachingIterator($it, CachingIterator::FULL_CACHE);
+$it2[] = $it2;
+$it2->next();
+
+// Append
+/* TODO
+$it = new ArrayIterator();
+$it2 = new AppendIterator();
+$it[] = $it2;
+$it2->append($it);
+ */
+
+?>
+===DONE===
+--EXPECT--
+===DONE===