From: George Peter Banyard Date: Mon, 27 Apr 2020 18:30:07 +0000 (+0200) Subject: Use ZPP check for string|int|null arguments in array_column() X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=34f727e63716dfb798865289c079b017812ad03b;p=php Use ZPP check for string|int|null arguments in array_column() --- diff --git a/ext/standard/array.c b/ext/standard/array.c index 09756b74e5..3e54946cd0 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -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)) { diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index c8c473d0db..09c3513b68 100755 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -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 {} diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 985c57bcac..bc26e4ad97 100755 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -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) diff --git a/ext/standard/tests/array/array_column_error.phpt b/ext/standard/tests/array/array_column_scalar_index_strict_types.phpt 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 547ecf91cb..de98d5d999 100644 --- a/ext/standard/tests/array/array_column_error.phpt +++ b/ext/standard/tests/array/array_column_scalar_index_strict_types.phpt @@ -1,7 +1,8 @@ --TEST-- -Test array_column() function: error conditions +Test array_column(): Index argument with various types in strict type mode --FILE-- 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 index 0000000000..ae8c248a59 --- /dev/null +++ b/ext/standard/tests/array/array_column_scalar_index_weak_types.phpt @@ -0,0 +1,88 @@ +--TEST-- +Test array_column(): Index argument with various types in weak type mode +--FILE-- +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