From 441ca5be7245b57c5473913a8cbcb9fb31ad1253 Mon Sep 17 00:00:00 2001 From: CismonX Date: Sun, 3 Jun 2018 19:18:40 +0800 Subject: [PATCH] Add `takeWhile()` and `takeLastWhile()`. --- src/collections_methods.c | 57 +++++++++++++++++++++++++++++++++++++-- stubs/Collection.php | 4 +-- tests/034-take-while.phpt | 18 +++++++++++++ 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 tests/034-take-while.phpt diff --git a/src/collections_methods.c b/src/collections_methods.c index c4cb05e..c711482 100644 --- a/src/collections_methods.c +++ b/src/collections_methods.c @@ -1429,6 +1429,8 @@ PHP_METHOD(Collection, take) continue; --n; Z_TRY_ADDREF(bucket->val); + // Works for any zend_array, however, it doesn't make sense if you use any of these methods + // on non-packed zend_arrays. if (bucket->key) zend_hash_add_new(new_collection, bucket->key, &bucket->val); else if (HT_IS_PACKED(current)) @@ -1481,12 +1483,63 @@ PHP_METHOD(Collection, takeLast) PHP_METHOD(Collection, takeLastWhile) { - + zend_fcall_info fci; + zend_fcall_info_cache fcc; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); + INIT_FCI(2); + zend_array* current = COLLECTION_FETCH_CURRENT(); + ARRAY_NEW_EX(new_collection, current); + uint32_t num_elements = zend_hash_num_elements(current); + Bucket* taken[num_elements]; + ZEND_HASH_REVERSE_FOREACH_BUCKET(current, Bucket* bucket) + CALLBACK_KEYVAL_INVOKE(params, bucket); + if (zend_is_true(&retval)) + taken[--num_elements] = bucket; + else + break; + ZEND_HASH_FOREACH_END(); + memset(&taken[0], NULL, num_elements * sizeof(Bucket*)); + int i = 0; + for (; i < zend_hash_num_elements(current); ++i) { + Bucket* bucket = taken[i]; + if (bucket == NULL) + continue; + Z_TRY_ADDREF(bucket->val); + if (bucket->key) + zend_hash_add_new(new_collection, bucket->key, &bucket->val); + else if (HT_IS_PACKED(current)) + zend_hash_next_index_insert(new_collection, &bucket->val); + else + zend_hash_index_add_new(new_collection, bucket->h, &bucket->val); + } + RETVAL_NEW_COLLECTION(new_collection); } PHP_METHOD(Collection, takeWhile) { - + zend_fcall_info fci; + zend_fcall_info_cache fcc; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); + INIT_FCI(2); + zend_array* current = COLLECTION_FETCH_CURRENT(); + ARRAY_NEW_EX(new_collection, current); + ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) + CALLBACK_KEYVAL_INVOKE(params, bucket); + if (zend_is_true(&retval)) { + if (bucket->key) + zend_hash_add_new(new_collection, bucket->key, &bucket->val); + else if (HT_IS_PACKED(current)) + zend_hash_next_index_insert(new_collection, &bucket->val); + else + zend_hash_index_add_new(new_collection, bucket->h, &bucket->val); + } else + break; + ZEND_HASH_FOREACH_END(); + RETVAL_NEW_COLLECTION(new_collection); } PHP_METHOD(Collection, toArray) diff --git a/stubs/Collection.php b/stubs/Collection.php index f62179c..1d2d3e1 100644 --- a/stubs/Collection.php +++ b/stubs/Collection.php @@ -840,7 +840,7 @@ class Collection implements ArrayAccess, Countable /** * Returns a collection containing last elements satisfying the given predicate. * - * @param $predicate ($value, $key) -> bool + * @param callable $predicate ($value, $key) -> bool * @return Collection */ function takeLastWhile($predicate) {} @@ -848,7 +848,7 @@ class Collection implements ArrayAccess, Countable /** * Returns a collection containing first elements satisfying the given predicate. * - * @param $predicate ($value, $key) -> bool + * @param callable $predicate ($value, $key) -> bool * @return Collection */ function takeWhile($predicate) {} diff --git a/tests/034-take-while.phpt b/tests/034-take-while.phpt new file mode 100644 index 0000000..3969d9d --- /dev/null +++ b/tests/034-take-while.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test Collection::takeWhile() and Collection::takeLastWhile(). +--FILE-- +takeWhile(function ($value) { + return ord($value) > ord('Z'); +}); +if ($collection1->toArray() != ['a', 'b']) + echo 'Collection::takeWhile() failed.', PHP_EOL; +$collection2 = $collection->takeLastWhile(function ($value) { + return $value != 'b'; +}); +if ($collection2->toArray() != ['C', 'D', 'e']) + echo 'Collection::takeLastWhile() failed.', PHP_EOL; +?> +--EXPECT--