From 203014b3942350f01d8f3c72a7783b952015114b Mon Sep 17 00:00:00 2001 From: HypeMC Date: Thu, 17 Sep 2026 01:03:22 +0200 Subject: [PATCH] Return `InternalIterator` from collection `getIterator()` methods --- README.md | 2 ++ src/php/classes/php_heap_ce.c | 2 +- src/php/classes/php_map_ce.c | 2 +- src/php/classes/php_seq_ce.c | 2 +- src/php/classes/php_set_ce.c | 2 +- tests/Heap/getIterator.phpt | 43 +++++++++++++++++++++++++++++++++++ tests/Map/getIterator.phpt | 43 +++++++++++++++++++++++++++++++++++ tests/Seq/getIterator.phpt | 43 +++++++++++++++++++++++++++++++++++ tests/Set/getIterator.phpt | 43 +++++++++++++++++++++++++++++++++++ 9 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 tests/Heap/getIterator.phpt create mode 100644 tests/Map/getIterator.phpt create mode 100644 tests/Seq/getIterator.phpt create mode 100644 tests/Set/getIterator.phpt diff --git a/README.md b/README.md index 0856f6e..afcfd44 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ Documentation is available on [php.net](https://www.php.net/manual/en/book.ds.ph - **Ds\Pair** -- readonly key-value pair - **Ds\Key** -- interface for custom key equality (replaces Hashable) +`getIterator()` on `Seq`, `Map`, `Set`, and `Heap` returns an `InternalIterator` supporting the standard `Iterator` methods. + You should also include the [polyfill](https://github.com/php-ds/polyfill) in your project for IDE integration. ## Installation diff --git a/src/php/classes/php_heap_ce.c b/src/php/classes/php_heap_ce.c index 2b83fcb..1c6a819 100644 --- a/src/php/classes/php_heap_ce.c +++ b/src/php/classes/php_heap_ce.c @@ -163,7 +163,7 @@ METHOD(__unserialize) METHOD(getIterator) { PARSE_NONE; - ZVAL_COPY(return_value, getThis()); + zend_create_internal_iterator_zval(return_value, ZEND_THIS); } void php_ds_register_heap() diff --git a/src/php/classes/php_map_ce.c b/src/php/classes/php_map_ce.c index d025a0b..7a2d085 100644 --- a/src/php/classes/php_map_ce.c +++ b/src/php/classes/php_map_ce.c @@ -379,7 +379,7 @@ METHOD(__unserialize) METHOD(getIterator) { PARSE_NONE; - ZVAL_COPY(return_value, getThis()); + zend_create_internal_iterator_zval(return_value, ZEND_THIS); } METHOD(offsetExists) diff --git a/src/php/classes/php_seq_ce.c b/src/php/classes/php_seq_ce.c index 9d479ad..ef36c46 100644 --- a/src/php/classes/php_seq_ce.c +++ b/src/php/classes/php_seq_ce.c @@ -300,7 +300,7 @@ METHOD(__unserialize) METHOD(getIterator) { PARSE_NONE; - ZVAL_COPY(return_value, getThis()); + zend_create_internal_iterator_zval(return_value, ZEND_THIS); } METHOD(offsetExists) diff --git a/src/php/classes/php_set_ce.c b/src/php/classes/php_set_ce.c index 27359be..9760e17 100644 --- a/src/php/classes/php_set_ce.c +++ b/src/php/classes/php_set_ce.c @@ -260,7 +260,7 @@ METHOD(__unserialize) METHOD(getIterator) { PARSE_NONE; - ZVAL_COPY(return_value, getThis()); + zend_create_internal_iterator_zval(return_value, ZEND_THIS); } diff --git a/tests/Heap/getIterator.phpt b/tests/Heap/getIterator.phpt new file mode 100644 index 0000000..69fdee6 --- /dev/null +++ b/tests/Heap/getIterator.phpt @@ -0,0 +1,43 @@ +--TEST-- +Heap: getIterator returns an independent InternalIterator +--FILE-- +getIterator(); +var_dump($iterator instanceof InternalIterator); +var_dump($iterator instanceof Iterator); +var_dump($iterator !== $collection); + +$other = $collection->getIterator(); +var_dump($iterator !== $other); +$iterator->rewind(); +$other->rewind(); +$iterator->next(); +var_dump($other->key() === array_key_first($expected)); +var_dump($other->current() === reset($expected)); + +$result = []; +while ($other->valid()) { + $result[$other->key()] = $other->current(); + $other->next(); +} +var_dump($result === $expected); +var_dump(iterator_to_array($collection->getIterator()) === $expected); +var_dump($collection->count() === 3); + +$empty = (new Ds\Heap())->getIterator(); +$empty->rewind(); +var_dump($empty->valid()); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(false) diff --git a/tests/Map/getIterator.phpt b/tests/Map/getIterator.phpt new file mode 100644 index 0000000..d896cec --- /dev/null +++ b/tests/Map/getIterator.phpt @@ -0,0 +1,43 @@ +--TEST-- +Map: getIterator returns an independent InternalIterator +--FILE-- + 10, 'b' => 20, 'c' => 30]); +$expected = iterator_to_array($collection); +$iterator = $collection->getIterator(); +var_dump($iterator instanceof InternalIterator); +var_dump($iterator instanceof Iterator); +var_dump($iterator !== $collection); + +$other = $collection->getIterator(); +var_dump($iterator !== $other); +$iterator->rewind(); +$other->rewind(); +$iterator->next(); +var_dump($other->key() === array_key_first($expected)); +var_dump($other->current() === reset($expected)); + +$result = []; +while ($other->valid()) { + $result[$other->key()] = $other->current(); + $other->next(); +} +var_dump($result === $expected); +var_dump(iterator_to_array($collection->getIterator()) === $expected); +var_dump($collection->count() === 3); + +$empty = (new Ds\Map())->getIterator(); +$empty->rewind(); +var_dump($empty->valid()); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(false) diff --git a/tests/Seq/getIterator.phpt b/tests/Seq/getIterator.phpt new file mode 100644 index 0000000..d94b65a --- /dev/null +++ b/tests/Seq/getIterator.phpt @@ -0,0 +1,43 @@ +--TEST-- +Seq: getIterator returns an independent InternalIterator +--FILE-- +getIterator(); +var_dump($iterator instanceof InternalIterator); +var_dump($iterator instanceof Iterator); +var_dump($iterator !== $collection); + +$other = $collection->getIterator(); +var_dump($iterator !== $other); +$iterator->rewind(); +$other->rewind(); +$iterator->next(); +var_dump($other->key() === array_key_first($expected)); +var_dump($other->current() === reset($expected)); + +$result = []; +while ($other->valid()) { + $result[$other->key()] = $other->current(); + $other->next(); +} +var_dump($result === $expected); +var_dump(iterator_to_array($collection->getIterator()) === $expected); +var_dump($collection->count() === 3); + +$empty = (new Ds\Seq())->getIterator(); +$empty->rewind(); +var_dump($empty->valid()); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(false) diff --git a/tests/Set/getIterator.phpt b/tests/Set/getIterator.phpt new file mode 100644 index 0000000..17ab200 --- /dev/null +++ b/tests/Set/getIterator.phpt @@ -0,0 +1,43 @@ +--TEST-- +Set: getIterator returns an independent InternalIterator +--FILE-- +getIterator(); +var_dump($iterator instanceof InternalIterator); +var_dump($iterator instanceof Iterator); +var_dump($iterator !== $collection); + +$other = $collection->getIterator(); +var_dump($iterator !== $other); +$iterator->rewind(); +$other->rewind(); +$iterator->next(); +var_dump($other->key() === array_key_first($expected)); +var_dump($other->current() === reset($expected)); + +$result = []; +while ($other->valid()) { + $result[$other->key()] = $other->current(); + $other->next(); +} +var_dump($result === $expected); +var_dump(iterator_to_array($collection->getIterator()) === $expected); +var_dump($collection->count() === 3); + +$empty = (new Ds\Set())->getIterator(); +$empty->rewind(); +var_dump($empty->valid()); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(false)