Skip to content

Implemented Default Value in list() syntax #1623

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions Zend/tests/list_default_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
--TEST--
Testing list() default value: basic
--FILE--
<?php

// empty
list($a=1) = array();
var_dump($a);

list($a=1, $b=2) = array();
var_dump($a, $b);

list($a, $b=2) = array();
var_dump($a, $b);

echo "---------\n";

list($a=1) = array(2);
var_dump($a);

list($a=1, $b=2) = array(1 => 3);
var_dump($a, $b);

list($a, $b=2, $c) = array();
var_dump($a, $b, $c);

?>
--EXPECTF--
int(1)
int(1)
int(2)

Notice: Undefined offset: 0 in %s on line %s
NULL
int(2)
---------
int(2)
int(1)
int(3)

Notice: Undefined offset: %d in %s on line %s

Notice: Undefined offset: %d in %s on line %s
NULL
int(2)
NULL
15 changes: 15 additions & 0 deletions Zend/tests/list_default_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Testing list() default value: nested list
--FILE--
<?php

list($a, list($b=1, $c), $d=99) = [1, [1 => new stdclass]];
var_dump($a, $b, $c, $d);

?>
--EXPECT--
int(1)
int(1)
object(stdClass)#1 (0) {
}
int(99)
30 changes: 30 additions & 0 deletions Zend/tests/list_default_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
--TEST--
Testing list() default value: non array
--FILE--
<?php

list($a, $b=2) = 1;
var_dump($a, $b);

list($a, $b=2) = null;
var_dump($a, $b);

list($a, $b=2) = "rule the world!";
var_dump($a, $b);


list($a=10, list($b, $c=20), $d) = null;
var_dump($a, $b, $c, $d);

?>
--EXPECT--
NULL
int(2)
NULL
int(2)
NULL
int(2)
int(10)
NULL
int(20)
NULL
17 changes: 17 additions & 0 deletions Zend/tests/list_default_004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
Testing list() nested with empty array and default value
--FILE--
<?php

list($a, list($b, list(list($c=100, $d)))) = array();
var_dump($a, $b, $c, $d);

?>
--EXPECTF--
Notice: Undefined offset: 0 in %s on line %d

Notice: Undefined offset: 1 in %s on line %d
NULL
NULL
int(100)
NULL
47 changes: 47 additions & 0 deletions Zend/tests/list_default_005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
--TEST--
Testing list() assign with non-variable with default value
--FILE--
<?php

list($a[1], $b[3]=99) = [1];
var_dump($a, $b);


$obj = new stdclass;

list($a, $obj->prop1, $obj->prop2='default') = [];
var_dump($a, $obj);

$obj2 = new stdclass;

list($a, list($obj2->prop1, $obj2->prop2='default2')) = [1, [1]];
var_dump($a, $obj2);

?>
--EXPECTF--
array(1) {
[1]=>
int(1)
}
array(1) {
[3]=>
int(99)
}

Notice: Undefined offset: 0 in %s on line %d

Notice: Undefined offset: 1 in %s on line %d
NULL
object(stdClass)#1 (2) {
["prop1"]=>
NULL
["prop2"]=>
string(7) "default"
}
int(1)
object(stdClass)#2 (2) {
["prop1"]=>
int(1)
["prop2"]=>
string(8) "default2"
}
55 changes: 55 additions & 0 deletions Zend/tests/list_default_006.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
--TEST--
Testing list() assign with default different expressions
--FILE--
<?php

list($a, $b='str') = [1];
var_dump($a, $b);


list($a, $b=new stdclass) = [1];
var_dump($a, $b);

list($a, $b=new stdclass) = [1, 2];
var_dump($a, $b);

list($a, $b=function () {return true;}) = [1];
var_dump($a, $b);

list($a, $b=function () {return true;}) = [1, 2];
var_dump($a, $b);

echo "----------\n";

function test_func($str)
{
var_dump($str);
return "Yes Sir!";
}

list($a, $b=test_func("call me!")) = [1];
var_dump($a, $b);

list($a, $b=test_func("call me!")) = [1, 2];
var_dump($a, $b);

?>
--EXPECTF--
int(1)
string(3) "str"
int(1)
object(stdClass)#1 (0) {
}
int(1)
int(2)
int(1)
object(Closure)#1 (0) {
}
int(1)
int(2)
----------
string(8) "call me!"
int(1)
string(8) "Yes Sir!"
int(1)
int(2)
60 changes: 60 additions & 0 deletions Zend/tests/list_default_007.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
--TEST--
Testing list() default value in foreach context
--FILE--
<?php


$arr = [
[],
[1],
[1, 2],
[1, 2, 3]
];

foreach($arr as $key => list($one, $two ='default')) {
echo "$key => $one, $two\n";
}

