Skip to content

Commit 457a13d

Browse files
author
Andrei Zmievski
committed
Finished array_multisort() function. Basically it sorts multiple arrays similar to ORDER BY SQL clause. @ Added array_multisort() function. (Andrei) # Docs are coming soon
1 parent e911ee3 commit 457a13d

File tree

3 files changed

+88
-24
lines changed

3 files changed

+88
-24
lines changed

TODO

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ ext/oci8
2424
ext/standard
2525
------------
2626
* strpad() (Andrei)
27-
* advanced sort (Andrei)
2827
* NOT binary safe:
2928
strcspn()
3029
strtok()

ext/standard/array.c

Lines changed: 87 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ php_array_globals array_globals;
4949
#define EXTR_PREFIX_SAME 2
5050
#define EXTR_PREFIX_ALL 3
5151

52-
static unsigned char all_args_force_ref[] = { 1, BYREF_FORCE_REST };
52+
#define SORT_DESC -1
53+
#define SORT_ASC 1
5354

5455
function_entry array_functions[] = {
5556
PHP_FE(ksort, first_arg_force_ref)
@@ -76,7 +77,7 @@ function_entry array_functions[] = {
7677
PHP_FE(extract, NULL)
7778
PHP_FE(compact, NULL)
7879
PHP_FE(range, NULL)
79-
PHP_FE(multisort, all_args_force_ref)
80+
PHP_FE(array_multisort, NULL)
8081
PHP_FE(array_push, first_arg_force_ref)
8182
PHP_FE(array_pop, first_arg_force_ref)
8283
PHP_FE(array_shift, first_arg_force_ref)
@@ -122,6 +123,9 @@ PHP_MINIT_FUNCTION(array)
122123
REGISTER_LONG_CONSTANT("EXTR_PREFIX_SAME", EXTR_PREFIX_SAME, CONST_CS | CONST_PERSISTENT);
123124
REGISTER_LONG_CONSTANT("EXTR_PREFIX_ALL", EXTR_PREFIX_ALL, CONST_CS | CONST_PERSISTENT);
124125

126+
REGISTER_LONG_CONSTANT("SORT_ASC", SORT_ASC, CONST_CS | CONST_PERSISTENT);
127+
REGISTER_LONG_CONSTANT("SORT_DESC", SORT_DESC, CONST_CS | CONST_PERSISTENT);
128+
125129
return SUCCESS;
126130
}
127131

@@ -1960,27 +1964,42 @@ int multisort_compare(const void *a, const void *b)
19601964
int r;
19611965
int result = 0;
19621966
zval temp;
1967+
ARRAYLS_FETCH();
19631968

19641969
r = 0;
19651970
do {
19661971
compare_function(&temp, *((zval **)ab[r]->pData), *((zval **)bb[r]->pData));
1967-
result = temp.value.lval;
1972+
result = ARRAYG(multisort_flags)[r] * temp.value.lval;
19681973
if (result != 0)
19691974
return result;
19701975
r++;
19711976
} while (ab[r] != NULL);
19721977
return result;
19731978
}
19741979

1975-
PHP_FUNCTION(multisort)
1980+
#define MULTISORT_ABORT \
1981+
efree(ARRAYG(multisort_flags)); \
1982+
efree(arrays); \
1983+
efree(args); \
1984+
RETURN_FALSE;
1985+
1986+
/* {{{ proto bool array_multisort(array $ar1 [, SORT_ASC|SORT_DESC] [, array $ar2 [ICASE|NUM] [DESC|ASC], ...])
1987+
Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
1988+
PHP_FUNCTION(array_multisort)
19761989
{
19771990
zval*** args;
1991+
zval*** arrays;
19781992
Bucket*** indirect;
19791993
Bucket* p;
19801994
HashTable* hash;
19811995
int argc;
19821996
int array_size;
1997+
int num_arrays = 0;
1998+
int parse_state = 0; /* 0 - flag not allowed
1999+
1 - flag allowed */
2000+
int sort_order = SORT_ASC;
19832001
int i, k;
2002+
ARRAYLS_FETCH();
19842003

19852004
/* Get the argument count and check it */
19862005
argc = ARG_COUNT(ht);
@@ -1995,46 +2014,89 @@ PHP_FUNCTION(multisort)
19952014
WRONG_PARAM_COUNT;
19962015
}
19972016

