#define EXTR_PREFIX_SAME 2
#define EXTR_PREFIX_ALL 3
-static unsigned char all_args_force_ref[] = { 1, BYREF_FORCE_REST };
+#define SORT_DESC -1
+#define SORT_ASC 1
function_entry array_functions[] = {
PHP_FE(ksort, first_arg_force_ref)
PHP_FE(extract, NULL)
PHP_FE(compact, NULL)
PHP_FE(range, NULL)
- PHP_FE(multisort, all_args_force_ref)
+ PHP_FE(array_multisort, NULL)
PHP_FE(array_push, first_arg_force_ref)
PHP_FE(array_pop, first_arg_force_ref)
PHP_FE(array_shift, first_arg_force_ref)
REGISTER_LONG_CONSTANT("EXTR_PREFIX_SAME", EXTR_PREFIX_SAME, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("EXTR_PREFIX_ALL", EXTR_PREFIX_ALL, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("SORT_ASC", SORT_ASC, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("SORT_DESC", SORT_DESC, CONST_CS | CONST_PERSISTENT);
+
return SUCCESS;
}
int r;
int result = 0;
zval temp;
+ ARRAYLS_FETCH();
r = 0;
do {
compare_function(&temp, *((zval **)ab[r]->pData), *((zval **)bb[r]->pData));
- result = temp.value.lval;
+ result = ARRAYG(multisort_flags)[r] * temp.value.lval;
if (result != 0)
return result;
r++;
return result;
}
-PHP_FUNCTION(multisort)
+#define MULTISORT_ABORT \
+ efree(ARRAYG(multisort_flags)); \
+ efree(arrays); \
+ efree(args); \
+ RETURN_FALSE;
+
+/* {{{ proto bool array_multisort(array $ar1 [, SORT_ASC|SORT_DESC] [, array $ar2 [ICASE|NUM] [DESC|ASC], ...])
+ Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
+PHP_FUNCTION(array_multisort)
{
zval*** args;
+ zval*** arrays;
Bucket*** indirect;
Bucket* p;
HashTable* hash;
int argc;
int array_size;
+ int num_arrays = 0;
+ int parse_state = 0; /* 0 - flag not allowed
+ 1 - flag allowed */
+ int sort_order = SORT_ASC;
int i, k;
+ ARRAYLS_FETCH();
/* Get the argument count and check it */
argc = ARG_COUNT(ht);
WRONG_PARAM_COUNT;
}
+ /* Allocate space for storing pointers to input arrays and sort flags */
+ arrays = (zval ***)ecalloc(argc, sizeof(zval **));
+ ARRAYG(multisort_flags) = (int *)ecalloc(argc, sizeof(int));
+
+ /* Here we go through the input arguments and parse them. Each one can
+ be either an array or a sort order flag which follows an array. If
+ not specified, the sort order flag defaults to SORT_ASC. There can't
+ be two sort order flags in a row, and the very first argument has
+ to be an array.
+ */
for (i = 0; i < argc; i++) {
- if ((*args[i])->type != IS_ARRAY) {
- php_error(E_WARNING, "Argument %i to %s() is not an array", i+1,
+ if ((*args[i])->type == IS_ARRAY) {
+ /* We see the next array so update the sort order of
+ the previous array and reset the sort order */
+ if (i > 0) {
+ ARRAYG(multisort_flags)[num_arrays-1] = sort_order;
+ sort_order = SORT_ASC;
+ }
+ arrays[num_arrays++] = args[i];
+
+ /* next one may be array or sort flag */
+ parse_state = 1;
+ } else if ((*args[i])->type == IS_LONG) {
+ /* flag allowed here */
+ if (parse_state == 1) {
+ if ((*args[i])->value.lval == SORT_ASC || (*args[i])->value.lval == SORT_DESC) {
+ /* Save the flag and make sure then next arg is not a flag */
+ sort_order = (*args[i])->value.lval;
+ parse_state = 0;
+ } else {
+ php_error(E_WARNING, "Argument %i to %s() is an unknown sort flag", i+1,
+ get_active_function_name());
+ MULTISORT_ABORT;
+ }
+ } else {
+ php_error(E_WARNING, "Argument %i to %s() is expected to be an array", i+1,
+ get_active_function_name());
+ MULTISORT_ABORT;
+ }
+ } else {
+ php_error(E_WARNING, "Argument %i to %s() is expected to be an array or a sort flag", i+1,
get_active_function_name());
- efree(args);
- return;
+ MULTISORT_ABORT;
}
}
+ /* Take care of the last array sort flag */
+ ARRAYG(multisort_flags)[num_arrays-1] = sort_order;
/* Make sure the arrays are of the same size */
- array_size = zend_hash_num_elements((*args[0])->value.ht);
- for (i = 0; i < argc; i++) {
- if (zend_hash_num_elements((*args[i])->value.ht) != array_size) {
+ array_size = zend_hash_num_elements((*arrays[0])->value.ht);
+ for (i = 0; i < num_arrays; i++) {
+ if (zend_hash_num_elements((*arrays[i])->value.ht) != array_size) {
php_error(E_WARNING, "Array sizes are inconsistent");
- efree(args);
- return;
+ MULTISORT_ABORT;
}
}
- /* Create the indirection array */
+ /* Create the indirection array. This array is of size MxN, where
+ M is the number of entries in each input array and N is the number
+ of the input arrays + 1. The last column is NULL to indicate the end
+ of the row.
+ */
indirect = (Bucket ***)emalloc(array_size * sizeof(Bucket **));
for (i = 0; i < array_size; i++)
- indirect[i] = (Bucket **)emalloc((argc+1) * sizeof(Bucket *));
+ indirect[i] = (Bucket **)emalloc((num_arrays+1) * sizeof(Bucket *));
- for (i = 0; i < argc; i++) {
+ for (i = 0; i < num_arrays; i++) {
k = 0;
- for (p = (*args[i])->value.ht->pListHead; p; p = p->pListNext, k++) {
+ for (p = (*arrays[i])->value.ht->pListHead; p; p = p->pListNext, k++) {
indirect[k][i] = p;
}
}
for (k = 0; k < array_size; k++)
- indirect[k][argc] = NULL;
+ indirect[k][num_arrays] = NULL;
- /* Do the actual sort */
+ /* Do the actual sort magic - bada-bim, bada-boom */
qsort(indirect, array_size, sizeof(Bucket **), multisort_compare);
- /* Restructure the arrays based on sorted indirect */
+ /* Restructure the arrays based on sorted indirect - this is mostly
+ take from zend_hash_sort() function. */
HANDLE_BLOCK_INTERRUPTIONS();
- for (i = 0; i < argc; i++) {
- hash = (*args[i])->value.ht;
+ for (i = 0; i < num_arrays; i++) {
+ hash = (*arrays[i])->value.ht;
hash->pListHead = indirect[0][i];;
hash->pListTail = NULL;
hash->pInternalPointer = hash->pListHead;
for (i = 0; i < array_size; i++)
efree(indirect[i]);
efree(indirect);
+ efree(ARRAYG(multisort_flags));
+ efree(arrays);
efree(args);
+ RETURN_TRUE;
}