echo "-----------\n";

foreach($arr as $key => list($one='ONE')) {
echo "$key => $one\n";
}

echo "-----------\n";

foreach($arr as $key => list($one=strlen('ONE'))) {
echo "$key => $one\n";
}

function say_sth($str) {
echo "Beep\n";
return $str;
}

foreach($arr as $key => list($one=say_sth('ONE'))) {
echo "$key => $one\n";
}

?>
--EXPECTF--
Notice: Undefined offset: %d in %s on line %d
0 => , default
1 => 1, default
2 => 1, 2
3 => 1, 2
-----------
0 => ONE
1 => 1
2 => 1
3 => 1
-----------
0 => 3
1 => 1
2 => 1
3 => 1
Beep
0 => ONE
1 => 1
2 => 1
3 => 1
8 changes: 8 additions & 0 deletions Zend/zend_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -1074,6 +1074,14 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int
zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent);
smart_str_appendc(str, ')');
break;
case ZEND_AST_LIST_ELEM:
zend_ast_export_ex(str, ast->child[0], priority, indent);

if (ast->child[1]) {
smart_str_appendc(str, '=');
zend_ast_export_ex(str, ast->child[1], priority, indent);
}
break;
case ZEND_AST_ARRAY:
smart_str_appendc(str, '[');
zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent);
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ enum _zend_ast_kind {
ZEND_AST_INSTANCEOF,
ZEND_AST_YIELD,
ZEND_AST_COALESCE,
ZEND_AST_LIST_ELEM,

ZEND_AST_STATIC,
ZEND_AST_WHILE,
Expand Down
41 changes: 34 additions & 7 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -2461,23 +2461,46 @@ static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_n
zend_bool has_elems = 0;

for (i = 0; i < list->children; ++i) {
zend_ast *var_ast = list->child[i];
znode fetch_result, dim_node;
zend_ast *list_elm_ast = list->child[i];
zend_ast *var_ast, *default_ast;

znode fetch_result, dim_node, default_node;
zend_op *opline;

if (var_ast == NULL) {
if (list_elm_ast == NULL) {
continue;
}

has_elems = 1;

var_ast = list_elm_ast->child[0];
default_ast = list_elm_ast->child[1];

dim_node.op_type = IS_CONST;
ZVAL_LONG(&dim_node.u.constant, i);

if (expr_node->op_type == IS_CONST) {
Z_TRY_ADDREF(expr_node->u.constant);
}

zend_emit_op(&fetch_result, ZEND_FETCH_LIST, expr_node, &dim_node);
zend_emit_assign_znode(var_ast, &fetch_result);
opline = zend_emit_op(&fetch_result, ZEND_FETCH_LIST, expr_node, &dim_node);

if (default_ast) {
uint32_t opnum_skip_jmp;

zend_emit_assign_znode(var_ast, &fetch_result);

opnum_skip_jmp = zend_emit_jump(0);

opline->extended_value = get_next_op_number(CG(active_op_array));

zend_compile_expr(&default_node, default_ast);
zend_emit_assign_znode(var_ast, &default_node);

zend_update_jump_target_to_next(opnum_skip_jmp);
} else {
zend_emit_assign_znode(var_ast, &fetch_result);
}
}

if (!has_elems) {
Expand Down Expand Up @@ -2531,11 +2554,15 @@ 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 *elm_ast = list->child[i];
zend_ast *var_ast;

if (!elm_ast) {
continue;
}

var_ast = elm_ast->child[0];

/* Recursively check nested list()s */
if (var_ast->kind == ZEND_AST_LIST && zend_list_has_assign_to(var_ast, name)) {
return 1;
Expand Down
5 changes: 3 additions & 2 deletions Zend/zend_language_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -1177,8 +1177,9 @@ assignment_list:
;

assignment_list_element:
variable { $$ = $1; }
| T_LIST '(' assignment_list ')' { $$ = $3; }
variable { $$ = zend_ast_create_ex(ZEND_AST_LIST_ELEM, 0, $1, NULL); }
| variable '=' expr { $$ = zend_ast_create_ex(ZEND_AST_LIST_ELEM, 0, $1, $3); }
| T_LIST '(' assignment_list ')' { $$ = zend_ast_create_ex(ZEND_AST_LIST_ELEM, 0, $3, NULL); }
| /* empty */ { $$ = NULL; }
;

Expand Down
5 changes: 5 additions & 0 deletions Zend/zend_opcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,11 @@ ZEND_API int pass_two(zend_op_array *op_array)
opline->opcode = ZEND_GENERATOR_RETURN;
}
break;
case ZEND_FETCH_LIST:
if (opline->extended_value != 0) {
opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
}
break;
}
if (opline->op1_type == IS_CONST) {
ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1);
Expand Down
Loading