diff --git a/ext/standard/array.c b/ext/standard/array.c index 25259c47d61b..10330bdbb601 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -6511,13 +6511,19 @@ PHP_FUNCTION(array_filter) Z_PARAM_LONG(use_type) ZEND_PARSE_PARAMETERS_END(); + bool pack_result = use_type & ARRAY_FILTER_PACK_RESULT; + use_type &= ~ARRAY_FILTER_PACK_RESULT; + switch (use_type) { case ARRAY_FILTER_USE_VALUE: case ARRAY_FILTER_USE_BOTH: case ARRAY_FILTER_USE_KEY: break; default: - zend_argument_value_error(3, "must be one of ARRAY_FILTER_USE_VALUE, ARRAY_FILTER_USE_KEY, or ARRAY_FILTER_USE_BOTH"); + zend_argument_value_error( + 3, + "must be one of ARRAY_FILTER_USE_VALUE, ARRAY_FILTER_USE_KEY, or ARRAY_FILTER_USE_BOTH (optionally combined with ARRAY_FILTER_PACK_RESULT)" + ); RETURN_THROWS(); } @@ -6567,7 +6573,9 @@ PHP_FUNCTION(array_filter) continue; } - if (string_key) { + if (pack_result) { + operand = zend_hash_next_index_insert(Z_ARRVAL_P(return_value), operand); + } else if (string_key) { operand = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, operand); } else { operand = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, operand); diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 1999c9b92be1..ba523e430962 100644 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -132,6 +132,11 @@ enum SortDirection { * @cvalue ARRAY_FILTER_USE_KEY */ const ARRAY_FILTER_USE_KEY = UNKNOWN; +/** + * @var int + * @cvalue ARRAY_FILTER_PACK_RESULT + */ +const ARRAY_FILTER_PACK_RESULT = UNKNOWN; /* assert.c */ diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index e51a837ffa4d..28bbf5ab9dce 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit basic_functions.stub.php instead. - * Stub hash: 36b71aa7bbfe478a5e4af400b2822a77067efa2f + * Stub hash: 83a3b55b24a7707081e98c8dc2f926c8029c7d1e * Has decl header: yes */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) @@ -3542,6 +3542,7 @@ static void register_basic_functions_symbols(int module_number) REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_VALUE", ARRAY_FILTER_USE_VALUE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_BOTH", ARRAY_FILTER_USE_BOTH, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_KEY", ARRAY_FILTER_USE_KEY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("ARRAY_FILTER_PACK_RESULT", ARRAY_FILTER_PACK_RESULT, CONST_PERSISTENT); zend_constant *const_ASSERT_ACTIVE = REGISTER_LONG_CONSTANT("ASSERT_ACTIVE", PHP_ASSERT_ACTIVE, CONST_PERSISTENT | CONST_DEPRECATED); zend_constant *const_ASSERT_CALLBACK = REGISTER_LONG_CONSTANT("ASSERT_CALLBACK", PHP_ASSERT_CALLBACK, CONST_PERSISTENT | CONST_DEPRECATED); zend_constant *const_ASSERT_BAIL = REGISTER_LONG_CONSTANT("ASSERT_BAIL", PHP_ASSERT_BAIL, CONST_PERSISTENT | CONST_DEPRECATED); diff --git a/ext/standard/basic_functions_decl.h b/ext/standard/basic_functions_decl.h index b3eb25c5d988..67815aad779b 100644 --- a/ext/standard/basic_functions_decl.h +++ b/ext/standard/basic_functions_decl.h @@ -1,8 +1,8 @@ /* This is a generated file, edit basic_functions.stub.php instead. - * Stub hash: 36b71aa7bbfe478a5e4af400b2822a77067efa2f */ + * Stub hash: 83a3b55b24a7707081e98c8dc2f926c8029c7d1e */ -#ifndef ZEND_BASIC_FUNCTIONS_DECL_36b71aa7bbfe478a5e4af400b2822a77067efa2f_H -#define ZEND_BASIC_FUNCTIONS_DECL_36b71aa7bbfe478a5e4af400b2822a77067efa2f_H +#ifndef ZEND_BASIC_FUNCTIONS_DECL_83a3b55b24a7707081e98c8dc2f926c8029c7d1e_H +#define ZEND_BASIC_FUNCTIONS_DECL_83a3b55b24a7707081e98c8dc2f926c8029c7d1e_H typedef enum zend_enum_SortDirection { ZEND_ENUM_SortDirection_Ascending = 1, @@ -20,4 +20,4 @@ typedef enum zend_enum_RoundingMode { ZEND_ENUM_RoundingMode_PositiveInfinity = 8, } zend_enum_RoundingMode; -#endif /* ZEND_BASIC_FUNCTIONS_DECL_36b71aa7bbfe478a5e4af400b2822a77067efa2f_H */ +#endif /* ZEND_BASIC_FUNCTIONS_DECL_83a3b55b24a7707081e98c8dc2f926c8029c7d1e_H */ diff --git a/ext/standard/php_array.h b/ext/standard/php_array.h index ab6c8494dd4e..360324bb71bb 100644 --- a/ext/standard/php_array.h +++ b/ext/standard/php_array.h @@ -60,6 +60,7 @@ PHPAPI bool php_array_pick_keys(php_random_algo_with_state engine, zval *input, #define ARRAY_FILTER_USE_VALUE 0 #define ARRAY_FILTER_USE_BOTH 1 #define ARRAY_FILTER_USE_KEY 2 +#define ARRAY_FILTER_PACK_RESULT 4 extern PHPAPI zend_class_entry *sort_direction_ce; diff --git a/ext/standard/tests/array/array_filter_invalid_mode.phpt b/ext/standard/tests/array/array_filter_invalid_mode.phpt index b312bcfe5a23..f97ea1053862 100644 --- a/ext/standard/tests/array/array_filter_invalid_mode.phpt +++ b/ext/standard/tests/array/array_filter_invalid_mode.phpt @@ -12,5 +12,5 @@ try { echo "Done" ?> --EXPECT-- -ValueError: array_filter(): Argument #3 ($mode) must be one of ARRAY_FILTER_USE_VALUE, ARRAY_FILTER_USE_KEY, or ARRAY_FILTER_USE_BOTH +ValueError: array_filter(): Argument #3 ($mode) must be one of ARRAY_FILTER_USE_VALUE, ARRAY_FILTER_USE_KEY, or ARRAY_FILTER_USE_BOTH (optionally combined with ARRAY_FILTER_PACK_RESULT) Done diff --git a/ext/standard/tests/array/array_filter_pack.phpt b/ext/standard/tests/array/array_filter_pack.phpt new file mode 100644 index 000000000000..fdddc3f53886 --- /dev/null +++ b/ext/standard/tests/array/array_filter_pack.phpt @@ -0,0 +1,121 @@ +--TEST-- +array_filter() tests with ARRAY_FILTER_PACK_RESULT +--FILE-- + 'false key empty string', + false, +]; +// to filter based on a truthy key requires an explicit callback +var_dump( array_filter( $array2, fn ($v) => $v, ARRAY_FILTER_USE_KEY ) ); +var_dump( array_filter( $array2, fn ($v) => $v, ARRAY_FILTER_USE_KEY | ARRAY_FILTER_PACK_RESULT ) ); + +$array3 = [ + 'key1' => true, + 'key2' => false, + 'key3' => 123, +]; +function filter( $value, $key ) { + $value = var_export( $value, true ); + echo 'Filter: $key => $value\n'; + return true; +} +var_dump( array_filter( $array3, filter(...), ARRAY_FILTER_USE_BOTH ) ); +var_dump( array_filter( $array3, filter(...), ARRAY_FILTER_USE_BOTH | ARRAY_FILTER_PACK_RESULT ) ); + +$array4 = [ + 123, + true, + false, + (object)[], + [], + 456, + '123', +]; +var_dump( array_filter( $array4, is_int(...) ) ); +var_dump( array_filter( $array4, is_int(...), ARRAY_FILTER_PACK_RESULT ) ); + +$array4 = [ + 'first int' => 123, + 'true' => true, + 'false' => false, + 'object' => (object)[], + 'array' => [], + 'second int' => 456, + 'int-looking string' => '123', +]; +var_dump( array_filter( $array4, is_int(...) ) ); +var_dump( array_filter( $array4, is_int(...), ARRAY_FILTER_PACK_RESULT ) ); + +?> +--EXPECT-- +array(3) { + [2]=> + bool(true) + [4]=> + string(3) "foo" + [6]=> + int(123) +} +array(3) { + [0]=> + bool(true) + [1]=> + string(3) "foo" + [2]=> + int(123) +} +array(1) { + [1]=> + bool(false) +} +array(1) { + [0]=> + bool(false) +} +Filter: $key => $value\nFilter: $key => $value\nFilter: $key => $value\narray(3) { + ["key1"]=> + bool(true) + ["key2"]=> + bool(false) + ["key3"]=> + int(123) +} +Filter: $key => $value\nFilter: $key => $value\nFilter: $key => $value\narray(3) { + [0]=> + bool(true) + [1]=> + bool(false) + [2]=> + int(123) +} +array(2) { + [0]=> + int(123) + [5]=> + int(456) +} +array(2) { + [0]=> + int(123) + [1]=> + int(456) +} +array(2) { + ["first int"]=> + int(123) + ["second int"]=> + int(456) +} +array(2) { + [0]=> + int(123) + [1]=> + int(456) +}