SPLのコンテナクラスで循環参照するとGCで回収されない

Zend Framework 2 を弄っていて SplPriorityQueue に気になる不具合があることがわかりました。

下記の Bug の通りですが、SPL の幾つかのコンテナクラスで循環参照するとGCで回収されません。

 - [PHP :: Bug #53803 :: circular reference between SPL classes not collected by gc_collect_cycles()](https://bugs.php.net/bug.php?id=53803 "PHP :: Bug #53803 :: circular reference between SPL classes not collected by gc_collect_cycles()")

次のようなコードで確認できます。

```php:SplCircularReferenceTest.php
<?php
class TestClass
{
    public function __destruct()
    {
        SplCircularReferenceTest::$flg = true;
    }
}

class SplCircularReferenceTest extends \PHPUnit_Framework_TestCase
{
    public static $flg = false;

    function testArray()
    {
        self::$flg = false;

        call_user_func(function () {
            $aa = new TestClass();
            $bb = array($aa);
            $aa->bb = $bb;
        });

        gc_collect_cycles();
        $this->assertTrue(self::$flg);
    }

    function testObject()
    {
        self::$flg = false;

        call_user_func(function () {
            $aa = new TestClass();
            $bb = new \stdClass();
            $bb->aa = $aa;
            $aa->bb = $bb;
        });

        gc_collect_cycles();
        $this->assertTrue(self::$flg);
    }

    function testSplFixedArray()
    {
        self::$flg = false;

        call_user_func(function () {
            $aa = new TestClass();
            $bb = new \SplFixedArray(1);
            $bb[0] = $aa;
            $aa->bb = $bb;
        });

        gc_collect_cycles();
        $this->assertTrue(self::$flg);
    }

    function testSplObjectStorage()
    {
        self::$flg = false;

        call_user_func(function () {
            $aa = new TestClass();
            $bb = new \SplObjectStorage();
            $bb->attach($aa);
            $aa->bb = $bb;
        });

        gc_collect_cycles();
        $this->assertTrue(self::$flg);
    }

    function testArrayObject()
    {
        self::$flg = false;

        call_user_func(function () {
            $aa = new TestClass();
            $bb = new \ArrayObject([$aa]);
            $aa->bb = $bb;
        });

        gc_collect_cycles();
        //$this->assertTrue(self::$flg);
    }

    function testSplDoublyLinkedList()
    {
        self::$flg = false;

        call_user_func(function () {
                $aa = new TestClass();
                $bb = new \SplDoublyLinkedList();
                $bb->push($aa);
                $aa->bb = $bb;
            });

        gc_collect_cycles();
        //$this->assertTrue(self::$flg);
    }

    function testSplMaxHeap()
    {
        self::$flg = false;

        call_user_func(function () {
                $aa = new TestClass();
                $bb = new \SplMaxHeap();
                $bb->insert($aa);
                $aa->bb = $bb;
            });

        gc_collect_cycles();
        //$this->assertTrue(self::$flg);
    }

    function testSplPriorityQueue()
    {
        self::$flg = false;

        call_user_func(function () {
            $aa = new TestClass();
            $bb = new \SplPriorityQueue();
            $bb->insert($aa, 1);
            $aa->bb = $bb;
        });

        gc_collect_cycles();
        //$this->assertTrue(self::$flg);
    }
}
```

SplCircularReferenceTest::$flg は TestClass のデストラクタで true になることを期待しています。

各テストメソッド内で TestClass と配列やオブジェクトのインスタンスが循環参照していますが、gc_collect_cycles() を明示的に呼び出しているので解放されるはずです。

が、ArrayObject を含む幾つかのSPLクラスでオブジェクトが解放されませんでした。

SplFixedArray と SplObjectStorage は大丈夫ですが、その他のSPLクラスは要注意です。