From b6f236468d3fc7270d44ce32191658470f339cbd Mon Sep 17 00:00:00 2001 From: Ben Ramsey Date: Wed, 3 Apr 2013 16:02:13 -0500 Subject: [PATCH 1/3] array_column: Maintain original index association if no third parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If no third parameter is passed, the resulting array maintains the indexes passed in from the original array. Feature suggested by David Zülke. --- ext/standard/array.c | 14 +++- .../tests/array/array_column_basic.phpt | 4 +- .../tests/array/array_column_variant.phpt | 65 +++++++++++++++++++ 3 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 ext/standard/tests/array/array_column_variant.phpt diff --git a/ext/standard/array.c b/ext/standard/array.c index 9a64cf4e052a1..2373502810efd 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -2594,6 +2594,14 @@ PHP_FUNCTION(array_column) if (Z_TYPE_PP(data) == IS_ARRAY) { zval *strkey = NULL; + /* 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 && 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) { @@ -2639,8 +2647,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_variant.phpt b/ext/standard/tests/array/array_column_variant.phpt new file mode 100644 index 0000000000000..0694d05641a2f --- /dev/null +++ b/ext/standard/tests/array/array_column_variant.phpt @@ -0,0 +1,65 @@ +--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')); + +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" +} +Done From 444956031df28cba165dd163d648d71006bfc89c Mon Sep 17 00:00:00 2001 From: Ben Ramsey Date: Wed, 3 Apr 2013 20:59:45 -0500 Subject: [PATCH 2/3] array_column: 2nd param should be optional to use entire rows as result When NULL is passed as the second parameter, all columns from the input array are returned. See https://bugs.php.net/bug.php?id=64493 --- ext/standard/array.c | 8 +- .../tests/array/array_column_variant.phpt | 78 +++++++++++++++++++ 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 2373502810efd..b5e5b899c1187 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -2532,7 +2532,7 @@ 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; @@ -2542,7 +2542,7 @@ PHP_FUNCTION(array_column) 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); @@ -2602,7 +2602,9 @@ PHP_FUNCTION(array_column) hash_key_is_string = 1; } - if (column && zend_hash_find(Z_ARRVAL_PP(data), column, column_len + 1, (void**)&zcolval) == FAILURE) { + if (column_is_null) { + zcolval = data; + } 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; diff --git a/ext/standard/tests/array/array_column_variant.phpt b/ext/standard/tests/array/array_column_variant.phpt index 0694d05641a2f..cec5d0e8368f3 100644 --- a/ext/standard/tests/array/array_column_variant.phpt +++ b/ext/standard/tests/array/array_column_variant.phpt @@ -31,6 +31,21 @@ 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 "Done\n"; ?> --EXPECTF-- @@ -62,4 +77,67 @@ array(3) { [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" + } +} Done From 7f2aeb5c080904dc26890e8ae6d390a09d1bb52d Mon Sep 17 00:00:00 2001 From: Ben Ramsey Date: Sun, 21 Apr 2013 14:59:08 -0500 Subject: [PATCH 3/3] array_column: Support array of column names for second parameter If the second parameter is an array of column names, each row returned in the result array is an array containing only the specified columns. The rows are indexed by the third parameter. Feature inspired by suggestions from Hakre and Craig Campbell. See https://github.com/php/php-src/pull/257#issuecomment-12594630 --- ext/standard/array.c | 69 ++++++++++++- .../tests/array/array_column_error.phpt | 12 +-- .../tests/array/array_column_object_cast.phpt | 22 +++++ .../tests/array/array_column_variant.phpt | 98 +++++++++++++++++++ 4 files changed, 189 insertions(+), 12 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index b5e5b899c1187..164d3462a487b 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -2536,6 +2536,11 @@ PHP_FUNCTION(array_column) 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; } @@ -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; } @@ -2604,13 +2613,69 @@ PHP_FUNCTION(array_column) 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; 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 index cec5d0e8368f3..8e17ebe735f0f 100644 --- a/ext/standard/tests/array/array_column_variant.phpt +++ b/ext/standard/tests/array/array_column_variant.phpt @@ -46,6 +46,27 @@ 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-- @@ -140,4 +161,81 @@ array(2) { 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