Skip to content

Commit 07b57ff

Browse files
committed
Restore CV
1 parent 78f2cfb commit 07b57ff

File tree

11 files changed

+818
-601
lines changed

11 files changed

+818
-601
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
using() restores CV
3+
--FILE--
4+
<?php
5+
6+
require 'basic_manager.inc';
7+
8+
class A {}
9+
10+
$value = new A;
11+
$value2 = $value;
12+
13+
echo "# \$value before using():\n";
14+
var_dump($value);
15+
16+
using (new Manager() as $value) {
17+
echo "# \$value in using():\n";
18+
var_dump($value);
19+
}
20+
21+
echo "# \$value after using():\n";
22+
var_dump($value);
23+
var_dump($value === $value2);
24+
25+
?>
26+
--EXPECTF--
27+
# $value before using():
28+
object(A)#%d (0) {
29+
}
30+
Manager::enterContext()
31+
# $value in using():
32+
object(stdClass)#%d (0) {
33+
}
34+
Manager::exitContext(null)
35+
# $value after using():
36+
object(A)#%d (0) {
37+
}
38+
bool(true)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
using() restores CV iff it was set
3+
--FILE--
4+
<?php
5+
6+
require 'basic_manager.inc';
7+
8+
using (new Manager() as $value) {
9+
var_dump($value);
10+
}
11+
12+
echo "\$value is set after using():\n";
13+
var_dump(isset($value));
14+
var_dump(array_key_exists('value', get_defined_vars()));
15+
16+
?>
17+
--EXPECTF--
18+
Manager::enterContext()
19+
object(stdClass)#%d (0) {
20+
}
21+
Manager::exitContext(null)
22+
$value is set after using():
23+
bool(false)
24+
bool(false)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--TEST--
2+
using() restores CV and preserves references
3+
--FILE--
4+
<?php
5+
6+
require 'basic_manager.inc';
7+
8+
class A {}
9+
class B {}
10+
11+
$value = new A;
12+
$value2 = &$value;
13+
14+
echo "# \$value before using():\n";
15+
var_dump($value);
16+
17+
using (new Manager() as $value) {
18+
echo "# \$value in using():\n";
19+
var_dump($value);
20+
$value = null;
21+
}
22+
23+
echo "# \$value after using():\n";
24+
var_dump($value);
25+
$value = new B;
26+
27+
echo "# \$value2 references \$value:\n";
28+
var_dump($value2 === $value);
29+
30+
?>
31+
--EXPECTF--
32+
# $value before using():
33+
object(A)#%d (0) {
34+
}
35+
Manager::enterContext()
36+
# $value in using():
37+
object(stdClass)#%d (0) {
38+
}
39+
Manager::exitContext(null)
40+
# $value after using():
41+
object(A)#%d (0) {
42+
}
43+
# $value2 references $value:
44+
bool(true)

Zend/zend_ast.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ enum _zend_ast_kind {
154154
ZEND_AST_NAMED_ARG,
155155
ZEND_AST_PARENT_PROPERTY_HOOK_CALL,
156156
ZEND_AST_PIPE,
157+
ZEND_AST_RESTORE_CV,
157158

158159
/* 3 child nodes */
159160
ZEND_AST_METHOD_CALL = 3 << ZEND_AST_NUM_CHILDREN_SHIFT,

Zend/zend_compile.c

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5836,10 +5836,15 @@ static void zend_compile_using(zend_ast *ast)
58365836
zend_ast_create_znode(&tmp),
58375837
zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_ENTER_CONTEXT)),
58385838
zend_ast_create_list(0, ZEND_AST_ARG_LIST));
5839+
znode var_backup;
5840+
znode var_node;
58395841
if (var_ast) {
5840-
if (var_ast->kind != ZEND_AST_VAR) {
5841-
zend_throw_error(NULL, "TODO: support non-var on right side of 'as'");
5842-
}
5842+
/* backup $var */
5843+
zend_compile_var(&var_node, var_ast, BP_VAR_IS, false);
5844+
zend_emit_op(&var_backup, ZEND_BACKUP_CV, &var_node, NULL);
5845+
5846+
/* assign $var */
5847+
zend_compile_stmt(zend_ast_create(ZEND_AST_UNSET, var_ast));
58435848
zend_compile_assign(&tmp, zend_ast_create(ZEND_AST_ASSIGN, var_ast, enter_ast));
58445849
} else {
58455850
zend_compile_method_call(&tmp, enter_ast, BP_VAR_R);
@@ -5940,13 +5945,32 @@ static void zend_compile_using(zend_ast *ast)
59405945
/* unset($var) */
59415946
finally_stmt = zend_ast_list_add(finally_stmt,
59425947
zend_ast_create(ZEND_AST_UNSET, var_ast));
5948+
/* restore $var */
5949+
finally_stmt = zend_ast_list_add(finally_stmt,
5950+
zend_ast_create(ZEND_AST_RESTORE_CV, var_ast,
5951+
zend_ast_create_znode(&var_backup)));
59435952
}
59445953

