Skip to content

Commit 3bf012a

Browse files
committed
Feature: Enhanced array_column() to also work with object elements.
1 parent 86df85f commit 3bf012a

File tree

3 files changed

+202
-19
lines changed

3 files changed

+202
-19
lines changed

UPGRADING

+5
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,11 @@ Other
610610
hardcoded value of 16. This limit is now removed and the number of pipes is
611611
effectively limited by the amount of memory available to PHP.
612612

613+
- array_column():
614+
The function now supports an array of objects as well as two-dimensional
615+
arrays. Only public properties are considered, and objects that make use of
616+
__get() for dynamic properties must also implement __isset().
617+
613618
========================================
614619
6. New Functions
615620
========================================

ext/standard/array.c

+32-19
Original file line numberDiff line numberDiff line change
@@ -3068,15 +3068,36 @@ zend_bool array_column_param_helper(zval *param,
30683068
}
30693069
/* }}} */
30703070

3071+
static inline zval *array_column_fetch_prop(zval *data, zval *name, zval *rv)
3072+
{
3073+
zval *prop = NULL;
3074+
3075+
if (Z_TYPE_P(data) == IS_OBJECT) {
3076+
zend_string *key = zval_get_string(name);
3077+
3078+
if (!Z_OBJ_HANDLER_P(data, has_property) || Z_OBJ_HANDLER_P(data, has_property)(data, name, 1, NULL)) {
3079+
prop = zend_read_property(Z_OBJCE_P(data), data, key->val, key->len, 1, rv);
3080+
}
3081+
zend_string_release(key);
3082+
} else if (Z_TYPE_P(data) == IS_ARRAY) {
3083+
if (Z_TYPE_P(name) == IS_STRING) {
3084+
prop = zend_hash_find(Z_ARRVAL_P(data), Z_STR_P(name));
3085+
} else if (Z_TYPE_P(name) == IS_LONG) {
3086+
prop = zend_hash_index_find(Z_ARRVAL_P(data), Z_LVAL_P(name));
3087+
}
3088+
}
3089+
3090+
return prop;
3091+
}
3092+
30713093
/* {{{ proto array array_column(array input, mixed column_key[, mixed index_key])
30723094
Return the values from a single column in the input array, identified by the
30733095
value_key and optionally indexed by the index_key */
30743096
PHP_FUNCTION(array_column)
30753097
{
30763098
zval *zcolumn = NULL, *zkey = NULL, *data;
30773099
HashTable *arr_hash;
3078-
zval *zcolval = NULL, *zkeyval = NULL;
3079-
HashTable *ht;
3100+
zval *zcolval = NULL, *zkeyval = NULL, rvc, rvk;
30803101

30813102
if (zend_parse_parameters(ZEND_NUM_ARGS(), "hz!|z!", &arr_hash, &zcolumn, &zkey) == FAILURE) {
30823103
return;
@@ -3090,32 +3111,18 @@ PHP_FUNCTION(array_column)
30903111
array_init(return_value);
30913112
ZEND_HASH_FOREACH_VAL(arr_hash, data) {
30923113
ZVAL_DEREF(data);
3093-
if (Z_TYPE_P(data) != IS_ARRAY) {
3094-
/* Skip elemens which are not sub-arrays */
3095-
continue;
3096-
}
3097-
ht = Z_ARRVAL_P(data);
30983114

30993115
if (!zcolumn) {
3100-
/* NULL column ID means use entire subarray as data */
31013116
zcolval = data;
3102-
3103-
/* Otherwise, skip if the value doesn't exist in our subarray */
3104-
} else if ((Z_TYPE_P(zcolumn) == IS_STRING) &&
3105-
((zcolval = zend_hash_find(ht, Z_STR_P(zcolumn))) == NULL)) {
3106-
continue;
3107-
} else if ((Z_TYPE_P(zcolumn) == IS_LONG) &&
3108-
((zcolval = zend_hash_index_find(ht, Z_LVAL_P(zcolumn))) == NULL)) {
3117+
} else if ((zcolval = array_column_fetch_prop(data, zcolumn, &rvc)) == NULL) {
31093118
continue;
31103119
}
31113120

31123121
/* Failure will leave zkeyval alone which will land us on the final else block below
31133122
* which is to append the value as next_index
31143123
*/
3115-
if (zkey && (Z_TYPE_P(zkey) == IS_STRING)) {
3116-
zkeyval = zend_hash_find(ht, Z_STR_P(zkey));
3117-
} else if (zkey && (Z_TYPE_P(zkey) == IS_LONG)) {
3118-
zkeyval = zend_hash_index_find(ht, Z_LVAL_P(zkey));
3124+
if (zkey) {
3125+
zkeyval = array_column_fetch_prop(data, zkey, &rvk);
31193126
}
31203127

31213128
Z_TRY_ADDREF_P(zcolval);
@@ -3130,6 +3137,12 @@ PHP_FUNCTION(array_column)
31303137
} else {
31313138
add_next_index_zval(return_value, zcolval);
31323139
}
3140+
if (zcolval == &rvc) {
3141+
zval_ptr_dtor(&rvc);
3142+
}
3143+
if (zkeyval == &rvk) {
3144+
zval_ptr_dtor(&rvk);
3145+
}
31333146
} ZEND_HASH_FOREACH_END();
31343147
}
31353148
/* }}} */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
--TEST--
2+
Test array_column() function: testing with objects
3+
--FILE--
4+
<?php
5+
6+
class User
7+
{
8+
public $id, $first_name, $last_name;
9+
10+
public function __construct($id, $first_name, $last_name)
11+
{
12+
$this->id = $id;
13+
$this->first_name = $first_name;
14+
$this->last_name = $last_name;
15+
}
16+
}
17+
18+
function newUser($id, $first_name, $last_name)
19+
{
20+
$o = new stdClass;
21+
$o->{0} = $id;
22+
$o->{1} = $first_name;
23+
$o->{2} = $last_name;
24+
25+
return $o;
26+
}
27+
28+
class Something
29+
{
30+
public function __isset($name)
31+
{
32+
return $name == 'first_name';
33+
}
34+
35+
public function __get($name)
36+
{
37+
return new User(4, 'Jack', 'Sparrow');
38+
}
39+
}
40+
41+
$records = array(
42+
newUser(1, 'John', 'Doe'),
43+
newUser(2, 'Sally', 'Smith'),
44+
newUser(3, 'Jane', 'Jones'),
45+
new User(1, 'John', 'Doe'),
46+
new User(2, 'Sally', 'Smith'),
47+
new User(3, 'Jane', 'Jones'),
48+
new Something,
49+
);
50+
51+
echo "*** Testing array_column() : object property fetching (numeric property names) ***\n";
52+
53+
echo "-- first_name column from recordset --\n";
54+
var_dump(array_column($records, 1));
55+
56+
echo "-- id column from recordset --\n";
57+
var_dump(array_column($records, 0));
58+
59+
echo "-- last_name column from recordset, keyed by value from id column --\n";
60+
var_dump(array_column($records, 2, 0));
61+
62+
echo "-- last_name column from recordset, keyed by value from first_name column --\n";
63+
var_dump(array_column($records, 2, 1));
64+
65+
echo "*** Testing array_column() : object property fetching (string property names) ***\n";
66+
67+
echo "-- first_name column from recordset --\n";
68+
var_dump(array_column($records, 'first_name'));
69+
70+
echo "-- id column from recordset --\n";
71+
var_dump(array_column($records, 'id'));
72+
73+
echo "-- last_name column from recordset, keyed by value from id column --\n";
74+
var_dump(array_column($records, 'last_name', 'id'));
75+
76+
echo "-- last_name column from recordset, keyed by value from first_name column --\n";
77+
var_dump(array_column($records, 'last_name', 'first_name'));
78+
79+
echo "Done\n";
80+
?>
81+
--EXPECTF--
82+
*** Testing array_column() : object property fetching (numeric property names) ***
83+
-- first_name column from recordset --
84+
array(3) {
85+
[0]=>
86+
string(4) "John"
87+
[1]=>
88+
string(5) "Sally"
89+
[2]=>
90+
string(4) "Jane"
91+
}
92+
-- id column from recordset --
93+
array(3) {
94+
[0]=>
95+
int(1)
96+
[1]=>
97+
int(2)
98+
[2]=>
99+
int(3)
100+
}
101+
-- last_name column from recordset, keyed by value from id column --
102+
array(3) {
103+
[1]=>
104+
string(3) "Doe"
105+
[2]=>
106+
string(5) "Smith"
107+
[3]=>
108+
string(5) "Jones"
109+
}
110+
-- last_name column from recordset, keyed by value from first_name column --
111+
array(3) {
112+
["John"]=>
113+
string(3) "Doe"
114+
["Sally"]=>
115+
string(5) "Smith"
116+
["Jane"]=>
117+
string(5) "Jones"
118+
}
119+
*** Testing array_column() : object property fetching (string property names) ***
120+
-- first_name column from recordset --
121+
array(4) {
122+
[0]=>
123+
string(4) "John"
124+
[1]=>
125+
string(5) "Sally"
126+
[2]=>
127+
string(4) "Jane"
128+
[3]=>
129+
object(User)#8 (3) {
130+
["id"]=>
131+
int(4)
132+
["first_name"]=>
133+
string(4) "Jack"
134+
["last_name"]=>
135+
string(7) "Sparrow"
136+
}
137+
}
138+
-- id column from recordset --
139+
array(3) {
140+
[0]=>
141+
int(1)
142+
[1]=>
143+
int(2)
144+
[2]=>
145+
int(3)
146+
}
147+
-- last_name column from recordset, keyed by value from id column --
148+
array(3) {
149+
[1]=>
150+
string(3) "Doe"
151+
[2]=>
152+
string(5) "Smith"
153+
[3]=>
154+
string(5) "Jones"
155+
}
156+
-- last_name column from recordset, keyed by value from first_name column --
157+
array(3) {
158+
["John"]=>
159+
string(3) "Doe"
160+
["Sally"]=>
161+
string(5) "Smith"
162+
["Jane"]=>
163+
string(5) "Jones"
164+
}
165+
Done

0 commit comments

Comments
 (0)