Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 32 additions & 19 deletions ext/standard/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -3068,15 +3068,36 @@ zend_bool array_column_param_helper(zval *param,
}
/* }}} */

static inline zval *array_column_fetch_prop(zval *data, zval *name, zval *rv)
{
zval *prop = NULL;

if (Z_TYPE_P(data) == IS_OBJECT) {
zend_string *key = zval_get_string(name);

if (!Z_OBJ_HANDLER_P(data, has_property) || Z_OBJ_HANDLER_P(data, has_property)(data, name, 1, NULL)) {
prop = zend_read_property(Z_OBJCE_P(data), data, key->val, key->len, 1, rv);
}
zend_string_release(key);
} else if (Z_TYPE_P(data) == IS_ARRAY) {
if (Z_TYPE_P(name) == IS_STRING) {
prop = zend_hash_find(Z_ARRVAL_P(data), Z_STR_P(name));
} else if (Z_TYPE_P(name) == IS_LONG) {
prop = zend_hash_index_find(Z_ARRVAL_P(data), Z_LVAL_P(name));
}
}

return prop;
}

/* {{{ proto array array_column(array input, mixed column_key[, mixed index_key])
Return the values from a single column in the input array, identified by the
value_key and optionally indexed by the index_key */
PHP_FUNCTION(array_column)
{
zval *zcolumn = NULL, *zkey = NULL, *data;
HashTable *arr_hash;
zval *zcolval = NULL, *zkeyval = NULL;
HashTable *ht;
zval *zcolval = NULL, *zkeyval = NULL, rvc, rvk;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "hz!|z!", &arr_hash, &zcolumn, &zkey) == FAILURE) {
return;
Expand All @@ -3090,32 +3111,18 @@ PHP_FUNCTION(array_column)
array_init(return_value);
ZEND_HASH_FOREACH_VAL(arr_hash, data) {
ZVAL_DEREF(data);
if (Z_TYPE_P(data) != IS_ARRAY) {
/* Skip elemens which are not sub-arrays */
continue;
}
ht = Z_ARRVAL_P(data);

if (!zcolumn) {
/* NULL column ID means use entire subarray as data */
zcolval = data;

/* Otherwise, skip if the value doesn't exist in our subarray */
} else if ((Z_TYPE_P(zcolumn) == IS_STRING) &&
((zcolval = zend_hash_find(ht, Z_STR_P(zcolumn))) == NULL)) {
continue;
} else if ((Z_TYPE_P(zcolumn) == IS_LONG) &&
((zcolval = zend_hash_index_find(ht, Z_LVAL_P(zcolumn))) == NULL)) {
} else if ((zcolval = array_column_fetch_prop(data, zcolumn, &rvc)) == NULL) {
continue;
}

/* Failure will leave zkeyval alone which will land us on the final else block below
* which is to append the value as next_index
*/
if (zkey && (Z_TYPE_P(zkey) == IS_STRING)) {
zkeyval = zend_hash_find(ht, Z_STR_P(zkey));
} else if (zkey && (Z_TYPE_P(zkey) == IS_LONG)) {
zkeyval = zend_hash_index_find(ht, Z_LVAL_P(zkey));
if (zkey) {
zkeyval = array_column_fetch_prop(data, zkey, &rvk);
}

Z_TRY_ADDREF_P(zcolval);
Expand All @@ -3130,6 +3137,12 @@ PHP_FUNCTION(array_column)
} else {
add_next_index_zval(return_value, zcolval);
}
if (zcolval == &rvc) {
zval_ptr_dtor(&rvc);
}
if (zkeyval == &rvk) {
zval_ptr_dtor(&rvk);
}
} ZEND_HASH_FOREACH_END();
}
/* }}} */
Expand Down
165 changes: 165 additions & 0 deletions ext/standard/tests/array/array_column_variant_objects.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
--TEST--
Test array_column() function: testing with objects
--FILE--
<?php

class User
{
public $id, $first_name, $last_name;

public function __construct($id, $first_name, $last_name)
{
$this->id = $id;
$this->first_name = $first_name;
$this->last_name = $last_name;
}
}

function newUser($id, $first_name, $last_name)
{
$o = new stdClass;
$o->{0} = $id;
$o->{1} = $first_name;
$o->{2} = $last_name;

return $o;
}

class Something
{
public function __isset($name)
{
return $name == 'first_name';
}

public function __get($name)
{
return new User(4, 'Jack', 'Sparrow');
}
}

$records = array(
newUser(1, 'John', 'Doe'),
newUser(2, 'Sally', 'Smith'),
newUser(3, 'Jane', 'Jones'),
new User(1, 'John', 'Doe'),
new User(2, 'Sally', 'Smith'),
new User(3, 'Jane', 'Jones'),
new Something,
);

echo "*** Testing array_column() : object property fetching (numeric property names) ***\n";

echo "-- first_name column from recordset --\n";
var_dump(array_column($records, 1));

echo "-- id column from recordset --\n";
var_dump(array_column($records, 0));

echo "-- last_name column from recordset, keyed by value from id column --\n";
var_dump(array_column($records, 2, 0));

echo "-- last_name column from recordset, keyed by value from first_name column --\n";
var_dump(array_column($records, 2, 1));

echo "*** Testing array_column() : object property fetching (string property names) ***\n";

echo "-- first_name column from recordset --\n";
var_dump(array_column($records, 'first_name'));

echo "-- id column from recordset --\n";
var_dump(array_column($records, 'id'));

echo "-- last_name column from recordset, keyed by value from id column --\n";
var_dump(array_column($records, 'last_name', 'id'));

echo "-- last_name column from recordset, keyed by value from first_name column --\n";
var_dump(array_column($records, 'last_name', 'first_name'));

echo "Done\n";
?>
--EXPECTF--
*** Testing array_column() : object property fetching (numeric property names) ***
-- first_name column from recordset --
array(3) {
[0]=>
string(4) "John"
[1]=>
string(5) "Sally"
[2]=>
string(4) "Jane"
}
-- id column from recordset --
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
-- last_name column from recordset, keyed by value from id column --
array(3) {
[1]=>
string(3) "Doe"
[2]=>
string(5) "Smith"
[3]=>
string(5) "Jones"
}
-- last_name column from recordset, keyed by value from first_name column --
array(3) {
["John"]=>
string(3) "Doe"
["Sally"]=>
string(5) "Smith"
["Jane"]=>
string(5) "Jones"
}
*** Testing array_column() : object property fetching (string property names) ***
-- first_name column from recordset --
array(4) {
[0]=>
string(4) "John"
[1]=>
string(5) "Sally"
[2]=>
string(4) "Jane"
[3]=>
object(User)#8 (3) {
["id"]=>
int(4)
["first_name"]=>
string(4) "Jack"
["last_name"]=>
string(7) "Sparrow"
}
}
-- id column from recordset --
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
-- last_name column from recordset, keyed by value from id column --
array(3) {
[1]=>
string(3) "Doe"
[2]=>
string(5) "Smith"
[3]=>
string(5) "Jones"
}
-- last_name column from recordset, keyed by value from first_name column --
array(3) {
["John"]=>
string(3) "Doe"
["Sally"]=>
string(5) "Smith"
["Jane"]=>
string(5) "Jones"
}
Done