]> granicus.if.org Git - php/commitdiff
Use ZPP check for string|int|null arguments in array_column()
authorGeorge Peter Banyard <girgias@php.net>
Mon, 27 Apr 2020 18:30:07 +0000 (20:30 +0200)
committerGeorge Peter Banyard <girgias@php.net>
Sat, 2 May 2020 17:50:51 +0000 (19:50 +0200)
ext/standard/array.c
ext/standard/basic_functions.stub.php
ext/standard/basic_functions_arginfo.h
ext/standard/tests/array/array_column_scalar_index_strict_types.phpt [moved from ext/standard/tests/array/array_column_error.phpt with 62% similarity]
ext/standard/tests/array/array_column_scalar_index_weak_types.phpt [new file with mode: 0644]

index 09756b74e5b441ef3b18c9a335ecaefdffbcd666..3e54946cd040ca4920cab10fa1318d705dc4eac6 100644 (file)
@@ -4083,47 +4083,24 @@ PHP_FUNCTION(array_count_values)
 }
 /* }}} */
 
-/* {{{ array_column_param_helper
- * Specialized conversion rules for array_column() function
- */
-static inline
-zend_bool array_column_param_helper(zval *param, int parameter_number) {
-       switch (Z_TYPE_P(param)) {
-               case IS_DOUBLE:
-                       convert_to_long_ex(param);
-                       /* fallthrough */
-               case IS_LONG:
-                       return 1;
-
-               case IS_OBJECT:
-                       if (!try_convert_to_string(param)) {
-                               return 0;
-                       }
-                       /* fallthrough */
-               case IS_STRING:
-                       return 1;
-
-               default:
-                       zend_argument_type_error(parameter_number, "must be of type string|int, %s given", zend_zval_type_name(param));
-                       return 0;
-       }
-}
-/* }}} */
-
-static inline zval *array_column_fetch_prop(zval *data, zval *name, zval *rv) /* {{{ */
+static inline zval *array_column_fetch_prop(zval *data, zend_string *name_str, zend_long name_long, zval *rv) /* {{{ */
 {
        zval *prop = NULL;
 
        if (Z_TYPE_P(data) == IS_OBJECT) {
+               zend_string *tmp_str;
+               /* If name is an integer convert integer to string */
+               if (name_str == NULL) {
+                       tmp_str = zend_long_to_str(name_long);
+               } else {
+                       tmp_str = zend_string_copy(name_str);
+               }
                /* The has_property check is first performed in "exists" mode (which returns true for
                 * properties that are null but exist) and then in "has" mode to handle objects that
                 * implement __isset (which is not called in "exists" mode). */
-               zend_string *str, *tmp_str;
-
-               str = zval_get_tmp_string(name, &tmp_str);
-               if (Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), str, ZEND_PROPERTY_EXISTS, NULL)
-                               || Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), str, ZEND_PROPERTY_ISSET, NULL)) {
-                       prop = Z_OBJ_HANDLER_P(data, read_property)(Z_OBJ_P(data), str, BP_VAR_R, NULL, rv);
+               if (Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_EXISTS, NULL)
+                               || Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_ISSET, NULL)) {
+                       prop = Z_OBJ_HANDLER_P(data, read_property)(Z_OBJ_P(data), tmp_str, BP_VAR_R, NULL, rv);
                        if (prop) {
                                ZVAL_DEREF(prop);
                                if (prop != rv) {
@@ -4131,12 +4108,13 @@ static inline zval *array_column_fetch_prop(zval *data, zval *name, zval *rv) /*
                                }
                        }
                }
-               zend_tmp_string_release(tmp_str);
+               zend_string_release(tmp_str);
        } else if (Z_TYPE_P(data) == IS_ARRAY) {
-               if (Z_TYPE_P(name) == IS_STRING) {
-                       prop = zend_symtable_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));
+               /* Name is a string */
+               if (name_str != NULL) {
+                       prop = zend_symtable_find(Z_ARRVAL_P(data), name_str);
+               } else {
+                       prop = zend_hash_index_find(Z_ARRVAL_P(data), name_long);
                }
                if (prop) {
                        ZVAL_DEREF(prop);
@@ -4144,42 +4122,42 @@ static inline zval *array_column_fetch_prop(zval *data, zval *name, zval *rv) /*
                }
        }
 
-
        return prop;
 }
 /* }}} */
 
-/* {{{ proto array array_column(array input, mixed column_key[, mixed index_key])
+/* {{{ proto array array_column(array input, string|int|null column_key[, string|int|null 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)
 {
        HashTable *input;
        zval *colval, *data, rv;
-       zval *column = NULL, *index = NULL;
+       zend_string *column_str = NULL;
+       zend_long column_long;
+       zend_bool column_is_null = 0;
+       zend_string *index_str = NULL;
+       zend_long index_long;
+       zend_bool index_is_null = 1;
 
        ZEND_PARSE_PARAMETERS_START(2, 3)
                Z_PARAM_ARRAY_HT(input)
-               Z_PARAM_ZVAL_EX(column, 1, 0)
+               Z_PARAM_STR_OR_LONG_OR_NULL(column_str, column_long, column_is_null)
                Z_PARAM_OPTIONAL
-               Z_PARAM_ZVAL_EX(index, 1, 0)
+               Z_PARAM_STR_OR_LONG_OR_NULL(index_str, index_long, index_is_null)
        ZEND_PARSE_PARAMETERS_END();
 
-       if ((column && !array_column_param_helper(column, 2)) ||
-           (index && !array_column_param_helper(index, 3))) {
-               RETURN_THROWS();
-       }
-
        array_init_size(return_value, zend_hash_num_elements(input));
-       if (!index) {
+       /* Index param is not passed */
+       if (index_is_null) {
                zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
                ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
                        ZEND_HASH_FOREACH_VAL(input, data) {
                                ZVAL_DEREF(data);
-                               if (!column) {
+                               if (column_is_null) {
                                        Z_TRY_ADDREF_P(data);
                                        colval = data;
-                               } else if ((colval = array_column_fetch_prop(data, column, &rv)) == NULL) {
+                               } else if ((colval = array_column_fetch_prop(data, column_str, column_long, &rv)) == NULL) {
                                        continue;
                                }
                                ZEND_HASH_FILL_ADD(colval);
@@ -4189,10 +4167,10 @@ PHP_FUNCTION(array_column)
                ZEND_HASH_FOREACH_VAL(input, data) {
                        ZVAL_DEREF(data);
 
-                       if (!column) {
+                       if (column_is_null) {
                                Z_TRY_ADDREF_P(data);
                                colval = data;
-                       } else if ((colval = array_column_fetch_prop(data, column, &rv)) == NULL) {
+                       } else if ((colval = array_column_fetch_prop(data, column_str, column_long, &rv)) == NULL) {
                                continue;
                        }
 
@@ -4200,7 +4178,7 @@ PHP_FUNCTION(array_column)
                         * which is to append the value as next_index
                         */
                        zval rv;
-                       zval *keyval = array_column_fetch_prop(data, index, &rv);
+                       zval *keyval = array_column_fetch_prop(data, index_str, index_long, &rv);
 
                        if (keyval) {
                                switch (Z_TYPE_P(keyval)) {
index c8c473d0db848c01a022f4554be2d7c4da3fc05e..09c3513b68a0d08da5adc179043507a76f0e3c01 100755 (executable)
@@ -176,11 +176,7 @@ function array_values(array $arg): array {}
 
 function array_count_values(array $arg): array {}
 
-/**
- * @param int|string|null $column_key
- * @param int|string|null $index_key
- */
-function array_column(array $arg, $column_key, $index_key = null): array {}
+function array_column(array $arg, int|string|null $column_key, int|string|null $index_key = null): array {}
 
 function array_reverse(array $input, bool $preserve_keys = false): array {}
 
index 985c57bcaca5d156e0941fbd20e7de7895ee641f..bc26e4ad97fc9b44b0c38c0017a816e59e1045e9 100755 (executable)
@@ -245,8 +245,8 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_array_column, 0, 2, IS_ARRAY, 0)
        ZEND_ARG_TYPE_INFO(0, arg, IS_ARRAY, 0)
-       ZEND_ARG_INFO(0, column_key)
-       ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, index_key, "null")
+       ZEND_ARG_TYPE_MASK(0, column_key, MAY_BE_LONG|MAY_BE_STRING|MAY_BE_NULL, NULL)
+       ZEND_ARG_TYPE_MASK(0, index_key, MAY_BE_LONG|MAY_BE_STRING|MAY_BE_NULL, "null")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_array_reverse, 0, 1, IS_ARRAY, 0)
similarity index 62%
rename from ext/standard/tests/array/array_column_error.phpt
rename to ext/standard/tests/array/array_column_scalar_index_strict_types.phpt
index 547ecf91cb354ff50a75d43a7d1452ebde1cc141..de98d5d9998c2e701fba3f78b603691946627ab8 100644 (file)
@@ -1,7 +1,8 @@
 --TEST--
-Test array_column() function: error conditions
+Test array_column(): Index argument with various types in strict type mode
 --FILE--
 <?php
+declare(strict_types=1);
 /* Prototype:
  *  array array_column(array $input, mixed $column_key[, mixed $index_key]);
  * Description:
@@ -9,32 +10,40 @@ Test array_column() function: error conditions
  *  the specified "column" in a two-dimensional array.
  */
 
-echo "*** Testing array_column() : error conditions ***\n";
 echo "\n-- Testing array_column() column key parameter should be a string or an integer (testing bool) --\n";
 try {
-    var_dump(array_column(array(), true));
+    var_dump(array_column([['php7', 'foo'], ['php8', 'bar']], false));
+} catch (\TypeError $e) {
+    echo $e->getMessage() . "\n";
+}
+try {
+    var_dump(array_column([['php7', 'foo'], ['php8', 'bar']], true));
 } catch (\TypeError $e) {
     echo $e->getMessage() . "\n";
 }
-
 
 echo "\n-- Testing array_column() column key parameter should be a string or integer (testing array) --\n";
 try {
-    var_dump(array_column(array(), array()));
+    var_dump(array_column([['php7', 'foo'], ['php8', 'bar']], array()));
 } catch (\TypeError $e) {
     echo $e->getMessage() . "\n";
 }
 
 echo "\n-- Testing array_column() index key parameter should be a string or an integer (testing bool) --\n";
 try {
-    var_dump(array_column(array(), 'foo', true));
+    var_dump(array_column([['php' => 7, 'foo'], ['php' => 8, 'bar']], 'php', false));
+} catch (\TypeError $e) {
+    echo $e->getMessage() . "\n";
+}
+try {
+    var_dump(array_column([['php' => 7, 'foo'], ['php' => 8, 'bar']], 'php', true));
 } catch (\TypeError $e) {
     echo $e->getMessage() . "\n";
 }
 
 echo "\n-- Testing array_column() index key parameter should be a string or integer (testing array) --\n";
 try {
-    var_dump(array_column(array(), 'foo', array()));
+    var_dump(array_column([['php' => 7, 'foo'], ['php' => 8, 'bar']], 'php', array()));
 } catch (\TypeError $e) {
     echo $e->getMessage() . "\n";
 }
@@ -43,18 +52,18 @@ try {
 
 DONE
 --EXPECT--
-*** Testing array_column() : error conditions ***
-
 -- Testing array_column() column key parameter should be a string or an integer (testing bool) --
-array_column(): Argument #2 ($column_key) must be of type string|int, bool given
+array_column(): Argument #2 ($column_key) must be of type string|int|null, bool given
+array_column(): Argument #2 ($column_key) must be of type string|int|null, bool given
 
 -- Testing array_column() column key parameter should be a string or integer (testing array) --
-array_column(): Argument #2 ($column_key) must be of type string|int, array given
+array_column(): Argument #2 ($column_key) must be of type string|int|null, array given
 
 -- Testing array_column() index key parameter should be a string or an integer (testing bool) --
-array_column(): Argument #3 ($index_key) must be of type string|int, bool given
+array_column(): Argument #3 ($index_key) must be of type string|int|null, bool given
+array_column(): Argument #3 ($index_key) must be of type string|int|null, bool given
 
 -- Testing array_column() index key parameter should be a string or integer (testing array) --
-array_column(): Argument #3 ($index_key) must be of type string|int, array given
+array_column(): Argument #3 ($index_key) must be of type string|int|null, array given
 
 DONE
diff --git a/ext/standard/tests/array/array_column_scalar_index_weak_types.phpt b/ext/standard/tests/array/array_column_scalar_index_weak_types.phpt
new file mode 100644 (file)
index 0000000..ae8c248
--- /dev/null
@@ -0,0 +1,88 @@
+--TEST--
+Test array_column(): Index argument with various types in weak type mode
+--FILE--
+<?php
+/* Prototype:
+ *  array array_column(array $input, mixed $column_key[, mixed $index_key]);
+ * Description:
+ *  Returns an array containing all the values from
+ *  the specified "column" in a two-dimensional array.
+ */
+
+echo "\n-- Testing array_column() column key parameter should be a string or an integer (testing bool) --\n";
+try {
+    var_dump(array_column([['php7', 'foo'], ['php8', 'bar']], false));
+} catch (\TypeError $e) {
+    echo $e->getMessage() . "\n";
+}
+try {
+    var_dump(array_column([['php7', 'foo'], ['php8', 'bar']], true));
+} catch (\TypeError $e) {
+    echo $e->getMessage() . "\n";
+}
+
+echo "\n-- Testing array_column() column key parameter should be a string or integer (testing array) --\n";
+try {
+    var_dump(array_column([['php7', 'foo'], ['php8', 'bar']], array()));
+} catch (\TypeError $e) {
+    echo $e->getMessage() . "\n";
+}
+
+echo "\n-- Testing array_column() index key parameter should be a string or an integer (testing bool) --\n";
+try {
+    var_dump(array_column([['php' => 7, 'foo'], ['php' => 8, 'bar']], 'php', false));
+} catch (\TypeError $e) {
+    echo $e->getMessage() . "\n";
+}
+try {
+    var_dump(array_column([['php' => 7, 'foo'], ['php' => 8, 'bar']], 'php', true));
+} catch (\TypeError $e) {
+    echo $e->getMessage() . "\n";
+}
+
+echo "\n-- Testing array_column() index key parameter should be a string or integer (testing array) --\n";
+try {
+    var_dump(array_column([['php' => 7, 'foo'], ['php' => 8, 'bar']], 'php', array()));
+} catch (\TypeError $e) {
+    echo $e->getMessage() . "\n";
+}
+
+?>
+
+DONE
+--EXPECT--
+-- Testing array_column() column key parameter should be a string or an integer (testing bool) --
+array(2) {
+  [0]=>
+  string(4) "php7"
+  [1]=>
+  string(4) "php8"
+}
+array(2) {
+  [0]=>
+  string(3) "foo"
+  [1]=>
+  string(3) "bar"
+}
+
+-- Testing array_column() column key parameter should be a string or integer (testing array) --
+array_column(): Argument #2 ($column_key) must be of type string|int|null, array given
+
+-- Testing array_column() index key parameter should be a string or an integer (testing bool) --
+array(2) {
+  ["foo"]=>
+  int(7)
+  ["bar"]=>
+  int(8)
+}
+array(2) {
+  [0]=>
+  int(7)
+  [1]=>
+  int(8)
+}
+
+-- Testing array_column() index key parameter should be a string or integer (testing array) --
+array_column(): Argument #3 ($index_key) must be of type string|int|null, array given
+
+DONE