diff --git a/ext/standard/array.c b/ext/standard/array.c index f1b25387db060..4081340c46099 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -359,6 +359,205 @@ static int php_array_data_compare_string_locale_unstable_i(Bucket *f, Bucket *s) } /* }}} */ +static zend_always_inline int php_array_compare_strings(zend_string *s1, zend_string *s2) +{ + if (s1 == s2) { + return 0; + } + size_t len1 = ZSTR_LEN(s1); + size_t len2 = ZSTR_LEN(s2); + size_t min_len = len1 < len2 ? len1 : len2; + int cmp = memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), min_len); + if (cmp != 0) { + return cmp < 0 ? -1 : 1; + } + return ZEND_THREEWAY_COMPARE(len1, len2); +} + +static zend_always_inline int php_array_sort_compare_strict(zval *op1, zval *op2); +static int php_array_sort_compare_objects_strict(zval *o1, zval *o2); + +/* Wrapper for zend_hash_compare callback. php_array_sort_compare_strict is + * zend_always_inline for performance, so we need this addressable wrapper. */ +static int php_array_data_compare_strict_callback(zval *op1, zval *op2) +{ + return php_array_sort_compare_strict(op1, op2); +} + +static int php_array_sort_compare_symbol_tables_strict(HashTable *ht1, HashTable *ht2) +{ + if (ht1 == ht2) { + return 0; + } + + GC_TRY_ADDREF(ht1); + GC_TRY_ADDREF(ht2); + + int ret = zend_hash_compare(ht1, ht2, (compare_func_t)php_array_data_compare_strict_callback, 0); + + GC_TRY_DTOR_NO_REF(ht1); + GC_TRY_DTOR_NO_REF(ht2); + + return ret; +} + +static int php_array_sort_compare_objects_strict(zval *o1, zval *o2) +{ + ZEND_ASSERT(Z_TYPE_P(o1) == IS_OBJECT && Z_TYPE_P(o2) == IS_OBJECT); + + zend_object *zobj1 = Z_OBJ_P(o1); + zend_object *zobj2 = Z_OBJ_P(o2); + + if (zobj1->ce != zobj2->ce) { + return zobj1->ce > zobj2->ce ? 1 : -1; + } + + if (zobj1 == zobj2) { + return 0; + } + + if (zobj1->ce->ce_flags & ZEND_ACC_ENUM) { + return zobj1->handle > zobj2->handle ? 1 : -1; + } + + if (Z_OBJ_HT_P(o1)->compare && Z_OBJ_HT_P(o1)->compare != zend_std_compare_objects) { + return Z_OBJ_HT_P(o1)->compare(o1, o2); + } + + /* Compare declared properties directly when no dynamic properties exist. + * Lazy objects are excluded so they get initialized via zend_std_get_properties_ex(). */ + if (!zobj1->properties && !zobj2->properties + && !zend_object_is_lazy(zobj1) && !zend_object_is_lazy(zobj2)) { + zend_property_info *info; + int i, ret; + + if (!zobj1->ce->default_properties_count) { + return 0; + } + + if (UNEXPECTED(Z_IS_RECURSIVE_P(o1))) { + zend_throw_error(NULL, "Nesting level too deep - recursive dependency?"); + return ZEND_UNCOMPARABLE; + } + Z_PROTECT_RECURSION_P(o1); + + GC_ADDREF(zobj1); + GC_ADDREF(zobj2); + + ret = 0; + for (i = 0; i < zobj1->ce->default_properties_count; i++) { + zval *p1, *p2; + + info = zobj1->ce->properties_info_table[i]; + + if (!info) { + continue; + } + + p1 = OBJ_PROP(zobj1, info->offset); + p2 = OBJ_PROP(zobj2, info->offset); + + if (Z_TYPE_P(p1) != IS_UNDEF) { + if (Z_TYPE_P(p2) != IS_UNDEF) { + ret = php_array_sort_compare_strict(p1, p2); + if (ret != 0) { + break; + } + } else { + ret = 1; + break; + } + } else if (Z_TYPE_P(p2) != IS_UNDEF) { + ret = -1; + break; + } + } + + Z_UNPROTECT_RECURSION_P(o1); + OBJ_RELEASE(zobj1); + OBJ_RELEASE(zobj2); + return ret; + } + + /* Dynamic properties exist: compare via property hash tables */ + GC_ADDREF(zobj1); + GC_ADDREF(zobj2); + + int ret = php_array_sort_compare_symbol_tables_strict( + zend_std_get_properties_ex(zobj1), + zend_std_get_properties_ex(zobj2) + ); + + OBJ_RELEASE(zobj1); + OBJ_RELEASE(zobj2); + + return ret; +} + +static zend_always_inline int php_array_sort_compare_strict(zval *op1, zval *op2) +{ + ZVAL_DEREF(op1); + ZVAL_DEREF(op2); + + uint8_t t1 = Z_TYPE_P(op1); + uint8_t t2 = Z_TYPE_P(op2); + + if (t1 == t2) { + switch (t1) { + case IS_LONG: + return ZEND_THREEWAY_COMPARE(Z_LVAL_P(op1), Z_LVAL_P(op2)); + + case IS_STRING: + return php_array_compare_strings(Z_STR_P(op1), Z_STR_P(op2)); + + case IS_DOUBLE: + /* Per IEEE 754 totalOrder, NaN sorts after all other values */ + if (UNEXPECTED(zend_isnan(Z_DVAL_P(op1)))) { + return zend_isnan(Z_DVAL_P(op2)) ? 0 : 1; + } + if (UNEXPECTED(zend_isnan(Z_DVAL_P(op2)))) { + return -1; + } + return ZEND_THREEWAY_COMPARE(Z_DVAL_P(op1), Z_DVAL_P(op2)); + + case IS_ARRAY: + return php_array_sort_compare_symbol_tables_strict(Z_ARRVAL_P(op1), Z_ARRVAL_P(op2)); + + case IS_OBJECT: + return php_array_sort_compare_objects_strict(op1, op2); + + case IS_NULL: + case IS_FALSE: + case IS_TRUE: + return 0; + + case IS_RESOURCE: + return ZEND_THREEWAY_COMPARE(Z_RES_P(op1)->handle, Z_RES_P(op2)->handle); + + EMPTY_SWITCH_DEFAULT_CASE() + } + } + + /* Types differ: order by type hierarchy */ + return t1 > t2 ? 1 : -1; +} + +static zend_always_inline int php_array_data_compare_strict_unstable_i(Bucket *f, Bucket *s) +{ + return php_array_sort_compare_strict(&f->val, &s->val); +} + +static zend_always_inline int php_array_key_compare_strict_unstable_i(Bucket *f, Bucket *s) +{ + if (f->key == NULL && s->key == NULL) { + return ZEND_THREEWAY_COMPARE((zend_long)f->h, (zend_long)s->h); + } + if (f->key && s->key) { + return php_array_compare_strings(f->key, s->key); + } + return f->key ? 1 : -1; +} + DEFINE_SORT_VARIANTS(key_compare); DEFINE_SORT_VARIANTS(key_compare_numeric); DEFINE_SORT_VARIANTS(key_compare_string_case); @@ -371,6 +570,8 @@ DEFINE_SORT_VARIANTS(data_compare_string); DEFINE_SORT_VARIANTS(data_compare_string_locale); DEFINE_SORT_VARIANTS(natural_compare); DEFINE_SORT_VARIANTS(natural_case_compare); +DEFINE_SORT_VARIANTS(key_compare_strict); +DEFINE_SORT_VARIANTS(data_compare_strict); static bucket_compare_func_t php_get_key_compare_func(zend_long sort_type) { @@ -395,6 +596,9 @@ static bucket_compare_func_t php_get_key_compare_func(zend_long sort_type) case PHP_SORT_LOCALE_STRING: return php_array_key_compare_string_locale; + case PHP_SORT_STRICT: + return php_array_key_compare_strict; + case PHP_SORT_REGULAR: default: return php_array_key_compare; @@ -425,6 +629,9 @@ static bucket_compare_func_t php_get_key_reverse_compare_func(zend_long sort_typ case PHP_SORT_LOCALE_STRING: return php_array_reverse_key_compare_string_locale; + case PHP_SORT_STRICT: + return php_array_reverse_key_compare_strict; + case PHP_SORT_REGULAR: default: return php_array_reverse_key_compare; @@ -455,6 +662,9 @@ static bucket_compare_func_t php_get_data_compare_func(zend_long sort_type) /* { case PHP_SORT_LOCALE_STRING: return php_array_data_compare_string_locale; + case PHP_SORT_STRICT: + return php_array_data_compare_strict; + case PHP_SORT_REGULAR: default: return php_array_data_compare; @@ -485,6 +695,9 @@ static bucket_compare_func_t php_get_data_reverse_compare_func(zend_long sort_ty case PHP_SORT_LOCALE_STRING: return php_array_reverse_data_compare_string_locale; + case PHP_SORT_STRICT: + return php_array_reverse_data_compare_strict; + case PHP_SORT_REGULAR: default: return php_array_reverse_data_compare; @@ -543,6 +756,14 @@ static bucket_compare_func_t php_get_data_compare_func_unstable(zend_long sort_t } break; + case PHP_SORT_STRICT: + if (reverse) { + return php_array_reverse_data_compare_strict_unstable; + } else { + return php_array_data_compare_strict_unstable; + } + break; + case PHP_SORT_REGULAR: default: if (reverse) { @@ -6003,6 +6224,7 @@ PHP_FUNCTION(array_multisort) case PHP_SORT_STRING: case PHP_SORT_NATURAL: case PHP_SORT_LOCALE_STRING: + case PHP_SORT_STRICT: /* flag allowed here */ if (parse_state[MULTISORT_TYPE] == 1) { /* Save the flag and make sure then next arg is not the current flag. */ diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 7913ca0e00194..d26090ab5d951 100644 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -81,6 +81,11 @@ * @cvalue PHP_SORT_NATURAL */ const SORT_NATURAL = UNKNOWN; +/** + * @var int + * @cvalue PHP_SORT_STRICT + */ +const SORT_STRICT = UNKNOWN; /** * @var int * @cvalue PHP_SORT_FLAG_CASE diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 0a21d7d76426c..13a9ca30ce3ff 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f6bf6cdd07080c01d3a0cb08d71409d05b1084f9 */ + * Stub hash: bd2d16b9bd372e1996fed88a4185ed7ed8800663 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) @@ -3511,6 +3511,7 @@ static void register_basic_functions_symbols(int module_number) REGISTER_LONG_CONSTANT("SORT_STRING", PHP_SORT_STRING, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SORT_LOCALE_STRING", PHP_SORT_LOCALE_STRING, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SORT_NATURAL", PHP_SORT_NATURAL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SORT_STRICT", PHP_SORT_STRICT, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SORT_FLAG_CASE", PHP_SORT_FLAG_CASE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("CASE_LOWER", PHP_CASE_LOWER, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("CASE_UPPER", PHP_CASE_UPPER, CONST_PERSISTENT); diff --git a/ext/standard/php_array.h b/ext/standard/php_array.h index 2a35af6038083..037dcee494a51 100644 --- a/ext/standard/php_array.h +++ b/ext/standard/php_array.h @@ -54,6 +54,7 @@ PHPAPI bool php_array_pick_keys(php_random_algo_with_state engine, zval *input, #define PHP_SORT_ASC 4 #define PHP_SORT_LOCALE_STRING 5 #define PHP_SORT_NATURAL 6 +#define PHP_SORT_STRICT 7 #define PHP_SORT_FLAG_CASE 8 #define PHP_COUNT_NORMAL 0 diff --git a/ext/standard/tests/array/array_unique_variation10.phpt b/ext/standard/tests/array/array_unique_variation10.phpt new file mode 100644 index 0000000000000..d3a3cd13a4799 --- /dev/null +++ b/ext/standard/tests/array/array_unique_variation10.phpt @@ -0,0 +1,78 @@ +--TEST-- +Test array_unique() function : usage variations - SORT_STRICT flag preserves type-distinct values +--FILE-- + +--EXPECT-- +*** Testing array_unique() : SORT_STRICT flag *** + +-- Type-distinct values preserved with SORT_STRICT -- +array(5) { + [0]=> + int(0) + [1]=> + string(1) "0" + [2]=> + NULL + [3]=> + bool(false) + [4]=> + string(0) "" +} + +-- Same-type duplicates are still removed -- +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [3]=> + int(3) +} + +-- String duplicates removed -- +array(3) { + [0]=> + string(1) "a" + [1]=> + string(1) "b" + [3]=> + string(1) "c" +} + +-- Integer 1 and string "1" are different with SORT_STRICT -- +array(2) { + [0]=> + int(1) + [1]=> + string(1) "1" +} +Done diff --git a/ext/standard/tests/array/sort/array_multisort_strict.phpt b/ext/standard/tests/array/sort/array_multisort_strict.phpt new file mode 100644 index 0000000000000..416db725bf938 --- /dev/null +++ b/ext/standard/tests/array/sort/array_multisort_strict.phpt @@ -0,0 +1,67 @@ +--TEST-- +Test array_multisort() function : strict type sorting +--FILE-- + +--EXPECT-- +*** Testing array_multisort() : strict type sorting +array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + string(1) "1" + [3]=> + string(1) "2" +} +array(4) { + [0]=> + string(1) "a" + [1]=> + string(1) "c" + [2]=> + string(1) "b" + [3]=> + string(1) "d" +} + +*** Testing array_multisort() : strict type sorting descending +array(4) { + [0]=> + string(1) "2" + [1]=> + string(1) "1" + [2]=> + int(2) + [3]=> + int(1) +} +array(4) { + [0]=> + string(1) "d" + [1]=> + string(1) "b" + [2]=> + string(1) "c" + [3]=> + string(1) "a" +} diff --git a/ext/standard/tests/array/sort/arsort_basic.phpt b/ext/standard/tests/array/sort/arsort_basic.phpt index c636a6278457c..c45aa2a725e88 100644 --- a/ext/standard/tests/array/sort/arsort_basic.phpt +++ b/ext/standard/tests/array/sort/arsort_basic.phpt @@ -9,6 +9,7 @@ Test arsort() function : basic functionality * SORT_REGULAR - compare items normally * SORT_NUMERIC - compare items numerically * SORT_STRING - compare items as strings + * SORT_STRICT - compare items by type first, then by value */ echo "*** Testing arsort() : basic functionality ***\n"; @@ -67,6 +68,21 @@ $temp_array = $unsorted_numerics; var_dump( arsort($temp_array, SORT_NUMERIC) ); // expecting : bool(true) var_dump( $temp_array); +echo "\n-- Testing arsort() by supplying mixed type array, 'flag' = SORT_STRICT --\n"; +$mixed_types = array( "a" => 1, "b" => "1", "c" => 2, "d" => "2", "e" => true, "f" => false, "g" => null ); +var_dump( arsort($mixed_types, SORT_STRICT) ); // expecting : bool(true) +var_dump( $mixed_types); + +echo "\n-- Testing arsort() by supplying string array, 'flag' = SORT_STRICT --\n"; +$temp_array = $unsorted_strings; +var_dump( arsort($temp_array, SORT_STRICT) ); // expecting : bool(true) +var_dump( $temp_array); + +echo "\n-- Testing arsort() by supplying numeric array, 'flag' = SORT_STRICT --\n"; +$temp_array = $unsorted_numerics; +var_dump( arsort($temp_array, SORT_STRICT) ); // expecting : bool(true) +var_dump( $temp_array); + echo "Done\n"; ?> --EXPECT-- @@ -236,4 +252,57 @@ array(4) { [4]=> int(22) } + +-- Testing arsort() by supplying mixed type array, 'flag' = SORT_STRICT -- +bool(true) +array(7) { + ["d"]=> + string(1) "2" + ["b"]=> + string(1) "1" + ["c"]=> + int(2) + ["a"]=> + int(1) + ["e"]=> + bool(true) + ["f"]=> + bool(false) + ["g"]=> + NULL +} + +-- Testing arsort() by supplying string array, 'flag' = SORT_STRICT -- +bool(true) +array(8) { + ["o20"]=> + string(8) "orange20" + ["o2"]=> + string(7) "orange2" + ["o"]=> + string(6) "orange" + ["l"]=> + string(5) "lemon" + ["b"]=> + string(6) "banana" + ["O3"]=> + string(7) "Orange3" + ["O1"]=> + string(7) "Orange1" + ["O"]=> + string(6) "Orange" +} + +-- Testing arsort() by supplying numeric array, 'flag' = SORT_STRICT -- +bool(true) +array(4) { + [3]=> + int(555) + [1]=> + int(100) + [2]=> + int(33) + [4]=> + int(22) +} Done diff --git a/ext/standard/tests/array/sort/asort_basic.phpt b/ext/standard/tests/array/sort/asort_basic.phpt index 13f03a92ef6e0..b54c721a019a0 100644 --- a/ext/standard/tests/array/sort/asort_basic.phpt +++ b/ext/standard/tests/array/sort/asort_basic.phpt @@ -9,6 +9,7 @@ Test asort() function : basic functionality * SORT_REGULAR - compare items normally * SORT_NUMERIC - compare items numerically * SORT_STRING - compare items as strings + * SORT_STRICT - compare items by type first, then by value */ echo "*** Testing asort() : basic functionality ***\n"; @@ -67,6 +68,21 @@ $temp_array = $unsorted_numerics; var_dump( asort($temp_array, SORT_NUMERIC) ); // expecting : bool(true) var_dump( $temp_array); +echo "\n-- Testing asort() by supplying mixed type array, 'flag' = SORT_STRICT --\n"; +$mixed_types = array( "a" => 1, "b" => "1", "c" => 2, "d" => "2", "e" => true, "f" => false, "g" => null ); +var_dump( asort($mixed_types, SORT_STRICT) ); // expecting : bool(true) +var_dump( $mixed_types); + +echo "\n-- Testing asort() by supplying string array, 'flag' = SORT_STRICT --\n"; +$temp_array = $unsorted_strings; +var_dump( asort($temp_array, SORT_STRICT) ); // expecting : bool(true) +var_dump( $temp_array); + +echo "\n-- Testing asort() by supplying numeric array, 'flag' = SORT_STRICT --\n"; +$temp_array = $unsorted_numerics; +var_dump( asort($temp_array, SORT_STRICT) ); // expecting : bool(true) +var_dump( $temp_array); + echo "Done\n"; ?> --EXPECT-- @@ -236,4 +252,57 @@ array(4) { [3]=> int(555) } + +-- Testing asort() by supplying mixed type array, 'flag' = SORT_STRICT -- +bool(true) +array(7) { + ["g"]=> + NULL + ["f"]=> + bool(false) + ["e"]=> + bool(true) + ["a"]=> + int(1) + ["c"]=> + int(2) + ["b"]=> + string(1) "1" + ["d"]=> + string(1) "2" +} + +-- Testing asort() by supplying string array, 'flag' = SORT_STRICT -- +bool(true) +array(8) { + ["O"]=> + string(6) "Orange" + ["O1"]=> + string(7) "Orange1" + ["O3"]=> + string(7) "Orange3" + ["b"]=> + string(6) "banana" + ["l"]=> + string(5) "lemon" + ["o"]=> + string(6) "orange" + ["o2"]=> + string(7) "orange2" + ["o20"]=> + string(8) "orange20" +} + +-- Testing asort() by supplying numeric array, 'flag' = SORT_STRICT -- +bool(true) +array(4) { + [4]=> + int(22) + [2]=> + int(33) + [1]=> + int(100) + [3]=> + int(555) +} Done diff --git a/ext/standard/tests/array/sort/krsort_basic.phpt b/ext/standard/tests/array/sort/krsort_basic.phpt index 6137e1b24354a..364737c534677 100644 --- a/ext/standard/tests/array/sort/krsort_basic.phpt +++ b/ext/standard/tests/array/sort/krsort_basic.phpt @@ -9,6 +9,7 @@ Test krsort() function : basic functionality * 2.SORT_REGULAR - compare items normally * 3.SORT_NUMERIC - compare items numerically * 4.SORT_STRING - compare items as strings + * 5.SORT_STRICT - compare items by type first, then by value */ echo "*** Testing krsort() : basic functionality ***\n"; @@ -68,6 +69,21 @@ $temp_array = $unsorted_numerics; var_dump( krsort($temp_array, SORT_NUMERIC) ); // expecting : bool(true) var_dump( $temp_array); +echo "\n-- Testing krsort() by supplying mixed key array, 'flag' = SORT_STRICT --\n"; +$mixed_keys = array( 1 => "int1", "1a" => "str1a", 2 => "int2", "2b" => "str2b" ); +var_dump( krsort($mixed_keys, SORT_STRICT) ); // expecting : bool(true) +var_dump( $mixed_keys); + +echo "\n-- Testing krsort() by supplying string array, 'flag' = SORT_STRICT --\n"; +$temp_array = $unsorted_strings; +var_dump( krsort($temp_array, SORT_STRICT) ); // expecting : bool(true) +var_dump( $temp_array); + +echo "\n-- Testing krsort() by supplying numeric array, 'flag' = SORT_STRICT --\n"; +$temp_array = $unsorted_numerics; +var_dump( krsort($temp_array, SORT_STRICT) ); // expecting : bool(true) +var_dump( $temp_array); + echo "Done\n"; ?> --EXPECT-- @@ -237,4 +253,51 @@ array(4) { [22]=> int(1) } + +-- Testing krsort() by supplying mixed key array, 'flag' = SORT_STRICT -- +bool(true) +array(4) { + ["2b"]=> + string(5) "str2b" + ["1a"]=> + string(5) "str1a" + [2]=> + string(4) "int2" + [1]=> + string(4) "int1" +} + +-- Testing krsort() by supplying string array, 'flag' = SORT_STRICT -- +bool(true) +array(8) { + ["o20"]=> + string(8) "orange20" + ["o2"]=> + string(7) "orange2" + ["o"]=> + string(6) "orange" + ["l"]=> + string(5) "lemon" + ["b"]=> + string(6) "banana" + ["O3"]=> + string(7) "Orange3" + ["O1"]=> + string(7) "Orange1" + ["O"]=> + string(6) "Orange" +} + +-- Testing krsort() by supplying numeric array, 'flag' = SORT_STRICT -- +bool(true) +array(4) { + [555]=> + int(2) + [100]=> + int(4) + [33]=> + int(3) + [22]=> + int(1) +} Done diff --git a/ext/standard/tests/array/sort/ksort_basic.phpt b/ext/standard/tests/array/sort/ksort_basic.phpt index 96135981f3bfa..4c72eed6b1896 100644 --- a/ext/standard/tests/array/sort/ksort_basic.phpt +++ b/ext/standard/tests/array/sort/ksort_basic.phpt @@ -8,6 +8,7 @@ Test ksort() function : basic functionality * 2.SORT_REGULAR - compare items normally * 3.SORT_NUMERIC - compare items numerically * 4.SORT_STRING - compare items as strings + * 5.SORT_STRICT - compare items by type first, then by value */ echo "*** Testing ksort() : basic functionality ***\n"; @@ -66,6 +67,21 @@ $temp_array = $unsorted_numerics; var_dump( ksort($temp_array, SORT_NUMERIC) ); // expecting : bool(true) var_dump( $temp_array); +echo "\n-- Testing ksort() by supplying mixed key array, 'flag' = SORT_STRICT --\n"; +$mixed_keys = array( 1 => "int1", "1a" => "str1a", 2 => "int2", "2b" => "str2b" ); +var_dump( ksort($mixed_keys, SORT_STRICT) ); // expecting : bool(true) +var_dump( $mixed_keys); + +echo "\n-- Testing ksort() by supplying string array, 'flag' = SORT_STRICT --\n"; +$temp_array = $unsorted_strings; +var_dump( ksort($temp_array, SORT_STRICT) ); // expecting : bool(true) +var_dump( $temp_array); + +echo "\n-- Testing ksort() by supplying numeric array, 'flag' = SORT_STRICT --\n"; +$temp_array = $unsorted_numerics; +var_dump( ksort($temp_array, SORT_STRICT) ); // expecting : bool(true) +var_dump( $temp_array); + echo "Done\n"; ?> --EXPECT-- @@ -235,4 +251,51 @@ array(4) { [555]=> int(2) } + +-- Testing ksort() by supplying mixed key array, 'flag' = SORT_STRICT -- +bool(true) +array(4) { + [1]=> + string(4) "int1" + [2]=> + string(4) "int2" + ["1a"]=> + string(5) "str1a" + ["2b"]=> + string(5) "str2b" +} + +-- Testing ksort() by supplying string array, 'flag' = SORT_STRICT -- +bool(true) +array(8) { + ["O"]=> + string(6) "Orange" + ["O1"]=> + string(7) "Orange1" + ["O3"]=> + string(7) "Orange3" + ["b"]=> + string(6) "banana" + ["l"]=> + string(5) "lemon" + ["o"]=> + string(6) "orange" + ["o2"]=> + string(7) "orange2" + ["o20"]=> + string(8) "orange20" +} + +-- Testing ksort() by supplying numeric array, 'flag' = SORT_STRICT -- +bool(true) +array(4) { + [22]=> + int(1) + [33]=> + int(3) + [100]=> + int(4) + [555]=> + int(2) +} Done diff --git a/ext/standard/tests/array/sort/rsort_basic.phpt b/ext/standard/tests/array/sort/rsort_basic.phpt index a7ad5f3790b49..a0f68ca18aa08 100644 --- a/ext/standard/tests/array/sort/rsort_basic.phpt +++ b/ext/standard/tests/array/sort/rsort_basic.phpt @@ -63,6 +63,21 @@ $temp_array = $unsorted_numerics; var_dump( rsort($temp_array, SORT_NUMERIC) ); var_dump( $temp_array); +echo "\n-- Testing rsort() by supplying mixed type array, 'flag' = SORT_STRICT --\n"; +$mixed_types = array( 1, "1", 2, "2", true, false, null ); +var_dump( rsort($mixed_types, SORT_STRICT) ); +var_dump( $mixed_types); + +echo "\n-- Testing rsort() by supplying string array, 'flag' = SORT_STRICT --\n"; +$temp_array = $unsorted_strings; +var_dump( rsort($temp_array, SORT_STRICT) ); +var_dump( $temp_array); + +echo "\n-- Testing rsort() by supplying numeric array, 'flag' = SORT_STRICT --\n"; +$temp_array = $unsorted_numerics; +var_dump( rsort($temp_array, SORT_STRICT) ); +var_dump( $temp_array); + echo "Done"; ?> --EXPECT-- @@ -232,4 +247,57 @@ array(4) { [3]=> int(22) } + +-- Testing rsort() by supplying mixed type array, 'flag' = SORT_STRICT -- +bool(true) +array(7) { + [0]=> + string(1) "2" + [1]=> + string(1) "1" + [2]=> + int(2) + [3]=> + int(1) + [4]=> + bool(true) + [5]=> + bool(false) + [6]=> + NULL +} + +-- Testing rsort() by supplying string array, 'flag' = SORT_STRICT -- +bool(true) +array(8) { + [0]=> + string(8) "orange20" + [1]=> + string(7) "orange2" + [2]=> + string(6) "orange" + [3]=> + string(5) "lemon" + [4]=> + string(6) "banana" + [5]=> + string(7) "Orange3" + [6]=> + string(7) "Orange1" + [7]=> + string(6) "Orange" +} + +-- Testing rsort() by supplying numeric array, 'flag' = SORT_STRICT -- +bool(true) +array(4) { + [0]=> + int(555) + [1]=> + int(100) + [2]=> + int(33) + [3]=> + int(22) +} Done diff --git a/ext/standard/tests/array/sort/sort_basic.phpt b/ext/standard/tests/array/sort/sort_basic.phpt index 8f50c6e1be5f0..be5d79680ca02 100644 --- a/ext/standard/tests/array/sort/sort_basic.phpt +++ b/ext/standard/tests/array/sort/sort_basic.phpt @@ -9,6 +9,7 @@ Test sort() function : basic functionality * SORT_REGULAR - compare items normally * SORT_NUMERIC - compare items numerically * SORT_STRING - compare items as strings + * SORT_STRICT - compare items by type first, then by value */ echo "*** Testing sort() : basic functionality ***\n"; @@ -68,6 +69,21 @@ $temp_array = $unsorted_numerics; var_dump( sort($temp_array, SORT_NUMERIC) ); // expecting : bool(true) var_dump( $temp_array); +echo "\n-- Testing sort() by supplying mixed type array, 'flag' = SORT_STRICT --\n"; +$mixed_types = array( 1, "1", 2, "2", true, false, null ); +var_dump( sort($mixed_types, SORT_STRICT) ); // expecting : bool(true) +var_dump( $mixed_types); + +echo "\n-- Testing sort() by supplying string array, 'flag' = SORT_STRICT --\n"; +$temp_array = $unsorted_strings; +var_dump( sort($temp_array, SORT_STRICT) ); // expecting : bool(true) +var_dump( $temp_array); + +echo "\n-- Testing sort() by supplying numeric array, 'flag' = SORT_STRICT --\n"; +$temp_array = $unsorted_numerics; +var_dump( sort($temp_array, SORT_STRICT) ); // expecting : bool(true) +var_dump( $temp_array); + echo "Done\n"; ?> --EXPECT-- @@ -237,4 +253,57 @@ array(4) { [3]=> int(555) } + +-- Testing sort() by supplying mixed type array, 'flag' = SORT_STRICT -- +bool(true) +array(7) { + [0]=> + NULL + [1]=> + bool(false) + [2]=> + bool(true) + [3]=> + int(1) + [4]=> + int(2) + [5]=> + string(1) "1" + [6]=> + string(1) "2" +} + +-- Testing sort() by supplying string array, 'flag' = SORT_STRICT -- +bool(true) +array(8) { + [0]=> + string(6) "Orange" + [1]=> + string(7) "Orange1" + [2]=> + string(7) "Orange3" + [3]=> + string(6) "banana" + [4]=> + string(5) "lemon" + [5]=> + string(6) "orange" + [6]=> + string(7) "orange2" + [7]=> + string(8) "orange20" +} + +-- Testing sort() by supplying numeric array, 'flag' = SORT_STRICT -- +bool(true) +array(4) { + [0]=> + int(22) + [1]=> + int(33) + [2]=> + int(100) + [3]=> + int(555) +} Done diff --git a/ext/standard/tests/array/sort/sort_variation12.phpt b/ext/standard/tests/array/sort/sort_variation12.phpt new file mode 100644 index 0000000000000..af0e6bfba0e73 --- /dev/null +++ b/ext/standard/tests/array/sort/sort_variation12.phpt @@ -0,0 +1,211 @@ +--TEST-- +Test sort() function : usage variations - SORT_STRICT total ordering, NaN handling, and recursive comparison +--FILE-- + 1], +]; +sort($values, SORT_STRICT); +foreach ($values as $v) { + echo " " . gettype($v); + if (is_bool($v)) { + echo " (" . ($v ? "true" : "false") . ")"; + } elseif (is_scalar($v)) { + echo " (" . var_export($v, true) . ")"; + } + echo "\n"; +} +fclose($resource); + +echo "\n-- NaN sorts after all other floats (IEEE 754 totalOrder) --\n"; +$values = [1.0, NAN, -INF, INF, 0.0, NAN, -1.0]; +sort($values, SORT_STRICT); +foreach ($values as $v) { + if (is_nan($v)) { + echo " NAN\n"; + } else { + echo " " . $v . "\n"; + } +} + +echo "\n-- Recursive array comparison --\n"; +$values = [ + [1, [2, 3]], + [1, [2, 2]], + [1, [2, 4]], +]; +sort($values, SORT_STRICT); +var_dump($values); + +echo "\n-- Nested arrays with mixed types compared recursively --\n"; +$values = [ + ["a" => 1], + ["a" => "1"], + ["a" => true], +]; +sort($values, SORT_STRICT); +var_dump($values); + +echo "\n-- Object property comparison --\n"; +class TestClass { + public $x; + public $y; + public function __construct($x, $y) { + $this->x = $x; + $this->y = $y; + } +} +$values = [ + new TestClass(1, 2), + new TestClass(1, 1), + new TestClass(1, 3), +]; +sort($values, SORT_STRICT); +foreach ($values as $obj) { + echo " TestClass(x={$obj->x}, y={$obj->y})\n"; +} + +echo "\n-- Objects with different property types --\n"; +$values = [ + new TestClass(1, "1"), + new TestClass(1, 1), + new TestClass(1, true), +]; +sort($values, SORT_STRICT); +foreach ($values as $obj) { + $yType = gettype($obj->y); + $yVal = var_export($obj->y, true); + echo " TestClass(x={$obj->x}, y=$yVal [$yType])\n"; +} + +echo "\n-- Different classes are compared by properties --\n"; +class ClassA { + public $val = 1; +} +class ClassB { + public $val = 2; +} +$values = [new ClassB(), new ClassA()]; +sort($values, SORT_STRICT); +foreach ($values as $obj) { + echo " " . get_class($obj) . "(val={$obj->val})\n"; +} + +echo "Done"; +?> +--EXPECTF-- +*** Testing sort() : SORT_STRICT variations *** + +-- Complete type hierarchy: null < bool < int < float < string < array < object < resource -- + NULL + boolean (false) + boolean (true) + integer (42) + double (3.14) + string ('string') + array + object + resource + +-- NaN sorts after all other floats (IEEE 754 totalOrder) -- + -INF + -1 + 0 + 1 + INF + NAN + NAN + +-- Recursive array comparison -- +array(3) { + [0]=> + array(2) { + [0]=> + int(1) + [1]=> + array(2) { + [0]=> + int(2) + [1]=> + int(2) + } + } + [1]=> + array(2) { + [0]=> + int(1) + [1]=> + array(2) { + [0]=> + int(2) + [1]=> + int(3) + } + } + [2]=> + array(2) { + [0]=> + int(1) + [1]=> + array(2) { + [0]=> + int(2) + [1]=> + int(4) + } + } +} + +-- Nested arrays with mixed types compared recursively -- +array(3) { + [0]=> + array(1) { + ["a"]=> + bool(true) + } + [1]=> + array(1) { + ["a"]=> + int(1) + } + [2]=> + array(1) { + ["a"]=> + string(1) "1" + } +} + +-- Object property comparison -- + TestClass(x=1, y=1) + TestClass(x=1, y=2) + TestClass(x=1, y=3) + +-- Objects with different property types -- + TestClass(x=1, y=true [boolean]) + TestClass(x=1, y=1 [integer]) + TestClass(x=1, y='1' [string]) + +-- Different classes are compared by properties -- + ClassA(val=1) + ClassB(val=2) +Done