From: Nikita Popov Date: Tue, 24 Feb 2015 17:57:14 +0000 (+0100) Subject: Support list($a, $b) = $a X-Git-Tag: PRE_PHP7_EREG_MYSQL_REMOVALS~74 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=bdf1430eeba10d444274642572b9e1ac84013080;p=php Support list($a, $b) = $a By compiling the RHS $a as a non-CV fetch. This worked as expected in PHP 5, but broke in PHP 7 due to the different assign order. However the new implementation is more general, in that it allows $a on the LHS in any place, not just the first element. --- diff --git a/Zend/tests/list_self_assign.phpt b/Zend/tests/list_self_assign.phpt new file mode 100644 index 0000000000..4640912696 --- /dev/null +++ b/Zend/tests/list_self_assign.phpt @@ -0,0 +1,56 @@ +--TEST-- +Test variable occuring on both LHS and RHS of list() +--FILE-- + +--EXPECT-- +int(1) +int(2) +int(3) +int(1) +int(2) +int(3) +int(1) +int(2) +int(3) +int(1) +int(2) +int(3) +int(1) +int(2) +int(3) +int(1) +int(2) +int(3) +int(1) +int(2) +int(3) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 9db4f284af..e7177c7cd0 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2316,6 +2316,50 @@ zend_bool zend_is_assign_to_self(zend_ast *var_ast, zend_ast *expr_ast) /* {{{ * } /* }}} */ +/* Detects if list($a, $b, $c) contains variable with given name */ +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) { + continue; + } + + /* Recursively check nested list()s */ + if (var_ast->kind == ZEND_AST_LIST && zend_list_has_assign_to(var_ast, name)) { + return 1; + } + + if (var_ast->kind == ZEND_AST_VAR && var_ast->child[0]->kind == ZEND_AST_ZVAL) { + zend_string *var_name = zval_get_string(zend_ast_get_zval(var_ast->child[0])); + zend_bool result = zend_string_equals(var_name, name); + zend_string_release(var_name); + if (result) { + return 1; + } + } + } + + return 0; +} +/* }}} */ + +/* Detects patterns like list($a, $b, $c) = $a */ +zend_bool zend_list_has_assign_to_self(zend_ast *list_ast, zend_ast *expr_ast) /* {{{ */ +{ + /* Only check simple variables on the RHS, as only CVs cause issues with this. */ + if (expr_ast->kind == ZEND_AST_VAR && expr_ast->child[0]->kind == ZEND_AST_ZVAL) { + zend_string *name = zval_get_string(zend_ast_get_zval(expr_ast->child[0])); + zend_bool result = zend_list_has_assign_to(list_ast, name); + zend_string_release(name); + return result; + } + return 0; +} +/* }}} */ + void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */ { zend_ast *var_ast = ast->child[0]; @@ -2365,7 +2409,13 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */ zend_emit_op_data(&expr_node); return; case ZEND_AST_LIST: - zend_compile_expr(&expr_node, expr_ast); + 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); + } else { + zend_compile_expr(&expr_node, expr_ast); + } + zend_compile_list_assign(result, var_ast, &expr_node); return; EMPTY_SWITCH_DEFAULT_CASE();