summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--UPGRADING2
-rw-r--r--Zend/tests/assert/expect_015.phpt2
-rw-r--r--Zend/tests/list_001.phpt6
-rw-r--r--Zend/tests/list_008.phpt10
-rw-r--r--Zend/tests/list_009.phpt14
-rw-r--r--Zend/tests/list_010.phpt11
-rw-r--r--Zend/tests/list_011.phpt10
-rw-r--r--Zend/tests/list_012.phpt10
-rw-r--r--Zend/tests/list_mixed_keyed_unkeyed.phpt2
-rw-r--r--Zend/zend_ast.c5
-rw-r--r--Zend/zend_ast.h1
-rw-r--r--Zend/zend_compile.c99
-rw-r--r--Zend/zend_language_parser.y69
14 files changed, 164 insertions, 78 deletions
diff --git a/NEWS b/NEWS
index 6ae33d3700..5bb69bd52a 100644
--- a/NEWS
+++ b/NEWS
@@ -32,6 +32,7 @@ PHP NEWS
. Implemented the RFC `Catching multiple exception types`. (Bronislaw Bialek,
Pierrick)
. Raise a compile-time warning on octal escape sequence overflow. (Sara)
+ . Added [] = as alternative construct to list() =. (Bob)
- FTP:
. Implemented FR #55651 (Option to ignore the returned FTP PASV address).
diff --git a/UPGRADING b/UPGRADING
index e2bac16423..dcf80a6029 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -43,6 +43,8 @@ PHP 7.1 UPGRADE NOTES
(RFC: https://wiki.php.net/rfc/negative-string-offsets)
. Added a form of the list() construct where keys can be specified.
(RFC: https://wiki.php.net/rfc/list_keys)
+ . Added [] = as alternative construct to list() =.
+ (RFC: https://wiki.php.net/rfc/short_list_syntax)
. Number operators taking numeric strings now emit "A non well formed numeric
value encountered" E_NOTICEs for leading-numeric strings, and "A
non-numeric value encountered" E_WARNINGs for non-numeric strings.
diff --git a/Zend/tests/assert/expect_015.phpt b/Zend/tests/assert/expect_015.phpt
index 030913f7f9..0c53c75c4d 100644
--- a/Zend/tests/assert/expect_015.phpt
+++ b/Zend/tests/assert/expect_015.phpt
@@ -154,7 +154,7 @@ Warning: assert(): assert(0 && ($a = function () {
$x = $a ? $b : $c;
$x = $a ?: $c;
$x = $a ?? $b;
- list($a, $b, $c) = [1, 2 => 'x', 'z' => 'c'];
+ [$a, $b, $c] = [1, 2 => 'x', 'z' => 'c'];
@foo();
$y = clone $x;
yield 1 => 2;
diff --git a/Zend/tests/list_001.phpt b/Zend/tests/list_001.phpt
index a9fff55004..4e0053edee 100644
--- a/Zend/tests/list_001.phpt
+++ b/Zend/tests/list_001.phpt
@@ -5,6 +5,8 @@
list($a, list($b)) = array(new stdclass, array(new stdclass));
var_dump($a, $b);
+[$a, [$b]] = array(new stdclass, array(new stdclass));
+var_dump($a, $b);
?>
--EXPECT--
@@ -12,3 +14,7 @@ object(stdClass)#1 (0) {
}
object(stdClass)#2 (0) {
}
+object(stdClass)#3 (0) {
+}
+object(stdClass)#4 (0) {
+}
diff --git a/Zend/tests/list_008.phpt b/Zend/tests/list_008.phpt
new file mode 100644
index 0000000000..de8160c77e
--- /dev/null
+++ b/Zend/tests/list_008.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Assignment to invalid list() value
+--FILE--
+<?php
+
+[42] = [1];
+
+?>
+--EXPECTF--
+Fatal error: Assignments can only happen to writable values in %s on line %d
diff --git a/Zend/tests/list_009.phpt b/Zend/tests/list_009.phpt
new file mode 100644
index 0000000000..c28ca8000a
--- /dev/null
+++ b/Zend/tests/list_009.phpt
@@ -0,0 +1,14 @@
+--TEST--
+list with by-reference assignment should fail
+--FILE--
+<?php
+
+$a = [1];
+[&$foo] = $a;
+$foo = 2;
+
+var_dump($a);
+
+?>
+--EXPECTF--
+Fatal error: [] and list() assignments cannot be by reference in %s on line %d
diff --git a/Zend/tests/list_010.phpt b/Zend/tests/list_010.phpt
new file mode 100644
index 0000000000..a89ffda5cd
--- /dev/null
+++ b/Zend/tests/list_010.phpt
@@ -0,0 +1,11 @@
+--TEST--
+Do not allow mixing [] and list()
+--FILE--
+<?php
+
+list([$a]) = [[1]];
+var_dump($a);
+
+?>
+--EXPECTF--
+Fatal error: Cannot mix [] and list() in %s on line %d
diff --git a/Zend/tests/list_011.phpt b/Zend/tests/list_011.phpt
new file mode 100644
index 0000000000..316498c411
--- /dev/null
+++ b/Zend/tests/list_011.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Disallow list() usage as if it were an array
+--FILE--
+<?php
+
+var_dump(list(1, 2, 3));
+
+?>
+--EXPECTF--
+Parse error: syntax error, unexpected ')', expecting '=' in %s on line %d
diff --git a/Zend/tests/list_012.phpt b/Zend/tests/list_012.phpt
new file mode 100644
index 0000000000..072d28c01d
--- /dev/null
+++ b/Zend/tests/list_012.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Disallow empty elements in normal arrays
+--FILE--
+<?php
+
+var_dump([, 1, 2]);
+
+?>
+--EXPECTF--
+Fatal error: Cannot use empty array elements in arrays in %s on line %d
diff --git a/Zend/tests/list_mixed_keyed_unkeyed.phpt b/Zend/tests/list_mixed_keyed_unkeyed.phpt
index 5562479fc3..245d20f31f 100644
--- a/Zend/tests/list_mixed_keyed_unkeyed.phpt
+++ b/Zend/tests/list_mixed_keyed_unkeyed.phpt
@@ -13,4 +13,4 @@ list($zero, 1 => $one, "foo" => $foo) = $contrivedKeyedAndUnkeyedArrayExample;
?>
--EXPECTF--
-Parse error: syntax error, unexpected %s in %s on line %d
+Fatal error: Cannot mix keyed and unkeyed array entries in assignments in %s on line %d
diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c
index b738ac7ff3..236d0783fc 100644
--- a/Zend/zend_ast.c
+++ b/Zend/zend_ast.c
@@ -1102,11 +1102,6 @@ tail_call:
simple_list:
zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent);
break;
- case ZEND_AST_LIST:
- smart_str_appends(str, "list(");
- zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent);
- smart_str_appendc(str, ')');
- break;
case ZEND_AST_ARRAY:
smart_str_appendc(str, '[');
zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent);
diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h
index ec771003c0..886b106228 100644
--- a/Zend/zend_ast.h
+++ b/Zend/zend_ast.h
@@ -42,7 +42,6 @@ enum _zend_ast_kind {
/* list nodes */
ZEND_AST_ARG_LIST = 1 << ZEND_AST_IS_LIST_SHIFT,
- ZEND_AST_LIST,
ZEND_AST_ARRAY,
ZEND_AST_ENCAPS_LIST,
ZEND_AST_EXPR_LIST,
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 7ae54da5bb..e401ce2430 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -2578,13 +2578,13 @@ static void zend_separate_if_call_and_write(znode *node, zend_ast *ast, uint32_t
void zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type);
void zend_compile_assign(znode *result, zend_ast *ast);
-static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node);
+static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node, zend_bool old_style);
static inline void zend_emit_assign_znode(zend_ast *var_ast, znode *value_node) /* {{{ */
{
znode dummy_node;
- if (var_ast->kind == ZEND_AST_LIST) {
- zend_compile_list_assign(&dummy_node, var_ast, value_node);
+ if (var_ast->kind == ZEND_AST_ARRAY) {
+ zend_compile_list_assign(&dummy_node, var_ast, value_node, var_ast->attr);
} else {
zend_ast *assign_ast = zend_ast_create(ZEND_AST_ASSIGN, var_ast,
zend_ast_create_znode(value_node));
@@ -2728,18 +2728,35 @@ void zend_compile_static_prop(znode *result, zend_ast *ast, uint32_t type, int d
}
/* }}} */
-static void zend_compile_unkeyed_list_assign(zend_ast_list *list, znode *expr_node) /* {{{ */
+static void zend_verify_list_assign_target(zend_ast *var_ast, zend_bool old_style) /* {{{ */ {
+ if (var_ast->kind == ZEND_AST_ARRAY) {
+ if (old_style != var_ast->attr) {
+ zend_error(E_COMPILE_ERROR, "Cannot mix [] and list()");
+ }
+ } else if (!zend_can_write_to_variable(var_ast)) {
+ zend_error(E_COMPILE_ERROR, "Assignments can only happen to writable values");
+ }
+}
+/* }}} */
+
+static void zend_compile_unkeyed_list_assign(zend_ast_list *list, znode *expr_node, zend_bool old_style) /* {{{ */
{
uint32_t i;
zend_bool has_elems = 0;
for (i = 0; i < list->children; ++i) {
- zend_ast *var_ast = list->child[i];
+ zend_ast *elem_ast = list->child[i];
+ zend_ast *var_ast;
znode fetch_result, dim_node;
- if (var_ast == NULL) {
+ if (elem_ast == NULL) {
continue;
}
+ if (elem_ast->attr) {
+ zend_error(E_COMPILE_ERROR, "[] and list() assignments cannot be by reference");
+ }
+
+ var_ast = elem_ast->child[0];
has_elems = 1;
dim_node.op_type = IS_CONST;
@@ -2749,6 +2766,12 @@ static void zend_compile_unkeyed_list_assign(zend_ast_list *list, znode *expr_no
Z_TRY_ADDREF(expr_node->u.constant);
}
+ if (elem_ast->child[1] != NULL) {
+ zend_error(E_COMPILE_ERROR, "Cannot mix keyed and unkeyed array entries in assignments");
+ }
+
+ zend_verify_list_assign_target(var_ast, old_style);
+
zend_emit_op(&fetch_result, ZEND_FETCH_LIST, expr_node, &dim_node);
zend_emit_assign_znode(var_ast, &fetch_result);
}
@@ -2759,7 +2782,7 @@ static void zend_compile_unkeyed_list_assign(zend_ast_list *list, znode *expr_no
}
/* }}} */
-static void zend_compile_keyed_list_assign(zend_ast_list *list, znode *expr_node) /* {{{ */
+static void zend_compile_keyed_list_assign(zend_ast_list *list, znode *expr_node, zend_bool old_style) /* {{{ */
{
uint32_t i;
@@ -2769,26 +2792,40 @@ static void zend_compile_keyed_list_assign(zend_ast_list *list, znode *expr_node
zend_ast *key_ast = pair_ast->child[1];
znode fetch_result, dim_node;
+ if (pair_ast->attr) {
+ zend_error(E_COMPILE_ERROR, "[] and list() assignments cannot be by reference");
+ }
+
zend_compile_expr(&dim_node, key_ast);
if (expr_node->op_type == IS_CONST) {
Z_TRY_ADDREF(expr_node->u.constant);
}
+ if (var_ast == NULL) {
+ zend_error(E_COMPILE_ERROR, "Cannot use empty array entries in keyed array");
+ }
+
+ if (key_ast == NULL) {
+ zend_error(E_COMPILE_ERROR, "Cannot mix keyed and unkeyed array entries in assignments");
+ }
+
+ zend_verify_list_assign_target(var_ast, old_style);
+
zend_emit_op(&fetch_result, ZEND_FETCH_LIST, expr_node, &dim_node);
zend_emit_assign_znode(var_ast, &fetch_result);
}
}
/* }}} */
-static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node) /* {{{ */
+static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node, zend_bool old_style) /* {{{ */
{
zend_ast_list *list = zend_ast_get_list(ast);
- if (list->children > 0 && list->child[0] != NULL && list->child[0]->kind == ZEND_AST_ARRAY_ELEM) {
- zend_compile_keyed_list_assign(list, expr_node);
+ if (list->children > 0 && list->child[0] != NULL && list->child[0]->child[1] != NULL /* has key */) {
+ zend_compile_keyed_list_assign(list, expr_node, old_style);
} else {
- zend_compile_unkeyed_list_assign(list, expr_node);
+ zend_compile_unkeyed_list_assign(list, expr_node, old_style);
}
*result = *expr_node;
@@ -2838,13 +2875,16 @@ zend_bool zend_list_has_assign_to(zend_ast *list_ast, zend_string *name) /* {{{
zend_ast_list *list = zend_ast_get_list(list_ast);
uint32_t i;
for (i = 0; i < list->children; i++) {
- zend_ast *var_ast = list->child[i];
- if (!var_ast) {
+ zend_ast *elem_ast = list->child[i];
+ zend_ast *var_ast;
+
+ if (!elem_ast) {
continue;
}
+ var_ast = elem_ast->child[0];
/* Recursively check nested list()s */
- if (var_ast->kind == ZEND_AST_LIST && zend_list_has_assign_to(var_ast, name)) {
+ if (var_ast->kind == ZEND_AST_ARRAY && zend_list_has_assign_to(var_ast, name)) {
return 1;
}
@@ -2926,7 +2966,7 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
zend_emit_op_data(&expr_node);
return;
- case ZEND_AST_LIST:
+ case ZEND_AST_ARRAY:
if (zend_list_has_assign_to_self(var_ast, expr_ast)) {
/* list($a, $b) = $a should evaluate the right $a first */
zend_compile_simple_var_no_cv(&expr_node, expr_ast, BP_VAR_R, 0);
@@ -2934,7 +2974,7 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
zend_compile_expr(&expr_node, expr_ast);
}
- zend_compile_list_assign(result, var_ast, &expr_node);
+ zend_compile_list_assign(result, var_ast, &expr_node, var_ast->attr);
return;
EMPTY_SWITCH_DEFAULT_CASE();
}
@@ -4327,7 +4367,7 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */
if (key_ast->kind == ZEND_AST_REF) {
zend_error_noreturn(E_COMPILE_ERROR, "Key element cannot be a reference");
}
- if (key_ast->kind == ZEND_AST_LIST) {
+ if (key_ast->kind == ZEND_AST_ARRAY) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use list as key element");
}
}
@@ -6365,14 +6405,22 @@ static zend_bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */
uint32_t i;
zend_bool is_constant = 1;
+ if (ast->attr) {
+ zend_error(E_COMPILE_ERROR, "Cannot use list() as standalone expression");
+ }
+
/* First ensure that *all* child nodes are constant and by-val */
for (i = 0; i < list->children; ++i) {
zend_ast *elem_ast = list->child[i];
- zend_bool by_ref = elem_ast->attr;
+
+ if (elem_ast == NULL) {
+ zend_error(E_COMPILE_ERROR, "Cannot use empty array elements in arrays");
+ }
+
zend_eval_const_expr(&elem_ast->child[0]);
zend_eval_const_expr(&elem_ast->child[1]);
- if (by_ref || elem_ast->child[0]->kind != ZEND_AST_ZVAL
+ if (elem_ast->attr /* by_ref */ || elem_ast->child[0]->kind != ZEND_AST_ZVAL
|| (elem_ast->child[1] && elem_ast->child[1]->kind != ZEND_AST_ZVAL)
) {
is_constant = 0;
@@ -6990,9 +7038,16 @@ void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */
for (i = 0; i < list->children; ++i) {
zend_ast *elem_ast = list->child[i];
- zend_ast *value_ast = elem_ast->child[0];
- zend_ast *key_ast = elem_ast->child[1];
- zend_bool by_ref = elem_ast->attr;
+ zend_ast *value_ast, *key_ast;
+ zend_bool by_ref;
+
+ if (elem_ast == NULL) {
+ zend_error(E_COMPILE_ERROR, "Cannot use empty array elements in arrays");
+ }
+
+ value_ast = elem_ast->child[0];
+ key_ast = elem_ast->child[1];
+ by_ref = elem_ast->attr;
znode value_node, key_node, *key_node_ptr = NULL;
diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y
index bedb7e62bc..5f233d6a3b 100644
--- a/Zend/zend_language_parser.y
+++ b/Zend/zend_language_parser.y
@@ -241,7 +241,6 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%type <ast> exit_expr scalar backticks_expr lexical_var function_call member_name property_name
%type <ast> variable_class_name dereferencable_scalar constant dereferencable
%type <ast> callable_expr callable_variable static_member new_variable
-%type <ast> unkeyed_assignment_list_element keyed_assignment_list_element array_pair
%type <ast> encaps_var encaps_var_offset isset_variables
%type <ast> top_statement_list use_declarations const_list inner_statement_list if_stmt
%type <ast> alt_if_stmt for_exprs switch_case_list global_var_list static_var_list
@@ -250,8 +249,8 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%type <ast> non_empty_parameter_list argument_list non_empty_argument_list property_list
%type <ast> class_const_list class_const_decl name_list trait_adaptations method_body non_empty_for_exprs
%type <ast> ctor_arguments alt_if_stmt_without_else trait_adaptation_list lexical_vars
-%type <ast> lexical_var_list encaps_list array_pair_list non_empty_array_pair_list
-%type <ast> assignment_list unkeyed_assignment_list keyed_assignment_list
+%type <ast> lexical_var_list encaps_list
+%type <ast> array_pair non_empty_array_pair_list array_pair_list
%type <ast> isset_variable type return_type
%type <ast> identifier
@@ -545,7 +544,8 @@ implements_list:
foreach_variable:
variable { $$ = $1; }
| '&' variable { $$ = zend_ast_create(ZEND_AST_REF, $2); }
- | T_LIST '(' assignment_list ')' { $$ = $3; }
+ | T_LIST '(' array_pair_list ')' { $3->attr = 1; $$ = $3; }
+ | '[' array_pair_list ']' { $$ = $2; }
;
for_statement:
@@ -860,8 +860,10 @@ new_expr:
;
expr_without_variable:
- T_LIST '(' assignment_list ')' '=' expr
- { $$ = zend_ast_create(ZEND_AST_ASSIGN, $3, $6); }
+ T_LIST '(' array_pair_list ')' '=' expr
+ { $3->attr = 1; $$ = zend_ast_create(ZEND_AST_ASSIGN, $3, $6); }
+ | '[' array_pair_list ']' '=' expr
+ { $$ = zend_ast_create(ZEND_AST_ASSIGN, $2, $5); }
| variable '=' expr
{ $$ = zend_ast_create(ZEND_AST_ASSIGN, $1, $3); }
| variable '=' '&' variable
@@ -1086,11 +1088,6 @@ constant:
{ $$ = zend_ast_create(ZEND_AST_CLASS_CONST, $1, $3); }
;
-possible_comma:
- /* empty */
- | ','
-;
-
expr:
variable { $$ = $1; }
| expr_without_variable { $$ = $1; }
@@ -1180,44 +1177,9 @@ property_name:
| simple_variable { $$ = zend_ast_create(ZEND_AST_VAR, $1); }
;
-assignment_list:
- unkeyed_assignment_list
- { $$ = $1; }
- | keyed_assignment_list possible_comma
- { $$ = $1; }
-;
-
-unkeyed_assignment_list:
- unkeyed_assignment_list ',' unkeyed_assignment_list_element
- { $$ = zend_ast_list_add($1, $3); }
- | unkeyed_assignment_list_element
- { $$ = zend_ast_create_list(1, ZEND_AST_LIST, $1); }
-;
-
-unkeyed_assignment_list_element:
- variable { $$ = $1; }
- | T_LIST '(' assignment_list ')' { $$ = $3; }
- | /* empty */ { $$ = NULL; }
-;
-
-keyed_assignment_list:
- keyed_assignment_list ',' keyed_assignment_list_element
- { $$ = zend_ast_list_add($1, $3); }
- | keyed_assignment_list_element
- { $$ = zend_ast_create_list(1, ZEND_AST_LIST, $1); }
-;
-
-keyed_assignment_list_element:
- expr T_DOUBLE_ARROW variable
- { $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $3, $1); }
- | expr T_DOUBLE_ARROW T_LIST '(' assignment_list ')'
- { $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $5, $1); }
-;
-
-
array_pair_list:
/* empty */ { $$ = zend_ast_create_list(0, ZEND_AST_ARRAY); }
- | non_empty_array_pair_list possible_comma { $$ = $1; }
+ | non_empty_array_pair_list { /* allow single trailing comma */ zend_ast_list *list = zend_ast_get_list($$ = $1); if (list->child[list->children - 1] == NULL) { list->children--; } }
;
non_empty_array_pair_list:
@@ -1225,16 +1187,27 @@ non_empty_array_pair_list:
{ $$ = zend_ast_list_add($1, $3); }
| array_pair
{ $$ = zend_ast_create_list(1, ZEND_AST_ARRAY, $1); }
+ | non_empty_array_pair_list ',' /* empty, for LHS array lists */
+ { $$ = zend_ast_list_add($1, NULL); }
+ | ','
+ { $$ = zend_ast_create_list(1, ZEND_AST_ARRAY, NULL); }
+ | ',' array_pair
+ { $$ = zend_ast_create_list(2, ZEND_AST_ARRAY, NULL, $2); }
;
array_pair:
expr T_DOUBLE_ARROW expr
{ $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $3, $1); }
- | expr { $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $1, NULL); }
+ | expr
+ { $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $1, NULL); }
| expr T_DOUBLE_ARROW '&' variable
{ $$ = zend_ast_create_ex(ZEND_AST_ARRAY_ELEM, 1, $4, $1); }
| '&' variable
{ $$ = zend_ast_create_ex(ZEND_AST_ARRAY_ELEM, 1, $2, NULL); }
+ | expr T_DOUBLE_ARROW T_LIST '(' array_pair_list ')'
+ { $5->attr = 1; $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $5, $1); }
+ | T_LIST '(' array_pair_list ')'
+ { $3->attr = 1; $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $3, NULL); }
;
encaps_list: