update compare flags
This commit is contained in:
parent
e9e1c966d7
commit
f32fa48291
|
@ -31,7 +31,13 @@ The `Collection` class implements `ArrayAccess` and `Countable` interface intern
|
||||||
* `empty()`, `count()` can be used on instance of `Collection`.
|
* `empty()`, `count()` can be used on instance of `Collection`.
|
||||||
* Elements can be traversed via `foreach()` keyword.
|
* Elements can be traversed via `foreach()` keyword.
|
||||||
|
|
||||||
### 2.3 Notes
|
### 2.3 Comparing elements
|
||||||
|
|
||||||
|
Some methods of `Collection` involves comparing two of its elements, which accepts `$flags` as one of its arguments.
|
||||||
|
|
||||||
|
When these methods are being invoked, type of the very first element of the `Collection` represents that of all other ones. Make sure all elements are of the same type (numeric/string/others), otherwise you're likely to get a segfault.
|
||||||
|
|
||||||
|
### 2.4 Notes
|
||||||
|
|
||||||
* The `Collection::xxxTo()` methods will preserve the original key-value pairs of destination `Collection` when keys collide.
|
* The `Collection::xxxTo()` methods will preserve the original key-value pairs of destination `Collection` when keys collide.
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,10 @@ PHP_MINIT_FUNCTION(collections)
|
||||||
zend_class_entry collection_ce;
|
zend_class_entry collection_ce;
|
||||||
INIT_CLASS_ENTRY_EX(collection_ce, "Collection", sizeof "Collection" - 1, collection_methods);
|
INIT_CLASS_ENTRY_EX(collection_ce, "Collection", sizeof "Collection" - 1, collection_methods);
|
||||||
collections_collection_ce = zend_register_internal_class(&collection_ce);
|
collections_collection_ce = zend_register_internal_class(&collection_ce);
|
||||||
|
zend_declare_class_constant_long(collections_collection_ce,
|
||||||
|
"COMPARE_NATRUAL", sizeof "COMPARE_NATRUAL" - 1, PHP_COLLECTIONS_COMPARE_NATURAL);
|
||||||
|
zend_declare_class_constant_long(collections_collection_ce,
|
||||||
|
"FOLD_CASE", sizeof "FOLD_CASE" - 1, PHP_COLLECTIONS_FOLD_CASE);
|
||||||
zend_class_implements(collections_collection_ce,
|
zend_class_implements(collections_collection_ce,
|
||||||
#if PHP_VERSION_ID < 70200
|
#if PHP_VERSION_ID < 70200
|
||||||
1,
|
1,
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <php.h>
|
#include <php.h>
|
||||||
|
#include <ext/standard/php_string.h>
|
||||||
#include <ext/standard/php_mt_rand.h>
|
#include <ext/standard/php_mt_rand.h>
|
||||||
|
|
||||||
#include "php_collections.h"
|
#include "php_collections.h"
|
||||||
|
@ -53,22 +54,6 @@
|
||||||
(fci)->retval = &retval; \
|
(fci)->retval = &retval; \
|
||||||
(fci)->params = params
|
(fci)->params = params
|
||||||
|
|
||||||
#define BUCKET_2_PAIR(pair, bucket) \
|
|
||||||
{ \
|
|
||||||
zval _key; \
|
|
||||||
if ((bucket)->key) \
|
|
||||||
{ \
|
|
||||||
GC_ADDREF((bucket)->key); \
|
|
||||||
ZVAL_STR(&_key, (bucket)->key); \
|
|
||||||
} \
|
|
||||||
else \
|
|
||||||
{ \
|
|
||||||
ZVAL_LONG(&_key, (bucket)->h); \
|
|
||||||
} \
|
|
||||||
PAIR_UPDATE_FIRST(pair, &_key); \
|
|
||||||
PAIR_UPDATE_SECOND(pair, &(bucket)->val); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CALLBACK_KEYVAL_INVOKE(params, bucket) \
|
#define CALLBACK_KEYVAL_INVOKE(params, bucket) \
|
||||||
ZVAL_COPY_VALUE(¶ms[0], &bucket->val); \
|
ZVAL_COPY_VALUE(¶ms[0], &bucket->val); \
|
||||||
if ((bucket)->key) \
|
if ((bucket)->key) \
|
||||||
|
@ -81,15 +66,6 @@
|
||||||
} \
|
} \
|
||||||
zend_call_function(&fci, &fcc)
|
zend_call_function(&fci, &fcc)
|
||||||
|
|
||||||
#define INIT_EQUAL_CHECK_FUNC(val) \
|
|
||||||
int (*equal_check_func)(zval*, zval*); \
|
|
||||||
if (Z_TYPE_P(val) == IS_LONG) \
|
|
||||||
equal_check_func = fast_equal_check_long; \
|
|
||||||
else if (Z_TYPE_P(val) == IS_STRING) \
|
|
||||||
equal_check_func = fast_equal_check_string; \
|
|
||||||
else \
|
|
||||||
equal_check_func = fast_equal_check_function;
|
|
||||||
|
|
||||||
#define PHP_COLLECTIONS_ERROR(type, msg) php_error_docref(NULL, type, msg)
|
#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_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_KEY_TYPE() PHP_COLLECTIONS_ERROR(E_WARNING, "Key must be integer or string")
|
||||||
|
@ -97,6 +73,7 @@
|
||||||
#define ERR_BAD_SIZE() PHP_COLLECTIONS_ERROR(E_WARNING, "Size must be non-negative")
|
#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_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 ERR_NOT_ARITHMETIC() PHP_COLLECTIONS_ERROR(E_WARNING, "Elements should be int or double")
|
||||||
|
#define ERR_BAD_FLAG() PHP_COLLECTIONS_ERROR(E_WARNING, "Invalid compare flag")
|
||||||
#define ERR_SILENCED()
|
#define ERR_SILENCED()
|
||||||
|
|
||||||
#define ELEMENTS_VALIDATE(elements, err, err_then) \
|
#define ELEMENTS_VALIDATE(elements, err, err_then) \
|
||||||
|
@ -143,24 +120,92 @@
|
||||||
#define FCI_G COLLECTIONS_G(fci)
|
#define FCI_G COLLECTIONS_G(fci)
|
||||||
#define FCC_G COLLECTIONS_G(fcc)
|
#define FCC_G COLLECTIONS_G(fcc)
|
||||||
|
|
||||||
|
typedef int (*equal_check_func_t)(zval*, zval*);
|
||||||
|
|
||||||
/// Unused global variable.
|
/// Unused global variable.
|
||||||
zval rv;
|
zval rv;
|
||||||
|
|
||||||
static int bucket_compare_numeric(const void* op1, const void* op2)
|
static zend_always_inline void bucket_to_pair(zend_object* pair, Bucket* bucket)
|
||||||
|
{
|
||||||
|
zval key;
|
||||||
|
if (bucket->key)
|
||||||
|
{
|
||||||
|
GC_ADDREF(bucket->key);
|
||||||
|
ZVAL_STR(&key, bucket->key);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ZVAL_LONG(&key, bucket->h);
|
||||||
|
}
|
||||||
|
PAIR_UPDATE_FIRST(pair, &key);
|
||||||
|
PAIR_UPDATE_SECOND(pair, &bucket->val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static zend_always_inline int bucket_compare_numeric(const void* op1, const void* op2)
|
||||||
{
|
{
|
||||||
Bucket* b1 = (Bucket*)op1;
|
Bucket* b1 = (Bucket*)op1;
|
||||||
Bucket* b2 = (Bucket*)op2;
|
Bucket* b2 = (Bucket*)op2;
|
||||||
return numeric_compare_function(&b1->val, &b2->val);
|
return numeric_compare_function(&b1->val, &b2->val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bucket_compare_string(const void* op1, const void* op2)
|
static int bucket_reverse_compare_numeric(const void* op1, const void* op2)
|
||||||
|
{
|
||||||
|
return bucket_compare_numeric(op2, op1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static zend_always_inline int bucket_compare_string_ci(const void* op1, const void* op2)
|
||||||
|
{
|
||||||
|
Bucket* b1 = (Bucket*)op1;
|
||||||
|
Bucket* b2 = (Bucket*)op2;
|
||||||
|
return string_case_compare_function(&b1->val, &b2->val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bucket_reverse_compare_string_ci(const void* op1, const void* op2)
|
||||||
|
{
|
||||||
|
return bucket_compare_string_ci(op2, op1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int zend_always_inline bucket_compare_string_cs(const void* op1, const void* op2)
|
||||||
{
|
{
|
||||||
Bucket* b1 = (Bucket*)op1;
|
Bucket* b1 = (Bucket*)op1;
|
||||||
Bucket* b2 = (Bucket*)op2;
|
Bucket* b2 = (Bucket*)op2;
|
||||||
return string_compare_function(&b1->val, &b2->val);
|
return string_compare_function(&b1->val, &b2->val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bucket_compare_regular(const void* op1, const void* op2)
|
static int bucket_reverse_compare_string_cs(const void* op1, const void* op2)
|
||||||
|
{
|
||||||
|
return bucket_compare_string_cs(op2, op1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static zend_always_inline int bucket_compare_natural_ci(const void* op1, const void* op2)
|
||||||
|
{
|
||||||
|
Bucket* b1 = (Bucket*)op1;
|
||||||
|
Bucket* b2 = (Bucket*)op2;
|
||||||
|
zval* v1 = &b1->val;
|
||||||
|
zval* v2 = &b2->val;
|
||||||
|
return strnatcmp_ex(Z_STRVAL_P(v1), Z_STRLEN_P(v1), Z_STRVAL_P(v2), Z_STRLEN_P(v2), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bucket_reverse_compare_natural_ci(const void* op1, const void* op2)
|
||||||
|
{
|
||||||
|
return bucket_compare_natural_ci(op2, op1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static zend_always_inline int bucket_compare_natural_cs(const void* op1, const void* op2)
|
||||||
|
{
|
||||||
|
Bucket* b1 = (Bucket*)op1;
|
||||||
|
Bucket* b2 = (Bucket*)op2;
|
||||||
|
zval* v1 = &b1->val;
|
||||||
|
zval* v2 = &b2->val;
|
||||||
|
return strnatcmp_ex(Z_STRVAL_P(v1), Z_STRLEN_P(v1), Z_STRVAL_P(v2), Z_STRLEN_P(v2), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bucket_reverse_compare_natural_cs(const void* op1, const void* op2)
|
||||||
|
{
|
||||||
|
return bucket_compare_natural_cs(op2, op1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static zend_always_inline int bucket_compare_regular(const void* op1, const void* op2)
|
||||||
{
|
{
|
||||||
Bucket* b1 = (Bucket*)op1;
|
Bucket* b1 = (Bucket*)op1;
|
||||||
Bucket* b2 = (Bucket*)op2;
|
Bucket* b2 = (Bucket*)op2;
|
||||||
|
@ -172,6 +217,11 @@ static int bucket_compare_regular(const void* op1, const void* op2)
|
||||||
return ZEND_NORMALIZE_BOOL(Z_LVAL(result));
|
return ZEND_NORMALIZE_BOOL(Z_LVAL(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int bucket_reverse_compare_regular(const void* op1, const void* op2)
|
||||||
|
{
|
||||||
|
return bucket_compare_regular(op2, op1);
|
||||||
|
}
|
||||||
|
|
||||||
static int bucket_compare_userland(const void* op1, const void* op2)
|
static int bucket_compare_userland(const void* op1, const void* op2)
|
||||||
{
|
{
|
||||||
Bucket* b1 = (Bucket*)op1;
|
Bucket* b1 = (Bucket*)op1;
|
||||||
|
@ -185,6 +235,46 @@ static int bucket_compare_userland(const void* op1, const void* op2)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static zend_always_inline equal_check_func_t equal_check_func_init(zval* val)
|
||||||
|
{
|
||||||
|
if (Z_TYPE_P(val) == IS_LONG)
|
||||||
|
{
|
||||||
|
return fast_equal_check_long;
|
||||||
|
}
|
||||||
|
if (Z_TYPE_P(val) == IS_STRING)
|
||||||
|
{
|
||||||
|
return fast_equal_check_string;
|
||||||
|
}
|
||||||
|
return fast_equal_check_function;
|
||||||
|
}
|
||||||
|
|
||||||
|
static zend_always_inline compare_func_t compare_func_init(
|
||||||
|
zval* val, zend_bool reverse, zend_long flags)
|
||||||
|
{
|
||||||
|
zend_bool case_insensitive = flags & PHP_COLLECTIONS_FOLD_CASE;
|
||||||
|
if (Z_TYPE_P(val) == IS_LONG || Z_TYPE_P(val) == IS_DOUBLE)
|
||||||
|
{
|
||||||
|
return reverse ? bucket_reverse_compare_numeric : bucket_compare_numeric;
|
||||||
|
}
|
||||||
|
if (Z_TYPE_P(val) == IS_STRING)
|
||||||
|
{
|
||||||
|
if ((flags & ~PHP_COLLECTIONS_FOLD_CASE) == PHP_COLLECTIONS_COMPARE_NATURAL)
|
||||||
|
{
|
||||||
|
if (case_insensitive)
|
||||||
|
{
|
||||||
|
return reverse ? bucket_reverse_compare_natural_ci : bucket_compare_natural_ci;
|
||||||
|
}
|
||||||
|
return reverse ? bucket_reverse_compare_natural_cs : bucket_compare_natural_cs;
|
||||||
|
}
|
||||||
|
if (case_insensitive)
|
||||||
|
{
|
||||||
|
return reverse ? bucket_reverse_compare_string_ci : bucket_compare_string_ci;
|
||||||
|
}
|
||||||
|
return reverse ? bucket_reverse_compare_string_cs : bucket_compare_string_cs;
|
||||||
|
}
|
||||||
|
return reverse ? bucket_reverse_compare_regular : bucket_compare_regular;
|
||||||
|
}
|
||||||
|
|
||||||
int count_collection(zval* obj, zend_long* count)
|
int count_collection(zval* obj, zend_long* count)
|
||||||
{
|
{
|
||||||
zend_array* current = COLLECTION_FETCH(obj);
|
zend_array* current = COLLECTION_FETCH(obj);
|
||||||
|
@ -505,10 +595,10 @@ PHP_METHOD(Collection, containsAll)
|
||||||
ELEMENTS_VALIDATE(elements, ERR_BAD_ARGUMENT_TYPE, return);
|
ELEMENTS_VALIDATE(elements, ERR_BAD_ARGUMENT_TYPE, return);
|
||||||
zend_array* current = COLLECTION_FETCH_CURRENT();
|
zend_array* current = COLLECTION_FETCH_CURRENT();
|
||||||
ZEND_HASH_FOREACH_VAL(elements_arr, zval* element)
|
ZEND_HASH_FOREACH_VAL(elements_arr, zval* element)
|
||||||
INIT_EQUAL_CHECK_FUNC(element);
|
equal_check_func_t eql = equal_check_func_init(element);
|
||||||
int result = 0;
|
int result = 0;
|
||||||
ZEND_HASH_FOREACH_VAL(current, zval* val)
|
ZEND_HASH_FOREACH_VAL(current, zval* val)
|
||||||
result = equal_check_func(element, val);
|
result = eql(element, val);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
|
@ -548,9 +638,9 @@ PHP_METHOD(Collection, containsValue)
|
||||||
Z_PARAM_ZVAL(element)
|
Z_PARAM_ZVAL(element)
|
||||||
ZEND_PARSE_PARAMETERS_END();
|
ZEND_PARSE_PARAMETERS_END();
|
||||||
zend_array* current = COLLECTION_FETCH_CURRENT();
|
zend_array* current = COLLECTION_FETCH_CURRENT();
|
||||||
INIT_EQUAL_CHECK_FUNC(element);
|
equal_check_func_t eql = equal_check_func_init(element);
|
||||||
ZEND_HASH_FOREACH_VAL(current, zval* val)
|
ZEND_HASH_FOREACH_VAL(current, zval* val)
|
||||||
if (equal_check_func(element, val))
|
if (eql(element, val))
|
||||||
{
|
{
|
||||||
RETURN_TRUE;
|
RETURN_TRUE;
|
||||||
}
|
}
|
||||||
|
@ -1192,9 +1282,9 @@ PHP_METHOD(Collection, indexOf)
|
||||||
Z_PARAM_ZVAL(element)
|
Z_PARAM_ZVAL(element)
|
||||||
ZEND_PARSE_PARAMETERS_END();
|
ZEND_PARSE_PARAMETERS_END();
|
||||||
zend_array* current = COLLECTION_FETCH_CURRENT();
|
zend_array* current = COLLECTION_FETCH_CURRENT();
|
||||||
INIT_EQUAL_CHECK_FUNC(element);
|
equal_check_func_t eql = equal_check_func_init(element);
|
||||||
ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket)
|
ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket)
|
||||||
if (equal_check_func(element, &bucket->val))
|
if (eql(element, &bucket->val))
|
||||||
{
|
{
|
||||||
if (bucket->key)
|
if (bucket->key)
|
||||||
{
|
{
|
||||||
|
@ -1352,9 +1442,9 @@ PHP_METHOD(Collection, lastIndexOf)
|
||||||
Z_PARAM_ZVAL(element)
|
Z_PARAM_ZVAL(element)
|
||||||
ZEND_PARSE_PARAMETERS_END();
|
ZEND_PARSE_PARAMETERS_END();
|
||||||
zend_array* current = COLLECTION_FETCH_CURRENT();
|
zend_array* current = COLLECTION_FETCH_CURRENT();
|
||||||
INIT_EQUAL_CHECK_FUNC(element);
|
equal_check_func_t eql = equal_check_func_init(element);
|
||||||
ZEND_HASH_REVERSE_FOREACH_BUCKET(current, Bucket* bucket)
|
ZEND_HASH_REVERSE_FOREACH_BUCKET(current, Bucket* bucket)
|
||||||
if (equal_check_func(element, &bucket->val))
|
if (eql(element, &bucket->val))
|
||||||
{
|
{
|
||||||
if (bucket->key)
|
if (bucket->key)
|
||||||
{
|
{
|
||||||
|
@ -1405,8 +1495,18 @@ PHP_METHOD(Collection, mapTo)
|
||||||
|
|
||||||
PHP_METHOD(Collection, max)
|
PHP_METHOD(Collection, max)
|
||||||
{
|
{
|
||||||
|
zend_long flags = 0;
|
||||||
|
ZEND_PARSE_PARAMETERS_START(0, 1)
|
||||||
|
Z_PARAM_OPTIONAL
|
||||||
|
Z_PARAM_LONG(flags)
|
||||||
|
ZEND_PARSE_PARAMETERS_END();
|
||||||
zend_array* current = COLLECTION_FETCH_CURRENT();
|
zend_array* current = COLLECTION_FETCH_CURRENT();
|
||||||
zval* max = zend_hash_minmax(current, bucket_compare_numeric, 1);
|
compare_func_t cmp;
|
||||||
|
ZEND_HASH_FOREACH_VAL(current, zval* val)
|
||||||
|
cmp = compare_func_init(val, 0, flags);
|
||||||
|
break;
|
||||||
|
ZEND_HASH_FOREACH_END();
|
||||||
|
zval* max = zend_hash_minmax(current, cmp, 1);
|
||||||
if (max)
|
if (max)
|
||||||
{
|
{
|
||||||
RETURN_ZVAL(max, 0, 0);
|
RETURN_ZVAL(max, 0, 0);
|
||||||
|
@ -1418,17 +1518,25 @@ PHP_METHOD(Collection, maxBy)
|
||||||
{
|
{
|
||||||
zend_fcall_info fci;
|
zend_fcall_info fci;
|
||||||
zend_fcall_info_cache fcc;
|
zend_fcall_info_cache fcc;
|
||||||
ZEND_PARSE_PARAMETERS_START(1, 1)
|
zend_long flags = 0;
|
||||||
|
ZEND_PARSE_PARAMETERS_START(1, 2)
|
||||||
Z_PARAM_FUNC(fci, fcc)
|
Z_PARAM_FUNC(fci, fcc)
|
||||||
|
Z_PARAM_OPTIONAL
|
||||||
|
Z_PARAM_LONG(flags)
|
||||||
ZEND_PARSE_PARAMETERS_END();
|
ZEND_PARSE_PARAMETERS_END();
|
||||||
zend_array* current = COLLECTION_FETCH_CURRENT();
|
zend_array* current = COLLECTION_FETCH_CURRENT();
|
||||||
ARRAY_NEW_EX(max_by, current);
|
ARRAY_NEW_EX(max_by, current);
|
||||||
|
compare_func_t cmp = NULL;
|
||||||
INIT_FCI(&fci, 2);
|
INIT_FCI(&fci, 2);
|
||||||
ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket)
|
ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket)
|
||||||
CALLBACK_KEYVAL_INVOKE(params, bucket);
|
CALLBACK_KEYVAL_INVOKE(params, bucket);
|
||||||
|
if (UNEXPECTED(cmp == NULL))
|
||||||
|
{
|
||||||
|
cmp = compare_func_init(&retval, 0, flags);
|
||||||
|
}
|
||||||
zend_hash_index_add(max_by, bucket - current->arData, &retval);
|
zend_hash_index_add(max_by, bucket - current->arData, &retval);
|
||||||
ZEND_HASH_FOREACH_END();
|
ZEND_HASH_FOREACH_END();
|
||||||
zval* max = zend_hash_minmax(max_by, bucket_compare_numeric, 1);
|
zval* max = zend_hash_minmax(max_by, cmp, 1);
|
||||||
if (max)
|
if (max)
|
||||||
{
|
{
|
||||||
zend_ulong offset = *(zend_ulong*)(max + 1);
|
zend_ulong offset = *(zend_ulong*)(max + 1);
|
||||||
|
@ -1456,7 +1564,7 @@ PHP_METHOD(Collection, maxWith)
|
||||||
zend_array* max_with = zend_array_dup(current);
|
zend_array* max_with = zend_array_dup(current);
|
||||||
ZEND_HASH_FOREACH_BUCKET(max_with, Bucket* bucket)
|
ZEND_HASH_FOREACH_BUCKET(max_with, Bucket* bucket)
|
||||||
NEW_PAIR_OBJ(obj);
|
NEW_PAIR_OBJ(obj);
|
||||||
BUCKET_2_PAIR(obj, bucket);
|
bucket_to_pair(obj, bucket);
|
||||||
ZVAL_OBJ(&bucket->val, obj);
|
ZVAL_OBJ(&bucket->val, obj);
|
||||||
ZEND_HASH_FOREACH_END();
|
ZEND_HASH_FOREACH_END();
|
||||||
zval* max = PAIR_FETCH_SECOND(Z_OBJ_P(zend_hash_minmax(max_with, bucket_compare_userland, 1)));
|
zval* max = PAIR_FETCH_SECOND(Z_OBJ_P(zend_hash_minmax(max_with, bucket_compare_userland, 1)));
|
||||||
|
@ -1471,8 +1579,18 @@ PHP_METHOD(Collection, maxWith)
|
||||||
|
|
||||||
PHP_METHOD(Collection, min)
|
PHP_METHOD(Collection, min)
|
||||||
{
|
{
|
||||||
|
zend_long flags = 0;
|
||||||
|
ZEND_PARSE_PARAMETERS_START(0, 1)
|
||||||
|
Z_PARAM_OPTIONAL
|
||||||
|
Z_PARAM_LONG(flags)
|
||||||
|
ZEND_PARSE_PARAMETERS_END();
|
||||||
zend_array* current = COLLECTION_FETCH_CURRENT();
|
zend_array* current = COLLECTION_FETCH_CURRENT();
|
||||||
zval* min = zend_hash_minmax(current, bucket_compare_numeric, 0);
|
compare_func_t cmp;
|
||||||
|
ZEND_HASH_FOREACH_VAL(current, zval* val)
|
||||||
|
cmp = compare_func_init(val, 0, flags);
|
||||||
|
break;
|
||||||
|
ZEND_HASH_FOREACH_END();
|
||||||
|
zval* min = zend_hash_minmax(current, cmp, 0);
|
||||||
if (min)
|
if (min)
|
||||||
{
|
{
|
||||||
RETURN_ZVAL(min, 0, 0);
|
RETURN_ZVAL(min, 0, 0);
|
||||||
|
@ -1484,17 +1602,25 @@ PHP_METHOD(Collection, minBy)
|
||||||
{
|
{
|
||||||
zend_fcall_info fci;
|
zend_fcall_info fci;
|
||||||
zend_fcall_info_cache fcc;
|
zend_fcall_info_cache fcc;
|
||||||
ZEND_PARSE_PARAMETERS_START(1, 1)
|
zend_long flags = 0;
|
||||||
|
ZEND_PARSE_PARAMETERS_START(1, 2)
|
||||||
Z_PARAM_FUNC(fci, fcc)
|
Z_PARAM_FUNC(fci, fcc)
|
||||||
|
Z_PARAM_OPTIONAL
|
||||||
|
Z_PARAM_LONG(flags)
|
||||||
ZEND_PARSE_PARAMETERS_END();
|
ZEND_PARSE_PARAMETERS_END();
|
||||||
zend_array* current = COLLECTION_FETCH_CURRENT();
|
zend_array* current = COLLECTION_FETCH_CURRENT();
|
||||||
ARRAY_NEW_EX(min_by, current);
|
ARRAY_NEW_EX(min_by, current);
|
||||||
|
compare_func_t cmp = NULL;
|
||||||
INIT_FCI(&fci, 2);
|
INIT_FCI(&fci, 2);
|
||||||
ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket)
|
ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket)
|
||||||
CALLBACK_KEYVAL_INVOKE(params, bucket);
|
CALLBACK_KEYVAL_INVOKE(params, bucket);
|
||||||
|
if (UNEXPECTED(cmp == NULL))
|
||||||
|
{
|
||||||
|
cmp = compare_func_init(&retval, 0, flags);
|
||||||
|
}
|
||||||
zend_hash_index_add(min_by, bucket - current->arData, &retval);
|
zend_hash_index_add(min_by, bucket - current->arData, &retval);
|
||||||
ZEND_HASH_FOREACH_END();
|
ZEND_HASH_FOREACH_END();
|
||||||
zval* min = zend_hash_minmax(min_by, bucket_compare_numeric, 0);
|
zval* min = zend_hash_minmax(min_by, cmp, 0);
|
||||||
if (min)
|
if (min)
|
||||||
{
|
{
|
||||||
zend_ulong offset = *(zend_ulong*)(min + 1);
|
zend_ulong offset = *(zend_ulong*)(min + 1);
|
||||||
|
@ -1522,7 +1648,7 @@ PHP_METHOD(Collection, minWith)
|
||||||
zend_array* min_with = zend_array_dup(current);
|
zend_array* min_with = zend_array_dup(current);
|
||||||
ZEND_HASH_FOREACH_BUCKET(min_with, Bucket* bucket)
|
ZEND_HASH_FOREACH_BUCKET(min_with, Bucket* bucket)
|
||||||
NEW_PAIR_OBJ(obj);
|
NEW_PAIR_OBJ(obj);
|
||||||
BUCKET_2_PAIR(obj, bucket);
|
bucket_to_pair(obj, bucket);
|
||||||
ZVAL_OBJ(&bucket->val, obj);
|
ZVAL_OBJ(&bucket->val, obj);
|
||||||
ZEND_HASH_FOREACH_END();
|
ZEND_HASH_FOREACH_END();
|
||||||
zval* min = PAIR_FETCH_SECOND(Z_OBJ_P(zend_hash_minmax(min_with, bucket_compare_userland, 0)));
|
zval* min = PAIR_FETCH_SECOND(Z_OBJ_P(zend_hash_minmax(min_with, bucket_compare_userland, 0)));
|
||||||
|
@ -2092,7 +2218,7 @@ PHP_METHOD(Collection, sortWith)
|
||||||
zend_array* sorted_with = zend_array_dup(current);
|
zend_array* sorted_with = zend_array_dup(current);
|
||||||
ZEND_HASH_FOREACH_BUCKET(sorted_with, Bucket* bucket)
|
ZEND_HASH_FOREACH_BUCKET(sorted_with, Bucket* bucket)
|
||||||
NEW_PAIR_OBJ(obj);
|
NEW_PAIR_OBJ(obj);
|
||||||
BUCKET_2_PAIR(obj, bucket);
|
bucket_to_pair(obj, bucket);
|
||||||
ZVAL_OBJ(&bucket->val, obj);
|
ZVAL_OBJ(&bucket->val, obj);
|
||||||
ZEND_HASH_FOREACH_END();
|
ZEND_HASH_FOREACH_END();
|
||||||
zend_hash_sort(sorted_with, bucket_compare_userland, 1);
|
zend_hash_sort(sorted_with, bucket_compare_userland, 1);
|
||||||
|
@ -2145,7 +2271,7 @@ PHP_METHOD(Collection, sortedWith)
|
||||||
zend_array* sorted_with = zend_array_dup(current);
|
zend_array* sorted_with = zend_array_dup(current);
|
||||||
ZEND_HASH_FOREACH_BUCKET(sorted_with, Bucket* bucket)
|
ZEND_HASH_FOREACH_BUCKET(sorted_with, Bucket* bucket)
|
||||||
NEW_PAIR_OBJ(obj);
|
NEW_PAIR_OBJ(obj);
|
||||||
BUCKET_2_PAIR(obj, bucket);
|
bucket_to_pair(obj, bucket);
|
||||||
ZVAL_OBJ(&bucket->val, obj);
|
ZVAL_OBJ(&bucket->val, obj);
|
||||||
ZEND_HASH_FOREACH_END();
|
ZEND_HASH_FOREACH_END();
|
||||||
zend_hash_sort(sorted_with, bucket_compare_userland, 1);
|
zend_hash_sort(sorted_with, bucket_compare_userland, 1);
|
||||||
|
@ -2376,7 +2502,7 @@ PHP_METHOD(Collection, toPairs)
|
||||||
ARRAY_NEW_EX(new_collection, current);
|
ARRAY_NEW_EX(new_collection, current);
|
||||||
ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket)
|
ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket)
|
||||||
NEW_PAIR_OBJ(obj);
|
NEW_PAIR_OBJ(obj);
|
||||||
BUCKET_2_PAIR(obj, bucket);
|
bucket_to_pair(obj, bucket);
|
||||||
zval pair;
|
zval pair;
|
||||||
ZVAL_OBJ(&pair, obj);
|
ZVAL_OBJ(&pair, obj);
|
||||||
zend_hash_next_index_insert(new_collection, &pair);
|
zend_hash_next_index_insert(new_collection, &pair);
|
||||||
|
|
|
@ -25,6 +25,9 @@ extern zend_module_entry collections_module_entry;
|
||||||
#define GC_DELREF(p) --GC_REFCOUNT(p)
|
#define GC_DELREF(p) --GC_REFCOUNT(p)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define PHP_COLLECTIONS_COMPARE_NATURAL (1 << 0)
|
||||||
|
#define PHP_COLLECTIONS_FOLD_CASE (1 << 1)
|
||||||
|
|
||||||
ZEND_BEGIN_MODULE_GLOBALS(collections)
|
ZEND_BEGIN_MODULE_GLOBALS(collections)
|
||||||
zend_fcall_info* fci;
|
zend_fcall_info* fci;
|
||||||
zend_fcall_info_cache* fcc;
|
zend_fcall_info_cache* fcc;
|
||||||
|
|
|
@ -5,6 +5,17 @@
|
||||||
*/
|
*/
|
||||||
class Collection implements ArrayAccess, Countable
|
class Collection implements ArrayAccess, Countable
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Compare strings in alphabetical order, except that multi-digit numbers are ordered as a
|
||||||
|
* single character. Only used when comparing strings.
|
||||||
|
*/
|
||||||
|
const COMPARE_NATRUAL = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Elements will be campared in a case-insensitive manner. Only used when comparing strings.
|
||||||
|
*/
|
||||||
|
const FOLD_CASE = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private constructor.
|
* Private constructor.
|
||||||
* The Collection class should be initialized with static method Collection::init($data).
|
* The Collection class should be initialized with static method Collection::init($data).
|
||||||
|
@ -420,18 +431,20 @@ class Collection implements ArrayAccess, Countable
|
||||||
* Returns the largest element or null if there are no elements. The collection should contain
|
* Returns the largest element or null if there are no elements. The collection should contain
|
||||||
* only one type of numeric elements(int/double).
|
* only one type of numeric elements(int/double).
|
||||||
*
|
*
|
||||||
|
* @param int $flags[optional]
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
function max() {}
|
function max($flags) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the first element yielding the largest value of the given function or null if
|
* Returns the first element yielding the largest value of the given function or null if
|
||||||
* there are no elements.
|
* there are no elements.
|
||||||
*
|
*
|
||||||
* @param callable $selector ($value, $key) -> $new_value
|
* @param callable $selector ($value, $key) -> $new_value
|
||||||
|
* @param int $flags[optional]
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
function maxBy($selector) {}
|
function maxBy($selector, $flags) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the first element having the largest value according to the provided comparator or
|
* Returns the first element having the largest value according to the provided comparator or
|
||||||
|
@ -446,18 +459,20 @@ class Collection implements ArrayAccess, Countable
|
||||||
* Returns the largest element or null if there are no elements. The collection should contain
|
* Returns the largest element or null if there are no elements. The collection should contain
|
||||||
* only one type of numeric elements(int/double).
|
* only one type of numeric elements(int/double).
|
||||||
*
|
*
|
||||||
|
* @param int $flags[optional]
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
function min() {}
|
function min($flags) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the first element yielding the smallest value of the given function or null if
|
* Returns the first element yielding the smallest value of the given function or null if
|
||||||
* there are no elements.
|
* there are no elements.
|
||||||
*
|
*
|
||||||
* @param callable $selector ($value, $key) -> $new_value
|
* @param callable $selector ($value, $key) -> $new_value
|
||||||
|
* @param int $flags[optional]
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
function minBy($selector) {}
|
function minBy($selector, $flags) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the first element having the smallest value according to the provided comparator or
|
* Returns the first element having the smallest value according to the provided comparator or
|
||||||
|
@ -731,7 +746,7 @@ class Collection implements ArrayAccess, Countable
|
||||||
* @param int $order[optional]
|
* @param int $order[optional]
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function sort($order = SORT_REGULAR) {}
|
function sort($order) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sorts elements in the collection in-place according to the specified order of the value returned
|
* Sorts elements in the collection in-place according to the specified order of the value returned
|
||||||
|
@ -741,7 +756,7 @@ class Collection implements ArrayAccess, Countable
|
||||||
* @param int $flags[optional]
|
* @param int $flags[optional]
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function sortBy($selector, $flags = SORT_REGULAR) {}
|
function sortBy($selector, $flags) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sorts elements in the collection in-place descending according to the specified order of the
|
* Sorts elements in the collection in-place descending according to the specified order of the
|
||||||
|
@ -751,7 +766,7 @@ class Collection implements ArrayAccess, Countable
|
||||||
* @param int $flags[optional]
|
* @param int $flags[optional]
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function sortByDescending($selector, $flags = SORT_REGULAR) {}
|
function sortByDescending($selector, $flags) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sorts elements in the collection in-place descending according to the specified order.
|
* Sorts elements in the collection in-place descending according to the specified order.
|
||||||
|
@ -759,7 +774,7 @@ class Collection implements ArrayAccess, Countable
|
||||||
* @param int $flags[optional]
|
* @param int $flags[optional]
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function sortDescending($flags = SORT_REGULAR) {}
|
function sortDescending($flags) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sorts elements in the collection in-place according to the order specified with comparator.
|
* Sorts elements in the collection in-place according to the order specified with comparator.
|
||||||
|
@ -775,7 +790,7 @@ class Collection implements ArrayAccess, Countable
|
||||||
* @param int $flags[optional]
|
* @param int $flags[optional]
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
function sorted($flags = SORT_REGULAR) {}
|
function sorted($flags) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a collection of all elements sorted according to the specified order of the
|
* Returns a collection of all elements sorted according to the specified order of the
|
||||||
|
@ -785,7 +800,7 @@ class Collection implements ArrayAccess, Countable
|
||||||
* @param int $flags[optional]
|
* @param int $flags[optional]
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
function sortedBy($selector, $flags = SORT_REGULAR) {}
|
function sortedBy($selector, $flags) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a collection of all elements sorted descending according to the specified order
|
* Returns a collection of all elements sorted descending according to the specified order
|
||||||
|
@ -795,7 +810,7 @@ class Collection implements ArrayAccess, Countable
|
||||||
* @param int $flags[optional]
|
* @param int $flags[optional]
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
function sortedByDescending($selector, $flags = SORT_REGULAR) {}
|
function sortedByDescending($selector, $flags) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a collection of all elements sorted descending according to the specified order.
|
* Returns a collection of all elements sorted descending according to the specified order.
|
||||||
|
@ -804,7 +819,7 @@ class Collection implements ArrayAccess, Countable
|
||||||
* @param int $flags[optional]
|
* @param int $flags[optional]
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
function sortedDescending($selector, $flags = SORT_REGULAR) {}
|
function sortedDescending($selector, $flags) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a collection of all elements sorted according to the specified comparator.
|
* Returns a collection of all elements sorted according to the specified comparator.
|
||||||
|
|
|
@ -8,5 +8,11 @@ if ($collection->max() != max($array) || Collection::init()->max() != null)
|
||||||
echo 'Collection::max() failed.', PHP_EOL;
|
echo 'Collection::max() failed.', PHP_EOL;
|
||||||
if ($collection->min() != min($array) || Collection::init()->min() != null)
|
if ($collection->min() != min($array) || Collection::init()->min() != null)
|
||||||
echo 'Collection::min() failed.', PHP_EOL;
|
echo 'Collection::min() failed.', PHP_EOL;
|
||||||
|
$array1 = ['p3.4', 'p3.32', 'p10.2'];
|
||||||
|
$collection1 = Collection::init($array1);
|
||||||
|
if ($collection1->max() != $array1[0] || $collection1->max(Collection::COMPARE_NATRUAL) != $array1[2])
|
||||||
|
echo 'Collection::max() failed.', PHP_EOL;
|
||||||
|
if ($collection1->min() != $array1[2] || $collection1->min(Collection::COMPARE_NATRUAL) != $array1[0])
|
||||||
|
echo 'Collection::min() failed.', PHP_EOL;
|
||||||
?>
|
?>
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
|
|
|
@ -18,5 +18,21 @@ $result = $collection->minBy(function ($value) {
|
||||||
});
|
});
|
||||||
if ($result != $array[0])
|
if ($result != $array[0])
|
||||||
echo 'Collection::minBy() failed.', PHP_EOL;
|
echo 'Collection::minBy() failed.', PHP_EOL;
|
||||||
|
$array1 = [
|
||||||
|
['abc', 'ABD'],
|
||||||
|
['ACE', 'ABC'],
|
||||||
|
['acd', 'aba']
|
||||||
|
];
|
||||||
|
$collection1 = Collection::init($array1);
|
||||||
|
$result = $collection1->maxBy(function ($value) {
|
||||||
|
return $value[0];
|
||||||
|
});
|
||||||
|
if ($result != $array1[2])
|
||||||
|
echo 'Collection::maxBy() failed.', PHP_EOL;
|
||||||
|
$result = $collection1->minBy(function ($value) {
|
||||||
|
return $value[1];
|
||||||
|
}, Collection::FOLD_CASE);
|
||||||
|
if ($result != $array1[2])
|
||||||
|
echo 'Collection::minBy() failed.', PHP_EOL;
|
||||||
?>
|
?>
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
|
|
Reference in New Issue