Skip to content

Commit eaad7ce

Browse files
committed
Make array() a function
1 parent 1a9a2c7 commit eaad7ce

11 files changed

Lines changed: 198 additions & 10 deletions

Zend/zend_API.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3634,6 +3634,7 @@ static void zend_disable_function(const char *function_name, size_t function_nam
36343634
(function_name_length == strlen("exit") && !memcmp(function_name, "exit", strlen("exit")))
36353635
|| (function_name_length == strlen("die") && !memcmp(function_name, "die", strlen("die")))
36363636
|| (function_name_length == strlen("clone") && !memcmp(function_name, "clone", strlen("clone")))
3637+
|| (function_name_length == strlen("array") && !memcmp(function_name, "array", strlen("array")))
36373638
)) {
36383639
zend_error(E_WARNING, "Cannot disable function %s()", function_name);
36393640
return;

Zend/zend_ast.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2285,9 +2285,22 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
22852285
zend_ast_export_list(str, zend_ast_get_list(ast), true, 20, indent);
22862286
break;
22872287
case ZEND_AST_ARRAY:
2288-
smart_str_appendc(str, '[');
2289-
zend_ast_export_list(str, zend_ast_get_list(ast), true, 20, indent);
2290-
smart_str_appendc(str, ']');
2288+
switch (ast->attr) {
2289+
case ZEND_ARRAY_SYNTAX_FUNCTION:
2290+
smart_str_appendc(str, '\\');
2291+
/* fallthrough */
2292+
case ZEND_ARRAY_SYNTAX_LONG:
2293+
smart_str_appends(str, "array(");
2294+
zend_ast_export_list(str, zend_ast_get_list(ast), true, 20, indent);
2295+
smart_str_appendc(str, ')');
2296+
break;
2297+
case ZEND_ARRAY_SYNTAX_SHORT:
2298+
default:
2299+
smart_str_appendc(str, '[');
2300+
zend_ast_export_list(str, zend_ast_get_list(ast), true, 20, indent);
2301+
smart_str_appendc(str, ']');
2302+
break;
2303+
}
22912304
break;
22922305
case ZEND_AST_ENCAPS_LIST:
22932306
smart_str_appendc(str, '"');

Zend/zend_builtin_functions.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,43 @@ ZEND_FUNCTION(clone)
117117
}
118118
}
119119

120+
ZEND_FUNCTION(array)
121+
{
122+
zval *args;
123+
uint32_t argc;
124+
HashTable *named_params;
125+
126+
ZEND_PARSE_PARAMETERS_START(0, -1)
127+
Z_PARAM_VARIADIC_WITH_NAMED(args, argc, named_params);
128+
ZEND_PARSE_PARAMETERS_END();
129+
130+
if (EXPECTED(argc == 0)) {
131+
if (EXPECTED(named_params != NULL)) {
132+
GC_ADDREF(named_params);
133+
RETURN_ARR(named_params);
134+
} else {
135+
RETURN_EMPTY_ARRAY();
136+
}
137+
} else {
138+
HashTable *entries;
139+
140+
ALLOC_HASHTABLE(entries);
141+
zend_hash_init(entries, argc + (named_params ? zend_hash_num_elements(named_params) : 0), NULL, NULL, false);
142+
for (uint32_t i = 0; i < argc; i++) {
143+
zend_hash_index_add_new(entries, i, &args[i]);
144+
}
145+
if (named_params != NULL) {
146+
zend_string *key;
147+
zval *val;
148+
ZEND_HASH_FOREACH_STR_KEY_VAL(named_params, key, val) {
149+
zend_hash_update(entries, key, val);
150+
} ZEND_HASH_FOREACH_END();
151+
}
152+
153+
RETURN_ARR(entries);
154+
}
155+
}
156+
120157
ZEND_FUNCTION(exit)
121158
{
122159
zend_string *str = NULL;

Zend/zend_builtin_functions.stub.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ class stdClass
1010
/** @refcount 1 */
1111
function clone(object $object, array $withProperties = []): object {}
1212

13+
/** @compile-time-eval */
14+
function _array(mixed ...$entries): array {}
15+
1316
function exit(string|int $status = 0): never {}
1417

1518
/** @alias exit */

Zend/zend_builtin_functions_arginfo.h

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Zend/zend_compile.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5221,6 +5221,22 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg
52215221
return SUCCESS;
52225222
}
52235223

