diff --git a/src/collections_me.c b/src/collections_me.c index d42e8aa..a0dc675 100644 --- a/src/collections_me.c +++ b/src/collections_me.c @@ -129,6 +129,8 @@ const zend_function_entry collection_methods[] = { PHP_ME(Collection, associateByTo, associate_by_to_arginfo, ZEND_ACC_PUBLIC) PHP_ME(Collection, average, NULL, ZEND_ACC_PUBLIC) PHP_ME(Collection, containsAll, other_arginfo, ZEND_ACC_PUBLIC) + PHP_ME(Collection, containsAllKeys, other_arginfo, ZEND_ACC_PUBLIC) + PHP_ME(Collection, containsAllValues, other_arginfo, ZEND_ACC_PUBLIC) PHP_ME(Collection, containsKey, contains_key_arginfo, ZEND_ACC_PUBLIC) PHP_ME(Collection, containsValue, element_arginfo, ZEND_ACC_PUBLIC) PHP_ME(Collection, copyOf, copy_of_arginfo, ZEND_ACC_PUBLIC) diff --git a/src/collections_methods.c b/src/collections_methods.c index 7923807..a54203c 100644 --- a/src/collections_methods.c +++ b/src/collections_methods.c @@ -738,22 +738,99 @@ PHP_METHOD(Collection, containsAll) ZEND_PARSE_PARAMETERS_END(); ELEMENTS_VALIDATE(elements, ERR_BAD_ARGUMENT_TYPE, return); zend_array* current = COLLECTION_FETCH_CURRENT(); - ZEND_HASH_FOREACH_VAL(elements_arr, zval* element) - equal_check_func_t eql = equal_check_func_init(element); - int result = 0; - ZEND_HASH_FOREACH_VAL(current, zval* val) - result = eql(element, val); - if (result) { - break; + equal_check_func_t eql = NULL; + ZEND_HASH_FOREACH_BUCKET(elements_arr, Bucket* bucket) + if (UNEXPECTED(eql == NULL)) { + eql = equal_check_func_init(&bucket->val); + } + if (bucket->key) { + zval* result = zend_hash_find(current, bucket->key); + if (!result || !eql(&bucket->val, result)) { + RETURN_FALSE; + } + } else { + zval* result = zend_hash_index_find(current, bucket->h); + if (!result || !eql(&bucket->val, result)) { + RETURN_FALSE; + } + } + ZEND_HASH_FOREACH_END(); + RETVAL_TRUE; +} + +PHP_METHOD(Collection, containsAllKeys) +{ + zval* elements; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(elements) + ZEND_PARSE_PARAMETERS_END(); + ELEMENTS_VALIDATE(elements, ERR_BAD_ARGUMENT_TYPE, return); + zend_array* current = COLLECTION_FETCH_CURRENT(); + ZEND_HASH_FOREACH_BUCKET(elements_arr, Bucket* bucket) + if (bucket->key) { + if (!zend_hash_exists(current, bucket->key)) { + RETURN_FALSE; + } + } else { + if (!zend_hash_index_exists(current, bucket->h)) { + RETURN_FALSE; } - ZEND_HASH_FOREACH_END(); - if (result == 0) { - RETURN_FALSE; } ZEND_HASH_FOREACH_END(); RETURN_TRUE; } +PHP_METHOD(Collection, containsAllValues) +{ + zval* elements; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(elements) + ZEND_PARSE_PARAMETERS_END(); + ELEMENTS_VALIDATE(elements, ERR_BAD_ARGUMENT_TYPE, return); + zend_array* current = COLLECTION_FETCH_CURRENT(); + compare_func_t cmp = NULL; + equal_check_func_t eql = NULL; + ZEND_HASH_FOREACH_VAL(current, zval* val) + cmp = compare_func_init(val, 0, 0); + eql = equal_check_func_init(val); + break; + ZEND_HASH_FOREACH_END(); + ARRAY_CLONE(sorted_current, current); + zend_hash_sort(sorted_current, cmp, 1); + ARRAY_CLONE(sorted_other, elements_arr); + zend_hash_sort(sorted_other, cmp, 1); + Bucket* this = NULL; + Bucket* other = sorted_other->arData; + Bucket* end_other = other + zend_hash_num_elements(sorted_other); + zend_bool result = 1; + ZEND_HASH_FOREACH_BUCKET(sorted_current, Bucket* bucket) + if (EXPECTED(this) && eql(&bucket->val, &this->val)) { + continue; + } + this = bucket; + if (cmp(bucket, other)) { + result = 0; + break; + } + do { + if (UNEXPECTED(++other == end_other)) { + goto end; + } + } while (eql(&(other - 1)->val, &other->val)); + ZEND_HASH_FOREACH_END(); + result = 0; + do { + if (UNEXPECTED(++other == end_other)) { + result = 1; + break; + } + } while (eql(&(other - 1)->val, &other->val)); +end: + zend_array_destroy(sorted_current); + zend_array_destroy(sorted_other); + RETVAL_BOOL(result); +} + PHP_METHOD(Collection, containsKey) { zval* key; @@ -1546,18 +1623,18 @@ PHP_METHOD(Collection, intersect) zend_array* current = COLLECTION_FETCH_CURRENT(); ARRAY_NEW(intersected, 8); equal_check_func_t eql = NULL; - ZEND_HASH_FOREACH_BUCKET(elements_arr, Bucket* bucket) + ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) if (UNEXPECTED(eql == NULL)) { eql = equal_check_func_init(&bucket->val); } if (bucket->key) { - zval* result = zend_hash_find(current, bucket->key); + zval* result = zend_hash_find(elements_arr, bucket->key); if (result && eql(&bucket->val, result)) { zend_hash_add(intersected, bucket->key, result); Z_TRY_ADDREF_P(result); } } else { - zval* result = zend_hash_index_find(current, bucket->h); + zval* result = zend_hash_index_find(elements_arr, bucket->h); if (result && eql(&bucket->val, result)) { zend_hash_index_add(intersected, bucket->h, result); Z_TRY_ADDREF_P(result); diff --git a/src/php_collections_me.h b/src/php_collections_me.h index 1dc4d72..134738b 100644 --- a/src/php_collections_me.h +++ b/src/php_collections_me.h @@ -17,6 +17,8 @@ PHP_METHOD(Collection, associateBy); PHP_METHOD(Collection, associateByTo); PHP_METHOD(Collection, average); PHP_METHOD(Collection, containsAll); +PHP_METHOD(Collection, containsAllKeys); +PHP_METHOD(Collection, containsAllValues); PHP_METHOD(Collection, containsKey); PHP_METHOD(Collection, containsValue); PHP_METHOD(Collection, copyOf); diff --git a/stubs/Collection.php b/stubs/Collection.php index cdd8bfe..ffc248e 100644 --- a/stubs/Collection.php +++ b/stubs/Collection.php @@ -93,13 +93,29 @@ class Collection implements ArrayAccess, Countable function average() {} /** - * Checks if all elements in the specified collection are contained in this collection. + * Checks if all key-value pairs in the specified collection are contained in this collection. * * @param array|Collection $other * @return bool */ function containsAll($other) {} + /** + * Checks if all keys in the specified collection are contained in this collection. + * + * @param array|Collection $other + * @return bool + */ + function containsAllKeys($other) {} + + /** + * Checks if all values in the specified collection are contained in this collection. + * + * @param array|Collection $other + * @return bool + */ + function containsAllValues($other) {} + /** * Checks if the collection contains the given key. * diff --git a/tests/008-contains-all.phpt b/tests/008-contains-all.phpt index dd8a44e..b8acea0 100644 --- a/tests/008-contains-all.phpt +++ b/tests/008-contains-all.phpt @@ -1,12 +1,38 @@ --TEST-- -Test Collection::containsAll(). +Test Collection::containsAll(), Collection::containsAllKeys(), Collection::containsAllValues(). --FILE-- containsAll($array) || $collection->containsAll($array1)) { +$collection = Collection::init([ + 'a' => 'b', + 'c' => 'd', + 'e' => 'f' +]); +$collection1 = Collection::init([ + 'a' => 'j', + 'c' => 'f', + 'e' => 'b' +]); +$collection2 = Collection::init([ + 'a' => 'b', + 'e' => 'f' +]); +if (!$collection->containsAll($collection2) || + $collection2->containsAll($collection) || + $collection->containsAll($collection1) +) { echo 'Collection::containsAll() failed.', PHP_EOL; } +if (!$collection1->containsAllKeys($collection2) || + !$collection->containsAllKeys($collection1) || + $collection2->containsAllKeys($collection1) +) { + echo 'Collection::containsAllKeys() failed.', PHP_EOL; +} +if (!$collection->containsAllValues($collection2) || + !$collection1->containsAllValues($collection2) || + $collection->containsAllValues($collection1) +) { + echo 'Collection::containsAllValues() failed.', PHP_EOL; +} ?> --EXPECT--