diff --git a/Zend/zend_API.c b/Zend/zend_API.c index df8b4252c42ad..cb47c03466004 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3649,6 +3649,7 @@ static void zend_disable_function(const char *function_name, size_t function_nam (function_name_length == strlen("exit") && !memcmp(function_name, "exit", strlen("exit"))) || (function_name_length == strlen("die") && !memcmp(function_name, "die", strlen("die"))) || (function_name_length == strlen("clone") && !memcmp(function_name, "clone", strlen("clone"))) + || (function_name_length == strlen("array") && !memcmp(function_name, "array", strlen("array"))) )) { zend_error(E_WARNING, "Cannot disable function %s()", function_name); return; diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 728695bd9e930..c8d866939b938 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -2173,9 +2173,20 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent); break; case ZEND_AST_ARRAY: - smart_str_appendc(str, '['); - zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent); - smart_str_appendc(str, ']'); + switch (ast->attr) { + case ZEND_ARRAY_SYNTAX_LONG: + case ZEND_ARRAY_SYNTAX_FUNCTION: + smart_str_appends(str, "array("); + zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent); + smart_str_appendc(str, ')'); + break; + case ZEND_ARRAY_SYNTAX_SHORT: + default: + smart_str_appendc(str, '['); + zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent); + smart_str_appendc(str, ']'); + break; + } break; case ZEND_AST_ENCAPS_LIST: smart_str_appendc(str, '"'); diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 48e5c70897294..30f33146c7984 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -112,6 +112,43 @@ ZEND_FUNCTION(clone) } } +ZEND_FUNCTION(array) +{ + zval *args; + uint32_t argc; + HashTable *named_params; + + ZEND_PARSE_PARAMETERS_START(0, -1) + Z_PARAM_VARIADIC_WITH_NAMED(args, argc, named_params); + ZEND_PARSE_PARAMETERS_END(); + + if (EXPECTED(argc == 0)) { + if (EXPECTED(named_params != NULL)) { + GC_ADDREF(named_params); + RETURN_ARR(named_params); + } else { + RETURN_EMPTY_ARRAY(); + } + } else { + HashTable *entries; + + ALLOC_HASHTABLE(entries); + zend_hash_init(entries, argc + (named_params ? zend_hash_num_elements(named_params) : 0), NULL, NULL, false); + for (uint32_t i = 0; i < argc; i++) { + zend_hash_index_add_new(entries, i, &args[i]); + } + if (named_params != NULL) { + zend_string *key; + zval *val; + ZEND_HASH_FOREACH_STR_KEY_VAL(named_params, key, val) { + zend_hash_update(entries, key, val); + } ZEND_HASH_FOREACH_END(); + } + + RETURN_ARR(entries); + } +} + ZEND_FUNCTION(exit) { zend_string *str = NULL; diff --git a/Zend/zend_builtin_functions.stub.php b/Zend/zend_builtin_functions.stub.php index 256c405c71c28..cc613b6470776 100644 --- a/Zend/zend_builtin_functions.stub.php +++ b/Zend/zend_builtin_functions.stub.php @@ -10,6 +10,8 @@ class stdClass /** @refcount 1 */ function _clone(object $object): object {} +function _array(mixed ...$entries): array {} + function exit(string|int $status = 0): never {} /** @alias exit */ diff --git a/Zend/zend_builtin_functions_arginfo.h b/Zend/zend_builtin_functions_arginfo.h index 1c595ecd5777c..491ae9babc6d5 100644 --- a/Zend/zend_builtin_functions_arginfo.h +++ b/Zend/zend_builtin_functions_arginfo.h @@ -1,10 +1,14 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 12327caa3fe940ccef68ed99f9278982dc0173a5 */ + * Stub hash: 21d2300dd16bf696c65933c88cc2b895ecc7adcf */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_clone, 0, 1, IS_OBJECT, 0) ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_array, 0, 0, IS_ARRAY, 0) + ZEND_ARG_VARIADIC_TYPE_INFO(0, entries, IS_MIXED, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_exit, 0, 0, IS_NEVER, 0) ZEND_ARG_TYPE_MASK(0, status, MAY_BE_STRING|MAY_BE_LONG, "0") ZEND_END_ARG_INFO() @@ -248,6 +252,7 @@ static const zend_frameless_function_info frameless_function_infos_class_exists[ }; ZEND_FUNCTION(clone); +ZEND_FUNCTION(array); ZEND_FUNCTION(exit); ZEND_FUNCTION(zend_version); ZEND_FUNCTION(func_num_args); @@ -312,6 +317,7 @@ ZEND_FUNCTION(gc_status); static const zend_function_entry ext_functions[] = { ZEND_FE(clone, arginfo_clone) + ZEND_FE(array, arginfo_array) ZEND_FE(exit, arginfo_exit) ZEND_RAW_FENTRY("die", zif_exit, arginfo_die, 0, NULL, NULL) ZEND_FE(zend_version, arginfo_zend_version) diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 62d0fbcded2ee..ca5dde972dc34 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -1053,6 +1053,7 @@ ZEND_API zend_string *zend_type_to_string(zend_type type); #define ZEND_ARRAY_SYNTAX_LIST 1 /* list() */ #define ZEND_ARRAY_SYNTAX_LONG 2 /* array() */ #define ZEND_ARRAY_SYNTAX_SHORT 3 /* [] */ +#define ZEND_ARRAY_SYNTAX_FUNCTION 4 /* array(key: "val") */ /* var status for backpatching */ #define BP_VAR_R 0 diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 016c6e5c9d098..d7057d59ccd75 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -288,6 +288,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type function_name non_empty_member_modifiers %type property_hook property_hook_list optional_property_hook_list hooked_property property_hook_body %type optional_parameter_list +%type non_empty_array_function_argument_list array_function_argument %type returns_ref function fn is_reference is_variadic property_modifiers property_hook_modifiers %type method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers @@ -1460,11 +1461,30 @@ ctor_arguments: dereferenceable_scalar: T_ARRAY '(' array_pair_list ')' { $$ = $3; $$->attr = ZEND_ARRAY_SYNTAX_LONG; } + | T_ARRAY '(' non_empty_array_function_argument_list possible_comma ')' + { $$ = $3; $$->attr = ZEND_ARRAY_SYNTAX_FUNCTION; } + | T_ARRAY '(' T_ELLIPSIS ')' { + zend_ast *name = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_ARRAY)); + name->attr = ZEND_NAME_FQ; + $$ = zend_ast_create(ZEND_AST_CALL, name, zend_ast_create_fcc()); + } | '[' array_pair_list ']' { $$ = $2; $$->attr = ZEND_ARRAY_SYNTAX_SHORT; } | T_CONSTANT_ENCAPSED_STRING { $$ = $1; } | '"' encaps_list '"' { $$ = $2; } ; +non_empty_array_function_argument_list: + array_function_argument + { $$ = zend_ast_create_list(1, ZEND_AST_ARRAY, $1); } + | non_empty_array_function_argument_list ',' array_function_argument + { $$ = zend_ast_list_add($1, $3); } +; + +array_function_argument: + identifier ':' expr + { $$ = zend_ast_create(ZEND_AST_NAMED_ARG, $1, $3); } +; + scalar: T_LNUMBER { $$ = $1; } | T_DNUMBER { $$ = $1; } diff --git a/build/gen_stub.php b/build/gen_stub.php index a3dd14a562346..6db956fa30fbe 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -1012,6 +1012,9 @@ public function __construct(Name $name) { if ($name->name === '_clone') { $name = new Name('clone', $name->getAttributes()); } + if ($name->name === '_array') { + $name = new Name('array', $name->getAttributes()); + } $this->name = $name; } diff --git a/ext/standard/tests/array/array_function.phpt b/ext/standard/tests/array/array_function.phpt new file mode 100644 index 0000000000000..dddc4ef8d0626 --- /dev/null +++ b/ext/standard/tests/array/array_function.phpt @@ -0,0 +1,56 @@ +--TEST-- +Test array() function +--FILE-- +getMessage(), PHP_EOL; +} + +var_dump(array_map(array(...), [1, 2, 3])); + +?> +--EXPECT-- +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} +array(2) { + ["foo"]=> + string(3) "bar" + ["baz"]=> + string(4) "quux" +} +array(2) { + ["bar"]=> + string(3) "foo" + ["quux"]=> + string(3) "baz" +} +assert(false && \array(foo: 'bar', baz: 'quux')) +array(3) { + [0]=> + array(1) { + [0]=> + int(1) + } + [1]=> + array(1) { + [0]=> + int(2) + } + [2]=> + array(1) { + [0]=> + int(3) + } +} diff --git a/ext/standard/tests/array/array_map_variation16.phpt b/ext/standard/tests/array/array_map_variation16.phpt index 7ee1f04967c5c..5a0c648c7cc14 100644 --- a/ext/standard/tests/array/array_map_variation16.phpt +++ b/ext/standard/tests/array/array_map_variation16.phpt @@ -8,7 +8,6 @@ $arg = [1, 2]; // built-in functions & language constructs $callbacks = [ 'echo', - 'array', 'empty', 'eval', 'isset', @@ -29,7 +28,6 @@ echo "Done"; ?> --EXPECT-- array_map(): Argument #1 ($callback) must be a valid callback or null, function "echo" not found or invalid function name -array_map(): Argument #1 ($callback) must be a valid callback or null, function "array" not found or invalid function name array_map(): Argument #1 ($callback) must be a valid callback or null, function "empty" not found or invalid function name array_map(): Argument #1 ($callback) must be a valid callback or null, function "eval" not found or invalid function name array_map(): Argument #1 ($callback) must be a valid callback or null, function "isset" not found or invalid function name