diff --git a/Zend/tests/gh15938_001.phpt b/Zend/tests/gh15938_001.phpt new file mode 100644 index 0000000000000..287df51dc0bee --- /dev/null +++ b/Zend/tests/gh15938_001.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-15938 +--FILE-- +a = str_repeat('a', 10); +$obj->a .= new class { + function __toString() { + global $obj; + unset($obj->a); + return str_repeat('c', 10); + } +}; + +var_dump($obj->a); + +?> +--EXPECTF-- +Warning: Undefined property: C::$a in %s on line %d +string(10) "cccccccccc" diff --git a/Zend/tests/gh15938_002.phpt b/Zend/tests/gh15938_002.phpt new file mode 100644 index 0000000000000..3a98b9ae87ee1 --- /dev/null +++ b/Zend/tests/gh15938_002.phpt @@ -0,0 +1,44 @@ +--TEST-- +GH-15938 +--FILE-- +a = ''; +$obj->a .= new class { + function __toString() { + global $obj; + for ($i = 0; $i < 8; $i++) { + $obj->{$i} = 0; + } + return 'str'; + } +}; + +var_dump($obj); + +?> +--EXPECTF-- +object(C)#%d (9) { + ["a"]=> + string(3) "str" + ["0"]=> + int(0) + ["1"]=> + int(0) + ["2"]=> + int(0) + ["3"]=> + int(0) + ["4"]=> + int(0) + ["5"]=> + int(0) + ["6"]=> + int(0) + ["7"]=> + int(0) +} diff --git a/Zend/tests/gh15938_003.phpt b/Zend/tests/gh15938_003.phpt new file mode 100644 index 0000000000000..120697c1d85d6 --- /dev/null +++ b/Zend/tests/gh15938_003.phpt @@ -0,0 +1,25 @@ +--TEST-- +GH-15938 +--FILE-- +a = ''; +$obj->a .= new class { + function __toString() { + global $obj; + $obj = null; + return 'str'; + } +}; + +?> +--EXPECTF-- +Fatal error: Uncaught Error: Attempt to assign property "a" on null in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/tests/gh15938_004.phpt b/Zend/tests/gh15938_004.phpt new file mode 100644 index 0000000000000..591265e325f6b --- /dev/null +++ b/Zend/tests/gh15938_004.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-15938 +--FILE-- + +--EXPECT-- +array(9) { + [0]=> + string(3) "str" + [1]=> + int(0) + [2]=> + int(0) + [3]=> + int(0) + [4]=> + int(0) + [5]=> + int(0) + [6]=> + int(0) + [7]=> + int(0) + [8]=> + int(0) +} diff --git a/Zend/tests/gh15938_005.phpt b/Zend/tests/gh15938_005.phpt new file mode 100644 index 0000000000000..f21b2938ab937 --- /dev/null +++ b/Zend/tests/gh15938_005.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-15938 +--FILE-- + +--EXPECT-- +string(20) "aaaaaaaaaacccccccccc" diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 63787c902f647..8aeb541d65e8b 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3627,6 +3627,17 @@ static inline void zend_emit_assign_ref_znode(zend_ast *var_ast, znode *value_no } /* }}} */ +static void zend_cast_to_string_if_concat(uint32_t opcode, zend_ast *expr, znode *result) +{ + /* For .=, if the expression may contain objects, cast to a string before + * compiling the lhs W fetches. Otherwise, __toString() may cause the + * volatile zval storage to go away. */ + if (opcode == ZEND_CONCAT && expr->kind != ZEND_AST_ZVAL) { + zend_op *cast_opline = zend_emit_op_tmp(result, ZEND_CAST, result, NULL); + cast_opline->extended_value = IS_STRING; + } +} + static void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */ { zend_ast *var_ast = ast->child[0]; @@ -3669,6 +3680,7 @@ static void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */ offset = zend_delayed_compile_begin(); zend_delayed_compile_dim(result, var_ast, BP_VAR_RW, /* by_ref */ false); zend_compile_expr_with_potential_assign_to_self(&expr_node, expr_ast, var_ast); + zend_cast_to_string_if_concat(opcode, expr_ast, &expr_node); opline = zend_delayed_compile_end(offset); opline->opcode = ZEND_ASSIGN_DIM_OP; @@ -3683,6 +3695,7 @@ static void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */ offset = zend_delayed_compile_begin(); zend_delayed_compile_prop(result, var_ast, BP_VAR_RW); zend_compile_expr(&expr_node, expr_ast); + zend_cast_to_string_if_concat(opcode, expr_ast, &expr_node); opline = zend_delayed_compile_end(offset); cache_slot = opline->extended_value; diff --git a/ext/opcache/tests/jit/assign_obj_op_001.phpt b/ext/opcache/tests/jit/assign_obj_op_001.phpt index 5b2a6d793d987..adde07240abbc 100644 --- a/ext/opcache/tests/jit/assign_obj_op_001.phpt +++ b/ext/opcache/tests/jit/assign_obj_op_001.phpt @@ -13,8 +13,8 @@ $test = new Test; (function(){$this->y.=[];})->call($test); ?> --EXPECTF-- +Warning: Array to string conversion in %sassign_obj_op_001.php on line 6 + Deprecated: Creation of dynamic property Test::$y is deprecated in %sassign_obj_op_001.php on line 6 Warning: Undefined property: Test::$y in %sassign_obj_op_001.php on line 6 - -Warning: Array to string conversion in %sassign_obj_op_001.php on line 6