diff options
author | Dmitry Stogov <dmitry@zend.com> | 2016-06-16 02:30:23 +0300 |
---|---|---|
committer | Dmitry Stogov <dmitry@zend.com> | 2016-06-16 02:30:23 +0300 |
commit | a9512af8109e889eb2c6042c57797184930667cd (patch) | |
tree | 4f4f8bbcbddd3798d50d35c555a5a7620e77735f /Zend/zend_compile.c | |
parent | fba6f90ae3d9d15b2fab7a0ec06be5767e2ab148 (diff) | |
download | php-git-a9512af8109e889eb2c6042c57797184930667cd.tar.gz |
Implemented RFC: Fix inconsistent behavior of $this variable
Squashed commit of the following:
commit bdd3b6895c3ce3eacfcf7d4bf4feb8dfa61801fd
Author: Dmitry Stogov <dmitry@zend.com>
Date: Thu Jun 16 00:19:42 2016 +0300
Fixed GOTO VM
commit 2f1d7c8b89ce821086d357cf65f629f040a85c03
Author: Dmitry Stogov <dmitry@zend.com>
Date: Wed Jun 15 21:01:57 2016 +0300
Removed unused variable
commit cf749c42b0b1919f70b1e7d6dcbfff76899506af
Author: Dmitry Stogov <dmitry@zend.com>
Date: Wed Jun 15 19:06:16 2016 +0300
Protection from $this reassign through mb_parse_str()
commit 59a9a6c83c66b666971e57f1173b33a422166efd
Author: Dmitry Stogov <dmitry@zend.com>
Date: Wed Jun 15 18:14:50 2016 +0300
Added type inference rule for FETCH_THIS opcode
commit 73f8d14a856f14a461430b3c7534ab2ce870cbf6
Author: Dmitry Stogov <dmitry@zend.com>
Date: Wed Jun 15 18:11:18 2016 +0300
Restored PHP-7 behavior of isset($this->foo).
It throws exception if not in object context.
Removed useless opcode handlers.
commit fa0881381e8ae97e022ae5d1ec0851c952f33c82
Author: Dmitry Stogov <dmitry@zend.com>
Date: Tue May 31 12:25:47 2016 +0300
Changed "Notice: Undefined variable: this" into "Exception: Using $this when not in object context".
commit e32cc528c0f2c97963d8ec83eff0269f1f45af18
Author: Dmitry Stogov <dmitry@zend.com>
Date: Tue May 24 02:02:43 2016 +0300
Throw exception on attempt to re-assign $this through extract() and parse_str().
commit 41f1531b52113ec8a4c208aa6b9ef50f1386bb3f
Author: Dmitry Stogov <dmitry@zend.com>
Date: Mon May 23 22:18:36 2016 +0300
Fixed inconsistent $this behavior
Diffstat (limited to 'Zend/zend_compile.c')
-rw-r--r-- | Zend/zend_compile.c | 79 |
1 files changed, 47 insertions, 32 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 6928af10b8..02507f2366 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -756,6 +756,9 @@ void zend_do_free(znode *op1) /* {{{ */ additional FREE opcode and simplify the FETCH handlers their selves */ zend_emit_op(NULL, ZEND_FREE, op1, NULL); + } else if (opline->opcode == ZEND_FETCH_THIS) { + opline->opcode = ZEND_NOP; + opline->result_type = IS_UNUSED; } else { opline->result_type = IS_UNUSED; } @@ -1928,6 +1931,10 @@ static void zend_adjust_for_fetch_type(zend_op *opline, uint32_t type) /* {{{ */ { zend_uchar factor = (opline->opcode == ZEND_FETCH_STATIC_PROP_R) ? 1 : 3; + if (opline->opcode == ZEND_FETCH_THIS) { + return; + } + switch (type & BP_VAR_MASK) { case BP_VAR_R: return; @@ -2527,9 +2534,6 @@ static int zend_try_compile_cv(znode *result, zend_ast *ast) /* {{{ */ /* lookup_cv may be using another zend_string instance */ name = CG(active_op_array)->vars[EX_VAR_TO_NUM(result->u.op.var)]; - if (zend_string_equals_literal(name, "this")) { - CG(active_op_array)->this_var = result->u.op.var; - } return SUCCESS; } @@ -2560,22 +2564,31 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint opline->extended_value = ZEND_FETCH_GLOBAL; } else { opline->extended_value = ZEND_FETCH_LOCAL; - /* there is a chance someone is accessing $this */ - if (ast->kind != ZEND_AST_ZVAL - && CG(active_op_array)->scope && CG(active_op_array)->this_var == (uint32_t)-1 - ) { - zend_string *key = CG(known_strings)[ZEND_STR_THIS]; - CG(active_op_array)->this_var = lookup_cv(CG(active_op_array), key); - } } return opline; } /* }}} */ +static zend_bool is_this_fetch(zend_ast *ast) /* {{{ */ +{ + if (ast->kind == ZEND_AST_VAR && ast->child[0]->kind == ZEND_AST_ZVAL) { + zval *name = zend_ast_get_zval(ast->child[0]); + return Z_TYPE_P(name) == IS_STRING && zend_string_equals_literal(Z_STR_P(name), "this"); + } + + return 0; +} +/* }}} */ + static void zend_compile_simple_var(znode *result, zend_ast *ast, uint32_t type, int delayed) /* {{{ */ { - if (zend_try_compile_cv(result, ast) == FAILURE) { + zend_op *opline; + + if (is_this_fetch(ast)) { + opline = zend_emit_op(result, ZEND_FETCH_THIS, NULL, NULL); + zend_adjust_for_fetch_type(opline, type); + } else if (zend_try_compile_cv(result, ast) == FAILURE) { zend_op *opline = zend_compile_simple_var_no_cv(result, ast, type, delayed); zend_adjust_for_fetch_type(opline, type); } @@ -2656,17 +2669,6 @@ void zend_compile_dim(znode *result, zend_ast *ast, uint32_t type) /* {{{ */ } /* }}} */ -static zend_bool is_this_fetch(zend_ast *ast) /* {{{ */ -{ - if (ast->kind == ZEND_AST_VAR && ast->child[0]->kind == ZEND_AST_ZVAL) { - zval *name = zend_ast_get_zval(ast->child[0]); - return Z_TYPE_P(name) == IS_STRING && zend_string_equals_literal(Z_STR_P(name), "this"); - } - - return 0; -} -/* }}} */ - static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t type) /* {{{ */ { zend_ast *obj_ast = ast->child[0]; @@ -2965,7 +2967,8 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */ offset = zend_delayed_compile_begin(); zend_delayed_compile_dim(result, var_ast, BP_VAR_W); - if (zend_is_assign_to_self(var_ast, expr_ast)) { + if (zend_is_assign_to_self(var_ast, expr_ast) + && !is_this_fetch(expr_ast)) { /* $a[0] = $a should evaluate the right $a first */ zend_compile_simple_var_no_cv(&expr_node, expr_ast, BP_VAR_R, 0); } else { @@ -3899,7 +3902,9 @@ void zend_compile_global_var(zend_ast *ast) /* {{{ */ convert_to_string(&name_node.u.constant); } - if (zend_try_compile_cv(&result, var_ast) == SUCCESS) { + if (is_this_fetch(var_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as global variable"); + } else if (zend_try_compile_cv(&result, var_ast) == SUCCESS) { zend_op *opline = zend_emit_op(NULL, ZEND_BIND_GLOBAL, &result, &name_node); zend_alloc_cache_slot(opline->op2.constant); } else { @@ -3944,6 +3949,10 @@ static void zend_compile_static_var_common(zend_ast *var_ast, zval *value, zend_ } zend_hash_update(CG(active_op_array)->static_variables, Z_STR(var_node.u.constant), value); + if (zend_string_equals_literal(Z_STR(var_node.u.constant), "this")) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as static variable"); + } + opline = zend_emit_op(NULL, ZEND_BIND_STATIC, NULL, &var_node); opline->op1_type = IS_CV; opline->op1.var = lookup_cv(CG(active_op_array), zend_string_copy(Z_STR(var_node.u.constant))); @@ -3977,7 +3986,9 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */ switch (var_ast->kind) { case ZEND_AST_VAR: - if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) { + if (is_this_fetch(var_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot unset $this"); + } else if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) { opline = zend_emit_op(NULL, ZEND_UNSET_VAR, &var_node, NULL); opline->extended_value = ZEND_FETCH_LOCAL | ZEND_QUICK_SET; } else { @@ -4412,7 +4423,9 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ opnum_fetch = get_next_op_number(CG(active_op_array)); opline = zend_emit_op(NULL, by_ref ? ZEND_FE_FETCH_RW : ZEND_FE_FETCH_R, &reset_node, NULL); - if (value_ast->kind == ZEND_AST_VAR && + if (is_this_fetch(value_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); + } else if (value_ast->kind == ZEND_AST_VAR && zend_try_compile_cv(&value_node, value_ast) == SUCCESS) { SET_NODE(opline->op2, &value_node); } else { @@ -4666,6 +4679,10 @@ void zend_compile_try(zend_ast *ast) /* {{{ */ opline->op1.constant = zend_add_class_name_literal(CG(active_op_array), zend_resolve_class_name_ast(class_ast)); + if (zend_string_equals_literal(Z_STR_P(var_name), "this")) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); + } + opline->op2_type = IS_CV; opline->op2.var = lookup_cv(CG(active_op_array), zend_string_copy(Z_STR_P(var_name))); @@ -5009,11 +5026,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ zend_error_noreturn(E_COMPILE_ERROR, "Redefinition of parameter $%s", ZSTR_VAL(name)); } else if (zend_string_equals_literal(name, "this")) { - if ((op_array->scope || (op_array->fn_flags & ZEND_ACC_CLOSURE)) - && (op_array->fn_flags & ZEND_ACC_STATIC) == 0) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as parameter"); - } - op_array->this_var = var_node.u.op.var; + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as parameter"); } if (op_array->fn_flags & ZEND_ACC_VARIADIC) { @@ -6996,7 +7009,9 @@ void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */ switch (var_ast->kind) { case ZEND_AST_VAR: - if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) { + if (is_this_fetch(var_ast)) { + opline = zend_emit_op(result, ZEND_ISSET_ISEMPTY_THIS, NULL, NULL); + } else if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) { opline = zend_emit_op(result, ZEND_ISSET_ISEMPTY_VAR, &var_node, NULL); opline->extended_value = ZEND_FETCH_LOCAL | ZEND_QUICK_SET; } else { |