Skip to content

Commit 6ec732c

Browse files
committed
Make iterator_to_array() accept all iterables
While calling this function on an array either is a no-op or is equivalent to `\array_values()` if the `$preserve_keys` flag is `false`, it is also unnecessarily limiting for it to reject proper arrays. By allowing it to take the full `iterable` family, it is more easily possible to write a function that operates on an arbitrary `iterable` and internally uses array-specific functionality, such as `array_filter` or `array_map`: function test(iterable $foo) { $foo = iterator_to_array($foo); return array_map(strlen(...), $foo); } With this change it behaves similarly to `Array.from()` in JavaScript.
1 parent 20a9027 commit 6ec732c

File tree

4 files changed

+40
-9
lines changed

4 files changed

+40
-9
lines changed

ext/spl/php_spl.stub.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,4 @@ function iterator_apply(Traversable $iterator, callable $callback, ?array $args
5151
function iterator_count(Traversable $iterator): int {}
5252

5353
/** @refcount 1 */
54-
function iterator_to_array(Traversable $iterator, bool $preserve_keys = true): array {}
54+
function iterator_to_array(iterable $iterator, bool $preserve_keys = true): array {}

ext/spl/php_spl_arginfo.h

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/spl/spl_iterators.c

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3171,12 +3171,43 @@ PHP_FUNCTION(iterator_to_array)
31713171
zval *obj;
31723172
bool use_keys = 1;
31733173

3174-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &obj, zend_ce_traversable, &use_keys) == FAILURE) {
3175-
RETURN_THROWS();
3176-
}
3174+
ZEND_PARSE_PARAMETERS_START(1, 2)
3175+
Z_PARAM_ITERABLE(obj)
3176+
Z_PARAM_OPTIONAL
3177+
Z_PARAM_BOOL(use_keys)
3178+
ZEND_PARSE_PARAMETERS_END();
3179+
3180+
if (Z_TYPE_P(obj) == IS_ARRAY) {
3181+
if (use_keys) {
3182+
RETURN_COPY(obj);
3183+
}
3184+
else {
3185+
zend_array *arrval;
3186+
zend_long arrlen;
3187+
zval *entry;
3188+
3189+
arrval = Z_ARRVAL_P(obj);
3190+
3191+
arrlen = zend_hash_num_elements(arrval);
31773192

3178-
array_init(return_value);
3179-
spl_iterator_apply(obj, use_keys ? spl_iterator_to_array_apply : spl_iterator_to_values_apply, (void*)return_value);
3193+
array_init_size(return_value, arrlen);
3194+
zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
3195+
3196+
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3197+
ZEND_HASH_FOREACH_VAL(arrval, entry) {
3198+
if (UNEXPECTED(Z_ISREF_P(entry) && Z_REFCOUNT_P(entry) == 1)) {
3199+
entry = Z_REFVAL_P(entry);
3200+
}
3201+
Z_TRY_ADDREF_P(entry);
3202+
ZEND_HASH_FILL_ADD(entry);
3203+
} ZEND_HASH_FOREACH_END();
3204+
} ZEND_HASH_FILL_END();
3205+
}
3206+
}
3207+
else {
3208+
array_init(return_value);
3209+
spl_iterator_apply(obj, use_keys ? spl_iterator_to_array_apply : spl_iterator_to_values_apply, (void*)return_value);
3210+
}
31803211
} /* }}} */
31813212

31823213
static int spl_iterator_count_apply(zend_object_iterator *iter, void *puser) /* {{{ */

ext/spl/tests/iterator_to_array.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ iterator_to_array('test','test');
1313

1414
?>
1515
--EXPECTF--
16-
Fatal error: Uncaught TypeError: iterator_to_array(): Argument #1 ($iterator) must be of type Traversable, string given in %s:%d
16+
Fatal error: Uncaught TypeError: iterator_to_array(): Argument #1 ($iterator) must be of type iterable, string given in %s:%d
1717
Stack trace:
1818
#0 %s(%d): iterator_to_array('test', 'test')
1919
#1 {main}

0 commit comments

Comments
 (0)