@@ -49,7 +49,8 @@ php_array_globals array_globals;
49
49
#define EXTR_PREFIX_SAME 2
50
50
#define EXTR_PREFIX_ALL 3
51
51
52
- static unsigned char all_args_force_ref [] = { 1 , BYREF_FORCE_REST };
52
+ #define SORT_DESC -1
53
+ #define SORT_ASC 1
53
54
54
55
function_entry array_functions [] = {
55
56
PHP_FE (ksort , first_arg_force_ref )
@@ -76,7 +77,7 @@ function_entry array_functions[] = {
76
77
PHP_FE (extract , NULL )
77
78
PHP_FE (compact , NULL )
78
79
PHP_FE (range , NULL )
79
- PHP_FE (multisort , all_args_force_ref )
80
+ PHP_FE (array_multisort , NULL )
80
81
PHP_FE (array_push , first_arg_force_ref )
81
82
PHP_FE (array_pop , first_arg_force_ref )
82
83
PHP_FE (array_shift , first_arg_force_ref )
@@ -122,6 +123,9 @@ PHP_MINIT_FUNCTION(array)
122
123
REGISTER_LONG_CONSTANT ("EXTR_PREFIX_SAME" , EXTR_PREFIX_SAME , CONST_CS | CONST_PERSISTENT );
123
124
REGISTER_LONG_CONSTANT ("EXTR_PREFIX_ALL" , EXTR_PREFIX_ALL , CONST_CS | CONST_PERSISTENT );
124
125
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
+
125
129
return SUCCESS ;
126
130
}
127
131
@@ -1960,27 +1964,42 @@ int multisort_compare(const void *a, const void *b)
1960
1964
int r ;
1961
1965
int result = 0 ;
1962
1966
zval temp ;
1967
+ ARRAYLS_FETCH ();
1963
1968
1964
1969
r = 0 ;
1965
1970
do {
1966
1971
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 ;
1968
1973
if (result != 0 )
1969
1974
return result ;
1970
1975
r ++ ;
1971
1976
} while (ab [r ] != NULL );
1972
1977
return result ;
1973
1978
}
1974
1979
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 )
1976
1989
{
1977
1990
zval * * * args ;
1991
+ zval * * * arrays ;
1978
1992
Bucket * * * indirect ;
1979
1993
Bucket * p ;
1980
1994
HashTable * hash ;
1981
1995
int argc ;
1982
1996
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 ;
1983
2001
int i , k ;
2002
+ ARRAYLS_FETCH ();
1984
2003
1985
2004
/* Get the argument count and check it */
1986
2005
argc = ARG_COUNT (ht );
@@ -1995,46 +2014,89 @@ PHP_FUNCTION(multisort)
1995
2014
WRONG_PARAM_COUNT ;
1996
2015
}
1997
2016
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
+ */
1998
2027
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 ,
2001
2058
get_active_function_name ());
2002
- efree (args );
2003
- return ;
2059
+ MULTISORT_ABORT ;
2004
2060
}
2005
2061
}
2062
+ /* Take care of the last array sort flag */
2063
+ ARRAYG (multisort_flags )[num_arrays - 1 ] = sort_order ;
2006
2064
2007
2065
/* 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 ) {
2011
2069
php_error (E_WARNING , "Array sizes are inconsistent" );
2012
- efree (args );
2013
- return ;
2070
+ MULTISORT_ABORT ;
2014
2071
}
2015
2072
}
2016
2073
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
+ */
2018
2079
indirect = (Bucket * * * )emalloc (array_size * sizeof (Bucket * * ));
2019
2080
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 * ));
2021
2082
2022
- for (i = 0 ; i < argc ; i ++ ) {
2083
+ for (i = 0 ; i < num_arrays ; i ++ ) {
2023
2084
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 ++ ) {
2025
2086
indirect [k ][i ] = p ;
2026
2087
}
2027
2088
}
2028
2089
for (k = 0 ; k < array_size ; k ++ )
2029
- indirect [k ][argc ] = NULL ;
2090
+ indirect [k ][num_arrays ] = NULL ;
2030
2091
2031
- /* Do the actual sort */
2092
+ /* Do the actual sort magic - bada-bim, bada-boom */
2032
2093
qsort (indirect , array_size , sizeof (Bucket * * ), multisort_compare );
2033
2094
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. */
2035
2097
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 ;
2038
2100
hash -> pListHead = indirect [0 ][i ];;
2039
2101
hash -> pListTail = NULL ;
2040
2102
hash -> pInternalPointer = hash -> pListHead ;
@@ -2064,5 +2126,8 @@ PHP_FUNCTION(multisort)
2064
2126
for (i = 0 ; i < array_size ; i ++ )
2065
2127
efree (indirect [i ]);
2066
2128
efree (indirect );
2129
+ efree (ARRAYG (multisort_flags ));
2130
+ efree (arrays );
2067
2131
efree (args );
2132
+ RETURN_TRUE ;
2068
2133
}
0 commit comments