diff --git a/src/collections_methods.c b/src/collections_methods.c index dcb5d60..13882cc 100644 --- a/src/collections_methods.c +++ b/src/collections_methods.c @@ -889,7 +889,62 @@ PHP_METHOD(Collection, binarySearchBy) PHP_METHOD(Collection, chunked) { - + zend_long size; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_LONG(size) + Z_PARAM_OPTIONAL + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); + if (size <= 0) { + ERR_BAD_SIZE(); + RETURN_NULL(); + } + zend_bool transform = EX_NUM_ARGS() > 1; + zend_array* current = THIS_COLLECTION; + zend_bool packed = HT_IS_PACKED(current); + INIT_FCI(&fci, 2); + ARRAY_NEW(chunked, 8); + uint32_t num_remaining = 0; + uint32_t num_chunks = 0; + zend_array* chunk = NULL; + ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) + if (num_remaining == 0) { + chunk = (zend_array*)emalloc(sizeof(zend_array)); + zend_hash_init(chunk, 8, NULL, ZVAL_PTR_DTOR, 0); + num_remaining = size; + } + Z_TRY_ADDREF(bucket->val); + if (bucket->key) { + zend_hash_add_new(chunk, bucket->key, &bucket->val); + } else if (packed) { + zend_hash_next_index_insert(chunk, &bucket->val); + } else { + zend_hash_index_add_new(chunk, bucket->h, &bucket->val); + } + if (--num_remaining == 0) { + ZVAL_ARR(¶ms[0], chunk); + if (transform) { + ZVAL_LONG(¶ms[1], num_chunks++); + zend_call_function(&fci, &fcc); + zend_hash_next_index_insert(chunked, &retval); + } else { + zend_hash_next_index_insert(chunked, ¶ms[0]); + } + } + ZEND_HASH_FOREACH_END(); + if (num_remaining) { + ZVAL_ARR(¶ms[0], chunk); + if (transform) { + ZVAL_LONG(¶ms[1], num_chunks++); + zend_call_function(&fci, &fcc); + zend_hash_next_index_insert(chunked, &retval); + } else { + zend_hash_next_index_insert(chunked, ¶ms[0]); + } + } + RETVAL_NEW_COLLECTION(chunked); } PHP_METHOD(Collection, containsAll) diff --git a/stubs/Collection.php b/stubs/Collection.php index 7ac6315..ea3020a 100644 --- a/stubs/Collection.php +++ b/stubs/Collection.php @@ -131,6 +131,7 @@ class Collection implements ArrayAccess, Countable * * @param int $size * @param callable $transform[optional] ($value, $key) -> $new_value + * @return Collection */ function chunked($size, $transform) {} diff --git a/tests/057-chunked.phpt b/tests/057-chunked.phpt new file mode 100644 index 0000000..59f04f5 --- /dev/null +++ b/tests/057-chunked.phpt @@ -0,0 +1,27 @@ +--TEST-- +Test Collection::chunked(). +--FILE-- +chunked($chunk_size)->toArray() != $chunked) { + echo 'Collection::chunked() failed.', PHP_EOL; +} +$transform = function ($value) use ($chunk_size) { + if (count($value) < $chunk_size) { + return []; + } + return $value; +}; +$chunked = array_map($transform, $chunked); +if ($collection->chunked($chunk_size, $transform)->toArray() != $chunked) { + echo 'Collection::chunked() failed.', PHP_EOL; +} +?> +--EXPECT--