diff --git a/ext/standard/array.c b/ext/standard/array.c index 9a64cf4e052a1..164d3462a487b 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -2532,17 +2532,22 @@ PHP_FUNCTION(array_column) zval *zarray, **zcolumn, **zkey = NULL, **data, **zcolval, **zkeyval; HashTable *arr_hash; HashPosition pointer; - ulong column_idx = 0, key_idx = 0; + ulong column_idx = 0, key_idx = 0, column_is_null = 0; char *column = NULL, *key = NULL, *keyval = NULL; int column_len = 0, key_len = 0, keyval_idx = -1; + zval *column_arr = NULL, **column_data, **zcolval2; + HashTable *column_hash; + HashPosition column_pointer; + int column_is_array = 0; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "aZ|Z", &zarray, &zcolumn, &zkey) == FAILURE) { return; } switch (Z_TYPE_PP(zcolumn)) { case IS_NULL: - column_idx = 0; + column_is_null = 1; break; case IS_LONG: column_idx = Z_LVAL_PP(zcolumn); @@ -2556,8 +2561,12 @@ PHP_FUNCTION(array_column) column = Z_STRVAL_PP(zcolumn); column_len = Z_STRLEN_PP(zcolumn); break; + case IS_ARRAY: + column_is_array = 1; + column_hash = Z_ARRVAL_PP(zcolumn); + break; default: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "The column key should be either a string or an integer"); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "The column key should be a string, an integer, or an array"); RETURN_FALSE; } @@ -2594,13 +2603,79 @@ PHP_FUNCTION(array_column) if (Z_TYPE_PP(data) == IS_ARRAY) { zval *strkey = NULL; - if (column && zend_hash_find(Z_ARRVAL_PP(data), column, column_len + 1, (void**)&zcolval) == FAILURE) { + /* Find the current key/index of the array */ + char *current_key; + uint current_key_len, hash_key_is_string = 0; + ulong current_index; + if (zend_hash_get_current_key_ex(arr_hash, ¤t_key, ¤t_key_len, ¤t_index, 0, &pointer) == HASH_KEY_IS_STRING) { + hash_key_is_string = 1; + } + + if (column_is_null) { + zcolval = data; + } else if (column_is_array) { + + MAKE_STD_ZVAL(column_arr); + array_init(column_arr); + + for (zend_hash_internal_pointer_reset_ex(column_hash, &column_pointer); + zend_hash_get_current_data_ex(column_hash, (void**)&column_data, &column_pointer) == SUCCESS; + zend_hash_move_forward_ex(column_hash, &column_pointer)) { + + zval *strobj = NULL; + column = NULL; + + switch (Z_TYPE_PP(column_data)) { + case IS_LONG: + column_idx = Z_LVAL_PP(column_data); + break; + case IS_STRING: + column = Z_STRVAL_PP(column_data); + column_len = Z_STRLEN_PP(column_data); + break; + case IS_OBJECT: + { + MAKE_STD_ZVAL(strobj); + MAKE_COPY_ZVAL(column_data, strobj); + convert_to_string(strobj); + column = Z_STRVAL_P(strobj); + column_len = Z_STRLEN_P(strobj); + } + break; + default: + continue; + } + + if (column && zend_hash_find(Z_ARRVAL_PP(data), column, column_len + 1, (void**)&zcolval2) == FAILURE) { + continue; + } else if (!column && zend_hash_index_find(Z_ARRVAL_PP(data), column_idx, (void**)&zcolval2) == FAILURE) { + continue; + } + + Z_ADDREF_PP(zcolval2); + + if (column) { + add_assoc_zval(column_arr, column, *zcolval2); + if (strobj) { + zval_ptr_dtor(&strobj); + } + } else { + add_index_zval(column_arr, column_idx, *zcolval2); + } + + } + + zcolval = &column_arr; + + } else if (column && zend_hash_find(Z_ARRVAL_PP(data), column, column_len + 1, (void**)&zcolval) == FAILURE) { continue; } else if (!column && zend_hash_index_find(Z_ARRVAL_PP(data), column_idx, (void**)&zcolval) == FAILURE) { continue; } - Z_ADDREF_PP(zcolval); + if (column_arr == NULL) { + Z_ADDREF_PP(zcolval); + } keyval = NULL; keyval_idx = -1; @@ -2639,8 +2714,12 @@ PHP_FUNCTION(array_column) } } else if (keyval_idx != -1) { add_index_zval(return_value, keyval_idx, *zcolval); - } else { + } else if (zkey) { add_next_index_zval(return_value, *zcolval); + } else if (hash_key_is_string) { + add_assoc_zval(return_value, current_key, *zcolval); + } else { + add_index_zval(return_value, current_index, *zcolval); } } diff --git a/ext/standard/tests/array/array_column_basic.phpt b/ext/standard/tests/array/array_column_basic.phpt index 70ce2136b44d2..7b0d191f9ccbe 100644 --- a/ext/standard/tests/array/array_column_basic.phpt +++ b/ext/standard/tests/array/array_column_basic.phpt @@ -251,7 +251,7 @@ array(0) { *** Testing columns not present in all rows *** array(1) { - [0]=> + [1]=> string(3) "qux" } array(1) { @@ -277,7 +277,7 @@ array(3) { array(2) { [0]=> string(3) "bar" - [1]=> + [2]=> string(3) "fff" } array(2) { diff --git a/ext/standard/tests/array/array_column_error.phpt b/ext/standard/tests/array/array_column_error.phpt index 1aec1acc6364c..e0d3b52d74196 100644 --- a/ext/standard/tests/array/array_column_error.phpt +++ b/ext/standard/tests/array/array_column_error.phpt @@ -29,9 +29,6 @@ var_dump(array_column(array(), true)); echo "\n-- Testing array_column() column key parameter should be a string or integer (testing float) --\n"; var_dump(array_column(array(), 2.3)); -echo "\n-- Testing array_column() column key parameter should be a string or integer (testing array) --\n"; -var_dump(array_column(array(), array())); - echo "\n-- Testing array_column() index key parameter should be a string or an integer (testing bool) --\n"; var_dump(array_column(array(), 'foo', true)); @@ -68,17 +65,12 @@ NULL -- Testing array_column() column key parameter should be a string or an integer (testing bool) -- -Warning: array_column(): The column key should be either a string or an integer in %s on line %d +Warning: array_column(): The column key should be a string, an integer, or an array in %s on line %d bool(false) -- Testing array_column() column key parameter should be a string or integer (testing float) -- -Warning: array_column(): The column key should be either a string or an integer in %s on line %d -bool(false) - --- Testing array_column() column key parameter should be a string or integer (testing array) -- - -Warning: array_column(): The column key should be either a string or an integer in %s on line %d +Warning: array_column(): The column key should be a string, an integer, or an array in %s on line %d bool(false) -- Testing array_column() index key parameter should be a string or an integer (testing bool) -- diff --git a/ext/standard/tests/array/array_column_object_cast.phpt b/ext/standard/tests/array/array_column_object_cast.phpt index 762aaa81f4b95..626bdb7b72845 100644 --- a/ext/standard/tests/array/array_column_object_cast.phpt +++ b/ext/standard/tests/array/array_column_object_cast.phpt @@ -6,6 +6,10 @@ class ColumnKeyClass { function __toString() { return 'first_name'; } } +class ColumnKeyClass2 { + function __toString() { return 'last_name'; } +} + class IndexKeyClass { function __toString() { return 'id'; } } @@ -16,6 +20,7 @@ class ValueClass { $column_key = new ColumnKeyClass(); +$column_key2 = new ColumnKeyClass2(); $index_key = new IndexKeyClass(); $value = new ValueClass(); @@ -35,6 +40,8 @@ $records = array( ); $firstNames = array_column($records, $column_key, $index_key); print_r($firstNames); +$firstLastNames = array_column($records, array($column_key, $column_key2)); +print_r($firstLastNames); var_dump($column_key); var_dump($index_key); var_dump($value); @@ -44,6 +51,21 @@ Array [2135] => John [3245] => Sally ) +Array +( + [0] => Array + ( + [first_name] => John + [last_name] => XXX + ) + + [1] => Array + ( + [first_name] => Sally + [last_name] => Smith + ) + +) object(ColumnKeyClass)#%d (0) { } object(IndexKeyClass)#%d (0) { diff --git a/ext/standard/tests/array/array_column_variant.phpt b/ext/standard/tests/array/array_column_variant.phpt new file mode 100644 index 0000000000000..8e17ebe735f0f --- /dev/null +++ b/ext/standard/tests/array/array_column_variant.phpt @@ -0,0 +1,241 @@ +--TEST-- +Test array_column() function: variant functionality +--FILE-- + array( + 'id' => 1, + 'first_name' => 'John', + 'last_name' => 'Doe' + ), + 457 => array( + 'id' => 2, + 'first_name' => 'Sally', + 'last_name' => 'Smith' + ), + 458 => array( + 'id' => 3, + 'first_name' => 'Jane', + 'last_name' => 'Jones' + ) +); + +echo "-- first_name column from recordset --\n"; +var_dump(array_column($records, 'first_name')); + +echo "-- first_name column from recordset, keyed by value from id column --\n"; +var_dump(array_column($records, 'first_name', 'id')); + +echo "-- first_name column from recordset, zero-indexed due to missing index column --\n"; +var_dump(array_column($records, 'first_name', 'foo')); + +/* Array from Bug Request #64493 test script */ +$rows = array( + 456 => array('id' => '3', 'title' => 'Foo', 'date' => '2013-03-25'), + 457 => array('id' => '5', 'title' => 'Bar', 'date' => '2012-05-20'), +); + +echo "-- pass null as second parameter to get back all columns indexed by third parameter --\n"; +var_dump(array_column($rows, null, 'id')); + +echo "-- pass null as second parameter and bogus third param to get back zero-indexed array of all columns --\n"; +var_dump(array_column($rows, null, 'foo')); + +echo "-- pass null as second parameter and no third param to get back the exact same array as the input --\n"; +var_dump(array_column($rows, null)); + +echo "-- pass array as second parameter of column names to retrieve, specifying third parameter --\n"; +var_dump(array_column($rows, array('title', 'date'), 'id')); + +echo "-- pass array as second parameter of column names to retrieve, specifying bogus third parameter --\n"; +var_dump(array_column($rows, array('id', 'title'), 'foo')); + +echo "-- pass array as second parameter of column names to retrieve, with no third parameter --\n"; +var_dump(array_column($rows, array('title', 'date'))); + + +$rows2 = array( + 456 => array('id' => '3', 'title' => 'Foo', 'date' => '2013-03-25', 0 => 'John'), + 457 => array('id' => '5', 'title' => 'Bar', 'date' => '2012-05-20', 0 => 'Sally'), +); + +echo "-- test second paramater as array with mixed column names --\n"; +var_dump(array_column($rows2, array('title', 0), 'id')); + +echo "-- test second parameter as array with bogus column names --\n"; +var_dump(array_column($rows2, array('foo', 'bar'), 'id')); + +echo "Done\n"; +?> +--EXPECTF-- +*** Testing array_column() : variant functionality *** +-- first_name column from recordset -- +array(3) { + [456]=> + string(4) "John" + [457]=> + string(5) "Sally" + [458]=> + string(4) "Jane" +} +-- first_name column from recordset, keyed by value from id column -- +array(3) { + [1]=> + string(4) "John" + [2]=> + string(5) "Sally" + [3]=> + string(4) "Jane" +} +-- first_name column from recordset, zero-indexed due to missing index column -- +array(3) { + [0]=> + string(4) "John" + [1]=> + string(5) "Sally" + [2]=> + string(4) "Jane" +} +-- pass null as second parameter to get back all columns indexed by third parameter -- +array(2) { + [3]=> + array(3) { + ["id"]=> + string(1) "3" + ["title"]=> + string(3) "Foo" + ["date"]=> + string(10) "2013-03-25" + } + [5]=> + array(3) { + ["id"]=> + string(1) "5" + ["title"]=> + string(3) "Bar" + ["date"]=> + string(10) "2012-05-20" + } +} +-- pass null as second parameter and bogus third param to get back zero-indexed array of all columns -- +array(2) { + [0]=> + array(3) { + ["id"]=> + string(1) "3" + ["title"]=> + string(3) "Foo" + ["date"]=> + string(10) "2013-03-25" + } + [1]=> + array(3) { + ["id"]=> + string(1) "5" + ["title"]=> + string(3) "Bar" + ["date"]=> + string(10) "2012-05-20" + } +} +-- pass null as second parameter and no third param to get back the exact same array as the input -- +array(2) { + [456]=> + array(3) { + ["id"]=> + string(1) "3" + ["title"]=> + string(3) "Foo" + ["date"]=> + string(10) "2013-03-25" + } + [457]=> + array(3) { + ["id"]=> + string(1) "5" + ["title"]=> + string(3) "Bar" + ["date"]=> + string(10) "2012-05-20" + } +} +-- pass array as second parameter of column names to retrieve, specifying third parameter -- +array(2) { + [3]=> + array(2) { + ["title"]=> + string(3) "Foo" + ["date"]=> + string(10) "2013-03-25" + } + [5]=> + array(2) { + ["title"]=> + string(3) "Bar" + ["date"]=> + string(10) "2012-05-20" + } +} +-- pass array as second parameter of column names to retrieve, specifying bogus third parameter -- +array(2) { + [0]=> + array(2) { + ["id"]=> + string(1) "3" + ["title"]=> + string(3) "Foo" + } + [1]=> + array(2) { + ["id"]=> + string(1) "5" + ["title"]=> + string(3) "Bar" + } +} +-- pass array as second parameter of column names to retrieve, with no third parameter -- +array(2) { + [456]=> + array(2) { + ["title"]=> + string(3) "Foo" + ["date"]=> + string(10) "2013-03-25" + } + [457]=> + array(2) { + ["title"]=> + string(3) "Bar" + ["date"]=> + string(10) "2012-05-20" + } +} +-- test second paramater as array with mixed column names -- +array(2) { + [3]=> + array(2) { + ["title"]=> + string(3) "Foo" + [0]=> + string(4) "John" + } + [5]=> + array(2) { + ["title"]=> + string(3) "Bar" + [0]=> + string(5) "Sally" + } +} +-- test second parameter as array with bogus column names -- +array(2) { + [3]=> + array(0) { + } + [5]=> + array(0) { + } +} +Done