]> granicus.if.org Git - php/commitdiff
Feature: Enhanced array_column() to also work with object elements.
authorTjerk Meesters <datibbaw@php.net>
Fri, 26 Jun 2015 23:35:44 +0000 (07:35 +0800)
committerTjerk Meesters <datibbaw@php.net>
Fri, 26 Jun 2015 23:35:44 +0000 (07:35 +0800)
UPGRADING
ext/standard/array.c
ext/standard/tests/array/array_column_variant_objects.phpt [new file with mode: 0644]

index d1d3fd51cd306b61f553441fdb80d1330891533a..ae2989eb5d01d8617097dcc4cdf5980c130e3871 100644 (file)
--- a/UPGRADING
+++ b/UPGRADING
@@ -610,6 +610,11 @@ Other
   hardcoded value of 16. This limit is now removed and the number of pipes is
   effectively limited by the amount of memory available to PHP.
 
+- array_column():
+  The function now supports an array of objects as well as two-dimensional
+  arrays. Only public properties are considered, and objects that make use of
+  __get() for dynamic properties must also implement __isset().
+
 ========================================
 6. New Functions
 ========================================
index 1cf5da1d01b72825bf2eef794c96239afd2bc7e5..5341544b39d76d90c90c093587f14429a9c739d1 100644 (file)
@@ -3068,6 +3068,28 @@ 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 */
@@ -3075,8 +3097,7 @@ 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;
@@ -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);
@@ -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();
 }
 /* }}} */
diff --git a/ext/standard/tests/array/array_column_variant_objects.phpt b/ext/standard/tests/array/array_column_variant_objects.phpt
new file mode 100644 (file)
index 0000000..80e1839
--- /dev/null
@@ -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