Skip to content

Commit d6509bf

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 d6509bf

File tree

5 files changed

+82
-9
lines changed

5 files changed

+82
-9
lines changed

ext/spl/php_spl.stub.php

+1-1
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

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/spl/spl_iterators.c

+36-5
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

+1-1
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}
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
--TEST--
2+
SPL: iterator_to_array() supports arrays.
3+
--FILE--
4+
<?php
5+
6+
var_dump(iterator_to_array([]));
7+
var_dump(iterator_to_array([1]));
8+
var_dump(iterator_to_array(['a' => 1, 'b' => 2, 5 => 3]));
9+
var_dump(iterator_to_array([], false));
10+
var_dump(iterator_to_array([1], false));
11+
var_dump(iterator_to_array(['a' => 1, 'b' => 2, 5 => 3], false));
12+
13+
?>
14+
--EXPECT--
15+
array(0) {
16+
}
17+
array(1) {
18+
[0]=>
19+
int(1)
20+
}
21+
array(3) {
22+
["a"]=>
23+
int(1)
24+
["b"]=>
25+
int(2)
26+
[5]=>
27+
int(3)
28+
}
29+
array(0) {
30+
}
31+
array(1) {
32+
[0]=>
33+
int(1)
34+
}
35+
array(3) {
36+
[0]=>
37+
int(1)
38+
[1]=>
39+
int(2)
40+
[2]=>
41+
int(3)
42+
}

0 commit comments

Comments
 (0)