diff --git a/src/collections.c b/src/collections.c index b2d2b7e..d3329be 100644 --- a/src/collections.c +++ b/src/collections.c @@ -10,6 +10,7 @@ #include #include +#include #include #include "php_collections.h" @@ -18,6 +19,8 @@ zend_string* collection_property_name; zend_string* pair_first_name; zend_string* pair_second_name; +zend_object_handlers* collection_handlers; + zend_class_entry* collections_collection_ce; zend_class_entry* collections_pair_ce; @@ -26,7 +29,17 @@ PHP_MINIT_FUNCTION(collections) zend_class_entry collection_ce; INIT_CLASS_ENTRY_EX(collection_ce, "Collection", sizeof "Collection" - 1, collection_methods); collections_collection_ce = zend_register_internal_class(&collection_ce); + zend_class_implements(collections_collection_ce, +#if PHP_VERSION_ID < 70200 + 1, +#else + 2, zend_ce_countable, +#endif + zend_ce_arrayaccess); zend_declare_property_null(collections_collection_ce, "_", sizeof "_" - 1, ZEND_ACC_PRIVATE); + collection_handlers = (zend_object_handlers*)emalloc(sizeof(zend_object_handlers)); + memcpy(collection_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + collection_handlers->count_elements = count_collection; zend_class_entry pair_ce; INIT_CLASS_ENTRY_EX(pair_ce, "Pair", sizeof "Pair" - 1, pair_methods); collections_pair_ce = zend_register_internal_class(&pair_ce); diff --git a/src/collections_me.c b/src/collections_me.c index 7d42c3c..8de9b6e 100644 --- a/src/collections_me.c +++ b/src/collections_me.c @@ -35,11 +35,6 @@ ZEND_BEGIN_ARG_INFO(elements_arginfo, 0) ZEND_ARG_INFO(0, elements) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO(from_to_arginfo, 0) - ZEND_ARG_TYPE_INFO(0, from_index, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, to_index, IS_LONG, 0) -ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO(initial_operation_arginfo, 0) ZEND_ARG_INFO(0, initial) ZEND_ARG_CALLABLE_INFO(0, operation, 0) @@ -95,6 +90,11 @@ ZEND_BEGIN_ARG_INFO(copy_of_arginfo, 0) ZEND_ARG_TYPE_INFO(0, new_size, IS_LONG, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO(copy_of_range_arginfo, 0) + ZEND_ARG_TYPE_INFO(0, from_idx, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, num_elements, IS_LONG, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO(fill_arginfo, 0) ZEND_ARG_INFO(0, element) ZEND_ARG_TYPE_INFO(0, from_index, IS_LONG, 0) @@ -135,7 +135,7 @@ const zend_function_entry collection_methods[] = { PHP_ME(Collection, containsKey, key_arginfo, ZEND_ACC_PUBLIC) PHP_ME(Collection, containsValue, element_arginfo, ZEND_ACC_PUBLIC) PHP_ME(Collection, copyOf, copy_of_arginfo, ZEND_ACC_PUBLIC) - PHP_ME(Collection, copyOfRange, from_to_arginfo, ZEND_ACC_PUBLIC) + PHP_ME(Collection, copyOfRange, copy_of_range_arginfo, ZEND_ACC_PUBLIC) PHP_ME(Collection, count, NULL, ZEND_ACC_PUBLIC) PHP_ME(Collection, distinct, NULL, ZEND_ACC_PUBLIC) PHP_ME(Collection, distinctBy, selector_arginfo, ZEND_ACC_PUBLIC) @@ -210,7 +210,6 @@ const zend_function_entry collection_methods[] = { PHP_ME(Collection, shuffle, NULL, ZEND_ACC_PUBLIC) PHP_ME(Collection, single, predicate_arginfo, ZEND_ACC_PUBLIC) PHP_ME(Collection, slice, keys_arginfo, ZEND_ACC_PUBLIC) - PHP_ME(Collection, sliceRange, from_to_arginfo, ZEND_ACC_PUBLIC) PHP_ME(Collection, sort, NULL, ZEND_ACC_PUBLIC) PHP_ME(Collection, sortBy, selector_arginfo, ZEND_ACC_PUBLIC) PHP_ME(Collection, sortByDescending, selector_arginfo, ZEND_ACC_PUBLIC) diff --git a/src/collections_methods.c b/src/collections_methods.c index f73c310..40090e9 100644 --- a/src/collections_methods.c +++ b/src/collections_methods.c @@ -9,14 +9,16 @@ #include "php_collections.h" #include "php_collections_me.h" -#define NEW_OBJ(name, ce) \ +#define NEW_OBJ(name, ce, object_handlers) \ zend_object* (name) = (zend_object*)ecalloc(1, sizeof(zend_object) + \ zend_object_properties_size(ce)); \ zend_object_std_init(name, ce); \ object_properties_init(name, ce); \ - (name)->handlers = &std_object_handlers; -#define NEW_COLLECTION_OBJ(name) NEW_OBJ(name, collections_collection_ce) -#define NEW_PAIR_OBJ(name) NEW_OBJ(name, collections_pair_ce) + (name)->handlers = object_handlers; +#define NEW_COLLECTION_OBJ(name) \ + NEW_OBJ(name, collections_collection_ce, collection_handlers) +#define NEW_PAIR_OBJ(name) \ + NEW_OBJ(name, collections_pair_ce, &std_object_handlers) #define IS_COLLECTION_P(zval) \ Z_TYPE_P(zval) == IS_OBJECT && Z_OBJCE_P(zval) == collections_collection_ce @@ -58,12 +60,12 @@ else \ equal_check_func = fast_equal_check_function; - #define PHP_COLLECTIONS_ERROR(type, msg) php_error_docref(NULL, type, msg) #define ERR_BAD_ARGUMENT_TYPE() PHP_COLLECTIONS_ERROR(E_WARNING, "Bad argument type") #define ERR_BAD_KEY_TYPE() PHP_COLLECTIONS_ERROR(E_WARNING, "Key must be integer or string") #define ERR_BAD_CALLBACK_RETVAL() PHP_COLLECTIONS_ERROR(E_WARNING, "Bad callback return value") #define ERR_BAD_SIZE() PHP_COLLECTIONS_ERROR(E_WARNING, "Size must be non-negative") +#define ERR_BAD_INDEX() PHP_COLLECTIONS_ERROR(E_WARNING, "Index must be non-negative") #define ERR_NOT_ARITHMETIC() PHP_COLLECTIONS_ERROR(E_WARNING, "Elements should be int or double") #define ELEMENTS_VALIDATE(elements) \ @@ -101,6 +103,14 @@ return; \ } +int count_collection(zval* obj, zend_long* count) +{ + zval rv; + zval* current = COLLECTION_FETCH(obj); + *count = zend_hash_num_elements(Z_ARRVAL_P(current)); + return SUCCESS; +} + PHP_METHOD(Collection, __construct) {} PHP_METHOD(Collection, addAll) @@ -387,12 +397,38 @@ PHP_METHOD(Collection, copyOf) PHP_METHOD(Collection, copyOfRange) { - + zend_long from_idx, num_elements; + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(from_idx) + Z_PARAM_LONG(num_elements) + ZEND_PARSE_PARAMETERS_END(); + if (from_idx < 0) { + ERR_BAD_INDEX(); + RETURN_NULL(); + } + if (num_elements < 0) { + ERR_BAD_SIZE(); + RETURN_NULL(); + } + zval rv; + zval* current = COLLECTION_FETCH_EX(); + ARRAY_NEW(new_collection, num_elements); + Bucket* bucket = Z_ARRVAL_P(current)->arData; + Bucket* end = bucket + Z_ARRVAL_P(current)->nNumUsed; + for (bucket += from_idx; num_elements > 0 && bucket < end; ++bucket, --num_elements) { + if (bucket->key) + zend_hash_add(new_collection, bucket->key, &bucket->val); + else + zend_hash_next_index_insert(new_collection, &bucket->val); + } + RETVAL_NEW_COLLECTION(new_collection); } PHP_METHOD(Collection, count) { - + zend_long count; + count_collection(getThis(), &count); + RETVAL_LONG(count); } PHP_METHOD(Collection, distinct) @@ -783,11 +819,6 @@ PHP_METHOD(Collection, slice) } -PHP_METHOD(Collection, sliceRange) -{ - -} - PHP_METHOD(Collection, sort) { diff --git a/src/php_collections.h b/src/php_collections.h index 916c36c..6f7968c 100644 --- a/src/php_collections.h +++ b/src/php_collections.h @@ -27,6 +27,10 @@ extern zend_string* pair_second_name; extern PHP_COLLECTIONS_API zend_class_entry* collections_collection_ce; extern PHP_COLLECTIONS_API zend_class_entry* collections_pair_ce; +extern zend_object_handlers* collection_handlers; + +int count_collection(zval* obj, zend_long* count); + extern const zend_function_entry collection_methods[]; extern const zend_function_entry pair_methods[]; diff --git a/src/php_collections_me.h b/src/php_collections_me.h index d4888a1..81c6040 100644 --- a/src/php_collections_me.h +++ b/src/php_collections_me.h @@ -96,7 +96,6 @@ PHP_METHOD(Collection, reversed); PHP_METHOD(Collection, shuffle); PHP_METHOD(Collection, single); PHP_METHOD(Collection, slice); -PHP_METHOD(Collection, sliceRange); PHP_METHOD(Collection, sort); PHP_METHOD(Collection, sortBy); PHP_METHOD(Collection, sortByDescending); diff --git a/stubs/Collection.php b/stubs/Collection.php index 1a1e7db..72497c9 100644 --- a/stubs/Collection.php +++ b/stubs/Collection.php @@ -117,10 +117,10 @@ class Collection implements ArrayAccess, Countable * Returns new collection which is a copy of range of original collection. * * @param int $from_index - * @param int $to_index + * @param int $num_elements * @return Collection */ - function copyOfRange($from_index, $to_index) {} + function copyOfRange($from_index, $num_elements) {} /** * Returns the number of elements in this collection. @@ -746,14 +746,6 @@ class Collection implements ArrayAccess, Countable */ function slice($keys) {} - /** - * Returns a collection containing elements at indices in the specified indices range. - * - * @param int $from_index - * @param int $to_index[optional] - */ - function sliceRange($from_index, $to_index) {} - /** * Sorts the collection in-place according to the natural order of its elements. * diff --git a/tests/011-copy-of-range.phpt b/tests/011-copy-of-range.phpt new file mode 100644 index 0000000..e1bd26e --- /dev/null +++ b/tests/011-copy-of-range.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test Collection::copyOfRange(); +--FILE-- + 'b', 'c', 'd' => 'e']; +$array2 = Collection::init($array)->copyOfRange(2, 4)->toArray(); +$array3 = Collection::init($array1)->copyOfRange(1, 2)->toArray(); +if ($array2 != array_slice($array, 2, 4) || $array3 != array_slice($array1, 1, 2)) + echo 'Collection::copyOfRange() failed.', PHP_EOL; +?> +--EXPECT-- diff --git a/tests/012-countable.phpt b/tests/012-countable.phpt new file mode 100644 index 0000000..aa3399e --- /dev/null +++ b/tests/012-countable.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Collection::count() and countable interface. +--FILE-- +count() != count($array)) + echo 'Collection::count() failed.', PHP_EOL; +if (count($collection) != count($array)) + echo 'Test for countable interface failed.', PHP_EOL; +?> +--EXPECT--