59455954
zend_compile_try(zend_ast_create(ZEND_AST_TRY, stmts_ast, catch_list, finally_stmt));
59465955

59475956
zend_end_loop(get_next_op_number(), NULL);
59485957
}
59495958

5959+
static void zend_compile_restore_cv(zend_ast *ast)
5960+
{
5961+
zend_ast *var_ast = ast->child[0];
5962+
zend_ast *backup_ast = ast->child[1];
5963+
5964+
znode var_node;
5965+
znode backup_node;
5966+
5967+
zend_compile_expr(&var_node, var_ast);
5968+
zend_compile_expr(&backup_node, backup_ast);
5969+
5970+
zend_op *opline = zend_emit_op(NULL, ZEND_RESTORE_CV, &backup_node, NULL);
5971+
SET_NODE(opline->result, &var_node);
5972+
}
5973+
59505974
static void zend_compile_echo(zend_ast *ast) /* {{{ */
59515975
{
59525976
zend_op *opline;
@@ -11982,6 +12006,9 @@ static void zend_compile_stmt(zend_ast *ast) /* {{{ */
1198212006
case ZEND_AST_USING:
1198312007
zend_compile_using(ast);
1198412008
break;
12009+
case ZEND_AST_RESTORE_CV:
12010+
zend_compile_restore_cv(ast);
12011+
break;
1198512012
default:
1198612013
{
1198712014
znode result;

Zend/zend_language_parser.y

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -548,11 +548,10 @@ statement:
548548
;
549549

550550
optional_using_as_variable:
551-
%empty { $$ = NULL; }
552-
| T_AS variable { $$ = $2; }
551+
%empty { $$ = NULL; }
552+
| T_AS T_VARIABLE { $$ = zend_ast_create(ZEND_AST_VAR, $2); }
553553
;
554554

555-
556555
catch_list:
557556
%empty
558557
{ $$ = zend_ast_create_list(0, ZEND_AST_CATCH_LIST); }

Zend/zend_vm_def.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9983,6 +9983,26 @@ ZEND_VM_C_LABEL(type_error):
99839983
HANDLE_EXCEPTION();
99849984
}
99859985

9986+
ZEND_VM_HANDLER(212, ZEND_BACKUP_CV, CV, UNUSED)
9987+
{
9988+
USE_OPLINE;
9989+
9990+
zval *var = EX_VAR(opline->result.var);
9991+
zval *value = GET_OP1_ZVAL_PTR(BP_VAR_IS);
9992+
ZVAL_COPY(var, value);
9993+
ZEND_VM_NEXT_OPCODE();
9994+
}
9995+
9996+
ZEND_VM_HANDLER(213, ZEND_RESTORE_CV, VAR, UNUSED)
9997+
{
9998+
USE_OPLINE;
9999+
10000+
zval *var = EX_VAR(opline->result.var);
10001+
zval *value = GET_OP1_ZVAL_PTR(BP_VAR_IS);
10002+
ZVAL_COPY_VALUE(var, value);
10003+
ZEND_VM_NEXT_OPCODE();
10004+
}
10005+
998610006
ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_JMP, (OP_JMP_ADDR(op, op->op1) > op), ZEND_JMP_FORWARD, JMP_ADDR, ANY)
998710007
{
998810008
USE_OPLINE

0 commit comments

Comments
 (0)