5224+
static zend_result zend_compile_func_array(znode *result, zend_ast_list *args)
5225+
{
5226+
zend_ast *array = zend_ast_create_list(0, ZEND_AST_ARRAY);
5227+
for (uint32_t i = 0; i < args->children; i++) {
5228+
zend_ast *arg = args->child[i];
5229+
if (arg->kind == ZEND_AST_NAMED_ARG) {
5230+
array = zend_ast_list_add(array, zend_ast_create(ZEND_AST_ARRAY_ELEM, arg->child[1], arg->child[0]));
5231+
} else {
5232+
array = zend_ast_list_add(array, zend_ast_create(ZEND_AST_ARRAY_ELEM, arg, NULL));
5233+
}
5234+
}
5235+
zend_compile_expr(result, array);
5236+
5237+
return SUCCESS;
5238+
}
5239+
52245240
static zend_result zend_try_compile_special_func_ex(znode *result, zend_string *lcname, zend_ast_list *args, uint32_t type, uint32_t lineno) /* {{{ */
52255241
{
52265242
if (zend_string_equals_literal(lcname, "strlen")) {
@@ -5295,6 +5311,8 @@ static zend_result zend_try_compile_special_func_ex(znode *result, zend_string *
52955311
return zend_compile_func_clone(result, args);
52965312
} else if (zend_string_equals_literal(lcname, "array_map")) {
52975313
return zend_compile_func_array_map(result, args, lcname, lineno);
5314+
} else if (zend_string_equals_literal(lcname, "array")) {
5315+
return zend_compile_func_array(result, args);
52985316
} else {
52995317
return FAILURE;
53005318
}

Zend/zend_compile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,7 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
10691069
#define ZEND_ARRAY_SYNTAX_LIST 1 /* list() */
10701070
#define ZEND_ARRAY_SYNTAX_LONG 2 /* array() */
10711071
#define ZEND_ARRAY_SYNTAX_SHORT 3 /* [] */
1072+
#define ZEND_ARRAY_SYNTAX_FUNCTION 4 /* array(key: "val") */
10721073

10731074
/* var status for backpatching */
10741075
#define BP_VAR_R 0

Zend/zend_language_parser.y

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
287287
%type <ast> enum_declaration_statement enum_backing_type enum_case enum_case_expr
288288
%type <ast> function_name non_empty_member_modifiers
289289
%type <ast> property_hook property_hook_list optional_property_hook_list hooked_property property_hook_body
290-
%type <ast> optional_parameter_list clone_argument_list non_empty_clone_argument_list
290+
%type <ast> optional_parameter_list clone_argument_list non_empty_clone_argument_list non_empty_array_function_argument_list array_function_argument
291291

292292
%type <num> returns_ref function fn is_reference is_variadic property_modifiers property_hook_modifiers
293293
%type <num> method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers
@@ -1494,11 +1494,33 @@ ctor_arguments:
14941494

14951495
dereferenceable_scalar:
14961496
T_ARRAY '(' array_pair_list ')' { $$ = $3; $$->attr = ZEND_ARRAY_SYNTAX_LONG; }
1497+
| T_ARRAY '(' non_empty_array_function_argument_list possible_comma ')'
1498+
{
1499+
zend_ast *name = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_ARRAY));
1500+
name->attr = ZEND_NAME_FQ;
1501+
$$ = zend_ast_create(ZEND_AST_CALL, name, $3);
1502+
}
14971503
| '[' array_pair_list ']' { $$ = $2; $$->attr = ZEND_ARRAY_SYNTAX_SHORT; }
14981504
| T_CONSTANT_ENCAPSED_STRING { $$ = $1; }
14991505
| '"' encaps_list '"' { $$ = $2; }
15001506
;
15011507

