diff --git a/ext/standard/array.c b/ext/standard/array.c index c3d4c3d7a78a3..f3a0695ee0945 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -4337,6 +4337,72 @@ PHP_FUNCTION(array_column) } /* }}} */ +/* {{{ Return all the values from a single column in the input array, identified by the + value_key and optionally indexed by the index_key in an indexed array */ +PHP_FUNCTION(array_group) +{ + HashTable *input; + zval *colval, *data, rv; + zend_string *column_str = NULL; + zend_long column_long; + bool column_is_null = 0; + zend_string *index_str = NULL; + zend_long index_long; + bool index_is_null = 1; + + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_ARRAY_HT(input) + Z_PARAM_STR_OR_LONG_OR_NULL(column_str, column_long, column_is_null) + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_LONG_OR_NULL(index_str, index_long, index_is_null) + ZEND_PARSE_PARAMETERS_END(); + + void* cache_slot_column[3] = { NULL, NULL, NULL }; + void* cache_slot_index[3] = { NULL, NULL, NULL }; + + zval *group_entry; + zval fpal; + + array_init_size(return_value, zend_hash_num_elements(input)); + ZEND_HASH_FOREACH_VAL(input, data) { + ZVAL_DEREF(data); + + if (column_is_null) { + Z_TRY_ADDREF_P(data); + colval = data; + } else if ((colval = array_column_fetch_prop(data, column_str, column_long, cache_slot_column, &rv)) == NULL) { + continue; + } + + zval rv; + zval *keyval = array_column_fetch_prop(data, index_str, index_long, cache_slot_index, &rv); + + if (keyval) { + + if (Z_TYPE_P(keyval) == IS_LONG) { + group_entry = zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_P(keyval)); + } else { + group_entry = zend_symtable_find(Z_ARRVAL_P(return_value), Z_STR_P(keyval)); + } + + if (group_entry != NULL) { + ZVAL_COPY_VALUE(&fpal, group_entry); + } else { + array_init(&fpal); + array_set_zval_key(Z_ARRVAL_P(return_value), keyval, &fpal); + zval_ptr_dtor(&fpal); + } + + zend_hash_next_index_insert_new(Z_ARRVAL_P(&fpal), colval); + + zval_ptr_dtor(keyval); + } else { + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), colval); + } + } ZEND_HASH_FOREACH_END(); +} +/* }}} */ + /* {{{ Return input as a new array with the order of the entries reversed */ PHP_FUNCTION(array_reverse) { diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index ab7776d630919..1ae631f54fd23 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -247,6 +247,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_array_column, 0, 2, IS_ARRAY, 0) ZEND_ARG_TYPE_MASK(0, index_key, MAY_BE_LONG|MAY_BE_STRING|MAY_BE_NULL, "null") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_array_group, 0, 2, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0) + ZEND_ARG_TYPE_MASK(0, column_key, MAY_BE_LONG|MAY_BE_STRING|MAY_BE_NULL, NULL) + ZEND_ARG_TYPE_MASK(0, index_key, MAY_BE_LONG|MAY_BE_STRING|MAY_BE_NULL, "null") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_array_reverse, 0, 1, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, preserve_keys, _IS_BOOL, 0, "false") @@ -2287,6 +2293,7 @@ ZEND_FUNCTION(array_key_last); ZEND_FUNCTION(array_values); ZEND_FUNCTION(array_count_values); ZEND_FUNCTION(array_column); +ZEND_FUNCTION(array_group); ZEND_FUNCTION(array_reverse); ZEND_FUNCTION(array_pad); ZEND_FUNCTION(array_flip); @@ -2915,6 +2922,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(array_values, arginfo_array_values) ZEND_FE(array_count_values, arginfo_array_count_values) ZEND_FE(array_column, arginfo_array_column) + ZEND_FE(array_group, arginfo_array_group) ZEND_FE(array_reverse, arginfo_array_reverse) ZEND_FE(array_pad, arginfo_array_pad) ZEND_FE(array_flip, arginfo_array_flip) diff --git a/ext/standard/tests/array/array_group_001.phpt b/ext/standard/tests/array/array_group_001.phpt new file mode 100644 index 0000000000000..7cf539d078483 --- /dev/null +++ b/ext/standard/tests/array/array_group_001.phpt @@ -0,0 +1,114 @@ +--TEST-- +Group the whole array using integer index +--FILE-- + 1, 'name' => 'hassan', 'score' => 'A', 's' => '1'], + ['id' => 2, 'name' => 'hassan', 'score' => 'B', 's' => '2'], + ['id' => 3, 'name' => 'ahmed', 'score' => 'C', 's' => '1'], + ['id' => 4, 'name' => 'moustafa', 'score' => 'D', 's' => '2'], + ['id' => 5, 'name' => 'hassan', 'score' => 'A', 's' => '3'], + ['id' => 6, 'name' => 'wael', 'score' => 'A', 's' => '3'], + ['id' => 6, 'name' => 'wael', 'score' => 'D', 's' => '1'], +]; + +var_dump(array_group($ar, null, 'id')); +?> +--EXPECT-- +array(6) { + [1]=> + array(1) { + [0]=> + array(4) { + ["id"]=> + int(1) + ["name"]=> + string(6) "hassan" + ["score"]=> + string(1) "A" + ["s"]=> + string(1) "1" + } + } + [2]=> + array(1) { + [0]=> + array(4) { + ["id"]=> + int(2) + ["name"]=> + string(6) "hassan" + ["score"]=> + string(1) "B" + ["s"]=> + string(1) "2" + } + } + [3]=> + array(1) { + [0]=> + array(4) { + ["id"]=> + int(3) + ["name"]=> + string(5) "ahmed" + ["score"]=> + string(1) "C" + ["s"]=> + string(1) "1" + } + } + [4]=> + array(1) { + [0]=> + array(4) { + ["id"]=> + int(4) + ["name"]=> + string(8) "moustafa" + ["score"]=> + string(1) "D" + ["s"]=> + string(1) "2" + } + } + [5]=> + array(1) { + [0]=> + array(4) { + ["id"]=> + int(5) + ["name"]=> + string(6) "hassan" + ["score"]=> + string(1) "A" + ["s"]=> + string(1) "3" + } + } + [6]=> + array(2) { + [0]=> + array(4) { + ["id"]=> + int(6) + ["name"]=> + string(4) "wael" + ["score"]=> + string(1) "A" + ["s"]=> + string(1) "3" + } + [1]=> + array(4) { + ["id"]=> + int(6) + ["name"]=> + string(4) "wael" + ["score"]=> + string(1) "D" + ["s"]=> + string(1) "1" + } + } +} \ No newline at end of file diff --git a/ext/standard/tests/array/array_group_002.phpt b/ext/standard/tests/array/array_group_002.phpt new file mode 100644 index 0000000000000..9495c24b5d841 --- /dev/null +++ b/ext/standard/tests/array/array_group_002.phpt @@ -0,0 +1,45 @@ +--TEST-- +Group an element using string index +--FILE-- + 1, 'name' => 'hassan', 'score' => 'A', 's' => '1'], + ['id' => 2, 'name' => 'hassan', 'score' => 'B', 's' => '2'], + ['id' => 3, 'name' => 'ahmed', 'score' => 'C', 's' => '1'], + ['id' => 4, 'name' => 'moustafa', 'score' => 'D', 's' => '2'], + ['id' => 5, 'name' => 'hassan', 'score' => 'A', 's' => '3'], + ['id' => 6, 'name' => 'wael', 'score' => 'A', 's' => '3'], + ['id' => 6, 'name' => 'wael', 'score' => 'D', 's' => '1'], +]; + +print_r(array_group($ar, 'name', 'score')); + +?> +--EXPECT-- +Array +( + [A] => Array + ( + [0] => hassan + [1] => hassan + [2] => wael + ) + + [B] => Array + ( + [0] => hassan + ) + + [C] => Array + ( + [0] => ahmed + ) + + [D] => Array + ( + [0] => moustafa + [1] => wael + ) + +) diff --git a/ext/standard/tests/array/array_group_003.phpt b/ext/standard/tests/array/array_group_003.phpt new file mode 100644 index 0000000000000..aa81952621912 --- /dev/null +++ b/ext/standard/tests/array/array_group_003.phpt @@ -0,0 +1,38 @@ +--TEST-- +Group an element using an integer but casted into a string +--FILE-- + 1, 'name' => 'hassan', 'score' => 'A', 's' => '1'], + ['id' => 2, 'name' => 'hassan', 'score' => 'B', 's' => '2'], + ['id' => 3, 'name' => 'ahmed', 'score' => 'C', 's' => '1'], + ['id' => 4, 'name' => 'moustafa', 'score' => 'D', 's' => '2'], + ['id' => 5, 'name' => 'hassan', 'score' => 'A', 's' => '3'], + ['id' => 6, 'name' => 'wael', 'score' => 'A', 's' => '3'], + ['id' => 6, 'name' => 'wael', 'score' => 'D', 's' => '1'], +]; +print_r(array_group($ar, 'score', 's')); +?> +--EXPECT-- +Array +( + [1] => Array + ( + [0] => A + [1] => C + [2] => D + ) + + [2] => Array + ( + [0] => B + [1] => D + ) + + [3] => Array + ( + [0] => A + [1] => A + ) + +) diff --git a/ext/standard/tests/array/array_group_004.phpt b/ext/standard/tests/array/array_group_004.phpt new file mode 100644 index 0000000000000..3d646779b7a8e --- /dev/null +++ b/ext/standard/tests/array/array_group_004.phpt @@ -0,0 +1,92 @@ +--TEST-- +Group the whole array using string index +--FILE-- + 1, 'name' => 'hassan', 'score' => 'A', 's' => '1'], + ['id' => 2, 'name' => 'hassan', 'score' => 'B', 's' => '2'], + ['id' => 3, 'name' => 'ahmed', 'score' => 'C', 's' => '1'], + ['id' => 4, 'name' => 'moustafa', 'score' => 'D', 's' => '2'], + ['id' => 5, 'name' => 'hassan', 'score' => 'A', 's' => '3'], + ['id' => 6, 'name' => 'wael', 'score' => 'A', 's' => '3'], + ['id' => 6, 'name' => 'wael', 'score' => 'D', 's' => '1'], +]; + +print_r(array_group($ar, null, 'name')); +?> +--EXPECT-- +Array +( + [hassan] => Array + ( + [0] => Array + ( + [id] => 1 + [name] => hassan + [score] => A + [s] => 1 + ) + + [1] => Array + ( + [id] => 2 + [name] => hassan + [score] => B + [s] => 2 + ) + + [2] => Array + ( + [id] => 5 + [name] => hassan + [score] => A + [s] => 3 + ) + + ) + + [ahmed] => Array + ( + [0] => Array + ( + [id] => 3 + [name] => ahmed + [score] => C + [s] => 1 + ) + + ) + + [moustafa] => Array + ( + [0] => Array + ( + [id] => 4 + [name] => moustafa + [score] => D + [s] => 2 + ) + + ) + + [wael] => Array + ( + [0] => Array + ( + [id] => 6 + [name] => wael + [score] => A + [s] => 3 + ) + + [1] => Array + ( + [id] => 6 + [name] => wael + [score] => D + [s] => 1 + ) + + ) + +)