2017+
/* Allocate space for storing pointers to input arrays and sort flags */
2018+
arrays = (zval ***)ecalloc(argc, sizeof(zval **));
2019+
ARRAYG(multisort_flags) = (int *)ecalloc(argc, sizeof(int));
2020+
2021+
/* Here we go through the input arguments and parse them. Each one can
2022+
be either an array or a sort order flag which follows an array. If
2023+
not specified, the sort order flag defaults to SORT_ASC. There can't
2024+
be two sort order flags in a row, and the very first argument has
2025+
to be an array.
2026+
*/
19982027
for (i = 0; i < argc; i++) {
1999-
if ((*args[i])->type != IS_ARRAY) {
2000-
php_error(E_WARNING, "Argument %i to %s() is not an array", i+1,
2028+
if ((*args[i])->type == IS_ARRAY) {
2029+
/* We see the next array so update the sort order of
2030+
the previous array and reset the sort order */
2031+
if (i > 0) {
2032+
ARRAYG(multisort_flags)[num_arrays-1] = sort_order;
2033+
sort_order = SORT_ASC;
2034+
}
2035+
arrays[num_arrays++] = args[i];
2036+
2037+
/* next one may be array or sort flag */
2038+
parse_state = 1;
2039+
} else if ((*args[i])->type == IS_LONG) {
2040+
/* flag allowed here */
2041+
if (parse_state == 1) {
2042+
if ((*args[i])->value.lval == SORT_ASC || (*args[i])->value.lval == SORT_DESC) {
2043+
/* Save the flag and make sure then next arg is not a flag */
2044+
sort_order = (*args[i])->value.lval;
2045+
parse_state = 0;
2046+
} else {
2047+
php_error(E_WARNING, "Argument %i to %s() is an unknown sort flag", i+1,
2048+
get_active_function_name());
2049+
MULTISORT_ABORT;
2050+
}
2051+
} else {
2052+
php_error(E_WARNING, "Argument %i to %s() is expected to be an array", i+1,
2053+
get_active_function_name());
2054+
MULTISORT_ABORT;
2055+
}
2056+
} else {
2057+
php_error(E_WARNING, "Argument %i to %s() is expected to be an array or a sort flag", i+1,
20012058
get_active_function_name());
2002-
efree(args);
2003-
return;
2059+
MULTISORT_ABORT;
20042060
}
20052061
}
2062+
/* Take care of the last array sort flag */
2063+
ARRAYG(multisort_flags)[num_arrays-1] = sort_order;
20062064

20072065
/* Make sure the arrays are of the same size */
2008-
array_size = zend_hash_num_elements((*args[0])->value.ht);
2009-
for (i = 0; i < argc; i++) {
2010-
if (zend_hash_num_elements((*args[i])->value.ht) != array_size) {
2066+
array_size = zend_hash_num_elements((*arrays[0])->value.ht);
2067+
for (i = 0; i < num_arrays; i++) {
2068+
if (zend_hash_num_elements((*arrays[i])->value.ht) != array_size) {
20112069
php_error(E_WARNING, "Array sizes are inconsistent");
2012-
efree(args);
2013-
return;
2070+
MULTISORT_ABORT;
20142071
}
20152072
}
20162073

2017-
/* Create the indirection array */
2074+
/* Create the indirection array. This array is of size MxN, where
2075+
M is the number of entries in each input array and N is the number
2076+
of the input arrays + 1. The last column is NULL to indicate the end
2077+
of the row.
2078+
*/
20182079
indirect = (Bucket ***)emalloc(array_size * sizeof(Bucket **));
20192080
for (i = 0; i < array_size; i++)
2020-
indirect[i] = (Bucket **)emalloc((argc+1) * sizeof(Bucket *));
2081+
indirect[i] = (Bucket **)emalloc((num_arrays+1) * sizeof(Bucket *));
20212082

2022-
for (i = 0; i < argc; i++) {
2083+
for (i = 0; i < num_arrays; i++) {
20232084
k = 0;
2024-
for (p = (*args[i])->value.ht->pListHead; p; p = p->pListNext, k++) {
2085+
for (p = (*arrays[i])->value.ht->pListHead; p; p = p->pListNext, k++) {
20252086
indirect[k][i] = p;
20262087
}
20272088
}
20282089
for (k = 0; k < array_size; k++)
2029-
indirect[k][argc] = NULL;
2090+
indirect[k][num_arrays] = NULL;
20302091

2031-
/* Do the actual sort */
2092+
/* Do the actual sort magic - bada-bim, bada-boom */
20322093
qsort(indirect, array_size, sizeof(Bucket **), multisort_compare);
20332094

2034-
/* Restructure the arrays based on sorted indirect */
2095+
/* Restructure the arrays based on sorted indirect - this is mostly
2096+
take from zend_hash_sort() function. */
20352097
HANDLE_BLOCK_INTERRUPTIONS();
2036-
for (i = 0; i < argc; i++) {
2037-
hash = (*args[i])->value.ht;
2098+
for (i = 0; i < num_arrays; i++) {
2099+
hash = (*arrays[i])->value.ht;
20382100
hash->pListHead = indirect[0][i];;
20392101
hash->pListTail = NULL;
20402102
hash->pInternalPointer = hash->pListHead;
@@ -2064,5 +2126,8 @@ PHP_FUNCTION(multisort)
20642126
for (i = 0; i < array_size; i++)
20652127
efree(indirect[i]);
20662128
efree(indirect);
2129+
efree(ARRAYG(multisort_flags));
2130+
efree(arrays);
20672131
efree(args);
2132+
RETURN_TRUE;
20682133
}

ext/standard/php_array.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ PHP_FUNCTION(extract);
6262
PHP_FUNCTION(compact);
6363
PHP_FUNCTION(range);
6464
PHP_FUNCTION(shuffle);
65-
PHP_FUNCTION(multisort);
65+
PHP_FUNCTION(array_multisort);
6666
PHP_FUNCTION(array_push);
6767
PHP_FUNCTION(array_pop);
6868
PHP_FUNCTION(array_shift);

0 commit comments

Comments
 (0)