1508+
non_empty_array_function_argument_list:
1509+
array_function_argument
1510+
{ $$ = zend_ast_create_arg_list(1, ZEND_AST_ARG_LIST, $1); }
1511+
| non_empty_array_function_argument_list ',' array_function_argument
1512+
{ $$ = zend_ast_arg_list_add($1, $3); }
1513+
;
1514+
1515+
array_function_argument:
1516+
identifier ':' expr
1517+
{ $$ = zend_ast_create(ZEND_AST_NAMED_ARG, $1, $3); }
1518+
| T_ELLIPSIS
1519+
{ $$ = zend_ast_create_ex(ZEND_AST_PLACEHOLDER_ARG, ZEND_PLACEHOLDER_VARIADIC); }
1520+
| identifier ':' '?'
1521+
{ $$ = zend_ast_create(ZEND_AST_NAMED_ARG, $1, zend_ast_create(ZEND_AST_PLACEHOLDER_ARG)); }
1522+
;
1523+
15021524
scalar:
15031525
T_LNUMBER { $$ = $1; }
15041526
| T_DNUMBER { $$ = $1; }

build/gen_stub.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -926,9 +926,14 @@ public function isDestructor(): bool;
926926

927927
class FunctionName implements FunctionOrMethodName {
928928

929-
public function __construct(
930-
private readonly Name $name,
931-
) {}
929+
private readonly Name $name;
930+
931+
public function __construct(Name $name) {
932+
if ($name->name === '_array') {
933+
$name = new Name('array', $name->getAttributes());
934+
}
935+
$this->name = $name;
936+
}
932937

933938
public function getNamespace(): ?string {
934939
if ($this->name->isQualified()) {
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
--TEST--
2+
Test array() function
3+
--FILE--
4+
<?php
5+
6+
var_dump(\array(1, 2, 3));
7+
var_dump(\array(foo: "bar", baz: "quux"));
8+
9+
try {
10+
assert(false && \array(foo: "bar", baz: "quux"));
11+
} catch (AssertionError $e) {
12+
echo $e->getMessage(), PHP_EOL;
13+
}
14+
15+
var_dump(array_map(\array(...), [1, 2, 3]));
16+
17+
var_dump(array(foo: "bar", baz: "quux"));
18+
19+
try {
20+
assert(false && array(foo: "bar", baz: "quux"));
21+
} catch (AssertionError $e) {
22+
echo $e->getMessage(), PHP_EOL;
23+
}
24+
25+
var_dump(array_map(array(...), [1, 2, 3]));
26+
27+
?>
28+
--EXPECT--
29+
array(3) {
30+
[0]=>
31+
int(1)
32+
[1]=>
33+
int(2)
34+
[2]=>
35+
int(3)
36+
}
37+
array(2) {
38+
["foo"]=>
39+
string(3) "bar"
40+
["baz"]=>
41+
string(4) "quux"
42+
}
43+
assert(false && \array(foo: 'bar', baz: 'quux'))
44+
array(3) {
45+
[0]=>
46+
array(1) {
47+
[0]=>
48+
int(1)
49+
}
50+
[1]=>
51+
array(1) {
52+
[0]=>
53+
int(2)
54+
}
55+
[2]=>
56+
array(1) {
57+
[0]=>
58+
int(3)
59+
}
60+
}
61+
array(2) {
62+
["foo"]=>
63+
string(3) "bar"
64+
["baz"]=>
65+
string(4) "quux"
66+
}
67+
assert(false && \array(foo: 'bar', baz: 'quux'))
68+
array(3) {
69+
[0]=>
70+
array(1) {
71+
[0]=>
72+
int(1)
73+
}
74+
[1]=>
75+
array(1) {
76+
[0]=>
77+
int(2)
78+
}
79+
[2]=>
80+
array(1) {
81+
[0]=>
82+
int(3)
83+
}
84+
}

0 commit comments

Comments
 (0)