Implemented RFC: Fix inconsistent behavior of $this variable

Squashed commit of the following:

commit bdd3b6895c
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Thu Jun 16 00:19:42 2016 +0300

    Fixed GOTO VM

commit 2f1d7c8b89
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Wed Jun 15 21:01:57 2016 +0300

    Removed unused variable

commit cf749c42b0
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Wed Jun 15 19:06:16 2016 +0300

    Protection from $this reassign through mb_parse_str()

commit 59a9a6c83c
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Wed Jun 15 18:14:50 2016 +0300

    Added type inference rule for FETCH_THIS opcode

commit 73f8d14a85
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 fa0881381e
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 e32cc528c0
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 41f1531b52
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Mon May 23 22:18:36 2016 +0300

    Fixed inconsistent $this behavior
This commit is contained in:
Dmitry Stogov 2016-06-16 02:30:23 +03:00
parent fba6f90ae3
commit a9512af810
38 changed files with 1511 additions and 3576 deletions

View File

@ -31,40 +31,4 @@ $test->bar();
?>
--EXPECTF--
object(Exception)#%d (7) {
["message":protected]=>
string(3) "foo"
["string":"Exception":private]=>
string(0) ""
["code":protected]=>
int(0)
["file":protected]=>
string(%d) "%s030.php"
["line":protected]=>
int(%d)
["trace":"Exception":private]=>
array(1) {
[0]=>
array(6) {
["file"]=>
string(%d) "%s030.php"
["line"]=>
int(%d)
["function"]=>
string(3) "bar"
["class"]=>
string(3) "foo"
["type"]=>
string(2) "->"
["args"]=>
array(0) {
}
}
}
["previous":"Exception":private]=>
NULL
}
'test' => '0'
'test_2' => '1'
'test_3' => '2'
ok
Fatal error: Cannot re-assign $this in %s030.php on line 11

View File

@ -13,6 +13,4 @@ $x = $c->test();
print_r($x);
unset($c, $x);
--EXPECTF--
Array
(
)
Fatal error: Cannot unset $this in %sbug68370.php on line 4

View File

@ -5,7 +5,7 @@ Calling method from array
class Hello {
public function world($x) {
echo "Hello, $x\n"; return $this;
echo "Hello, $x\n";return $this;
}
}
@ -37,8 +37,16 @@ class Magic3 {
}
$f = array('Hello','world');
var_dump($f('you'));
var_dump(call_user_func($f, 'you'));
try {
var_dump($f('you'));
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}
try {
var_dump(call_user_func($f, 'you'));
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}
printf("-----\n");
@ -101,35 +109,31 @@ var_dump(call_user_func($f, 'you'));
--EXPECTF--
Deprecated: Non-static method Hello::world() should not be called statically in %s on line %d
Hello, you
Notice: Undefined variable: this in %s on line %d
NULL
Exception: Using $this when not in object context
Deprecated: %son-static method Hello::world() should not be called statically in %s on line %d
Hello, you
Notice: Undefined variable: this in %s on line %d
NULL
Exception: Using $this when not in object context
-----
Hello, again
object(Hello)#1 (0) {
object(Hello)#%d (0) {
}
Hello, again
object(Hello)#1 (0) {
object(Hello)#%d (0) {
}
-----
Hello, there
object(Hello)#2 (0) {
object(Hello)#%d (0) {
}
Hello, there
object(Hello)#2 (0) {
object(Hello)#%d (0) {
}
-----
Hello, devs
object(Hello)#4 (0) {
object(Hello)#%d (0) {
}
Hello, devs
object(Hello)#4 (0) {
object(Hello)#%d (0) {
}
-----
Magic::__call called (foo)!

View File

@ -10,11 +10,12 @@ class B {
function bar() { A::foo(); }
}
$b = new B;
$b->bar();
try {
$b->bar();
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}
?>
--EXPECTF--
Deprecated: Non-static method A::foo() should not be called statically in %s on line %d
Notice: Undefined variable: this in %s on line %d
string(1) "A"
Exception: Using $this when not in object context

View File

@ -17,8 +17,11 @@ class foo {
}
$arr = array('foo', 'abc');
$arr();
try {
$arr();
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}
$foo = new foo;
$arr = array($foo, 'abc');
$arr();
@ -28,9 +31,7 @@ $arr();
--EXPECTF--
From foo::__callStatic:
string(3) "abc"
Notice: Undefined variable: this in %s on line %d
NULL
Exception: Using $this when not in object context
From foo::__call:
string(3) "abc"
object(foo)#%d (0) {

View File

@ -0,0 +1,12 @@
--TEST--
$this as global variable
--FILE--
<?php
function foo() {
global $this;
var_dump($this);
}
foo();
?>
--EXPECTF--
Fatal error: Cannot use $this as global variable in %sthis_as_global.php on line 3

View File

@ -0,0 +1,11 @@
--TEST--
$this as parameter
--FILE--
<?php
function foo($this) {
var_dump($this);
}
foo(5);
?>
--EXPECTF--
Fatal error: Cannot use $this as parameter in %sthis_as_parameter.php on line 2

View File

@ -0,0 +1,12 @@
--TEST--
$this as static variable
--FILE--
<?php
function foo() {
static $this;
var_dump($this);
}
foo();
?>
--EXPECTF--
Fatal error: Cannot use $this as static variable in %sthis_as_static.php on line 3

View File

@ -0,0 +1,18 @@
--TEST--
$this in catch
--FILE--
<?php
class C {
function foo() {
try {
throw new Exception();
} catch (Exception $this) {
}
var_dump($this);
}
}
$obj = new C;
$obj->foo();
?>
--EXPECTF--
Fatal error: Cannot re-assign $this in %sthis_in_catch.php on line 6

View File

@ -0,0 +1,17 @@
--TEST--
$this re-assign in extract()
--FILE--
<?php
function foo() {
extract(["this"=>42]);
var_dump($this);
}
foo();
?>
--EXPECTF--
Fatal error: Uncaught Error: Cannot re-assign $this in %sthis_in_extract.php:3
Stack trace:
#0 %sthis_in_extract.php(3): extract(Array)
#1 %sthis_in_extract.php(6): foo()
#2 {main}
thrown in %sthis_in_extract.php on line 3

View File

@ -0,0 +1,11 @@
--TEST--
$this in foreach
--FILE--
<?php
$a = [1];
foreach ($a as $this) {
var_dump($this);
}
?>
--EXPECTF--
Fatal error: Cannot re-assign $this in %sthis_in_foreach_001.php on line 3

View File

@ -0,0 +1,11 @@
--TEST--
$this in foreach
--FILE--
<?php
$a = [1];
foreach ($a as $this => $dummy) {
var_dump($this);
}
?>
--EXPECTF--
Fatal error: Cannot re-assign $this in %sthis_in_foreach_002.php on line 3

View File

@ -0,0 +1,11 @@
--TEST--
$this in foreach
--FILE--
<?php
$a = [1];
foreach ($a as &$this) {
var_dump($this);
}
?>
--EXPECTF--
Fatal error: Cannot re-assign $this in %sthis_in_foreach_003.php on line 3

View File

@ -0,0 +1,11 @@
--TEST--
$this in foreach
--FILE--
<?php
$a = [[1]];
foreach ($a as list($this)) {
var_dump($this);
}
?>
--EXPECTF--
Fatal error: Cannot re-assign $this in %sthis_in_foreach_004.php on line 3

View File

@ -0,0 +1,41 @@
--TEST--
$this in isset
--FILE--
<?php
var_dump(isset($this));
try {
var_dump(isset($this->foo));
} catch (Throwable $e) {
echo "exception\n";
}
try {
var_dump(isset($this->foo->bar));
} catch (Throwable $e) {
echo "exception\n";
}
try {
var_dump(isset($this[0]));
} catch (Throwable $e) {
echo "exception\n";
}
class A extends ArrayObject {
public $foo = 5;
function foo() {
$this[0] = 5;
var_dump(isset($this));
var_dump(isset($this->foo));
var_dump(isset($this[0]));
}
}
$a = new A();
$a->foo();
?>
--EXPECT--
bool(false)
exception
exception
exception
bool(true)
bool(true)
bool(true)

View File

@ -0,0 +1,19 @@
--TEST--
$this re-assign in mb_parse_str()
--SKIPIF--
<?php extension_loaded('mbstring') or die('skip mbstring not available'); ?>
--FILE--
<?php
function foo() {
mb_parse_str("this=42");
var_dump($this);
}
foo();
?>
--EXPECTF--
Fatal error: Uncaught Error: Cannot re-assign $this in %sthis_in_mb_parse_str.php:3
Stack trace:
#0 %sthis_in_mb_parse_str.php(3): mb_parse_str('this=42')
#1 %sthis_in_mb_parse_str.php(6): foo()
#2 {main}
thrown in %sthis_in_mb_parse_str.php on line 3

View File

@ -0,0 +1,17 @@
--TEST--
$this re-assign in parse_str()
--FILE--
<?php
function foo() {
parse_str("this=42");
var_dump($this);
}
foo();
?>
--EXPECTF--
Fatal error: Uncaught Error: Cannot re-assign $this in %sthis_in_parse_str.php:3
Stack trace:
#0 %sthis_in_parse_str.php(3): parse_str('this=42')
#1 %sthis_in_parse_str.php(6): foo()
#2 {main}
thrown in %sthis_in_parse_str.php on line 3

View File

@ -0,0 +1,8 @@
--TEST--
$this in unset
--FILE--
<?php
unset($this);
?>
--EXPECTF--
Fatal error: Cannot unset $this in %sthis_in_unset.php on line 2

View File

@ -0,0 +1,17 @@
--TEST--
$this re-assign
--FILE--
<?php
function foo() {
$a = "this";
$$a = 0;
var_dump($$a);
}
foo();
?>
--EXPECTF--
Fatal error: Uncaught Error: Cannot re-assign $this in %sthis_reassign.php:4
Stack trace:
#0 %sthis_reassign.php(7): foo()
#1 {main}
thrown in %sthis_reassign.php on line 4

View File

@ -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 {

View File

@ -364,8 +364,6 @@ struct _zend_op_array {
uint32_t *refcount;
uint32_t this_var;
uint32_t last;
zend_op *opcodes;

View File

@ -2133,7 +2133,7 @@ static zend_never_inline ZEND_COLD ZEND_NORETURN void ZEND_FASTCALL zend_interru
* +----------------------------------------+
*/
static zend_always_inline void i_init_func_execute_data(zend_execute_data *execute_data, zend_op_array *op_array, zval *return_value, int check_this) /* {{{ */
static zend_always_inline void i_init_func_execute_data(zend_execute_data *execute_data, zend_op_array *op_array, zval *return_value) /* {{{ */
{
uint32_t first_extra_arg, num_args;
ZEND_ASSERT(EX(func) == (zend_function*)op_array);
@ -2191,11 +2191,6 @@ static zend_always_inline void i_init_func_execute_data(zend_execute_data *execu
} while (var != end);
}
if (check_this && op_array->this_var != (uint32_t)-1 && EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
ZVAL_OBJ(EX_VAR(op_array->this_var), Z_OBJ(EX(This)));
GC_REFCOUNT(Z_OBJ(EX(This)))++;
}
EX_LOAD_RUN_TIME_CACHE(op_array);
EX_LOAD_LITERALS(op_array);
@ -2219,13 +2214,6 @@ static zend_always_inline void i_init_code_execute_data(zend_execute_data *execu
EX(call) = NULL;
EX(return_value) = return_value;
if (UNEXPECTED(op_array->this_var != (uint32_t)-1) && EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
GC_REFCOUNT(Z_OBJ(EX(This)))++;
if (!zend_hash_add(EX(symbol_table), CG(known_strings)[ZEND_STR_THIS], &EX(This))) {
GC_REFCOUNT(Z_OBJ(EX(This)))--;
}
}
zend_attach_symbol_table(execute_data);
if (!op_array->run_time_cache) {
@ -2248,13 +2236,6 @@ static zend_always_inline void i_init_execute_data(zend_execute_data *execute_da
EX(return_value) = return_value;
if (EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE) {
if (UNEXPECTED(op_array->this_var != (uint32_t)-1) && EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
GC_REFCOUNT(Z_OBJ(EX(This)))++;
if (!zend_hash_add(EX(symbol_table), CG(known_strings)[ZEND_STR_THIS], &EX(This))) {
GC_REFCOUNT(Z_OBJ(EX(This)))--;
}
}
zend_attach_symbol_table(execute_data);
} else {
uint32_t first_extra_arg, num_args;
@ -2307,11 +2288,6 @@ static zend_always_inline void i_init_execute_data(zend_execute_data *execute_da
var++;
} while (var != end);
}
if (op_array->this_var != (uint32_t)-1 && EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
ZVAL_OBJ(EX_VAR(op_array->this_var), Z_OBJ(EX(This)));
GC_REFCOUNT(Z_OBJ(EX(This)))++;
}
}
if (!op_array->run_time_cache) {

View File

@ -1141,7 +1141,6 @@ ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend
if (is_static) {
func->fn_flags |= ZEND_ACC_STATIC;
}
func->this_var = -1;
func->opcodes = &EG(call_trampoline_op);
func->prototype = fbc;

View File

@ -85,8 +85,6 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz
op_array->static_variables = NULL;
op_array->last_try_catch = 0;
op_array->this_var = -1;
op_array->fn_flags = 0;
op_array->early_binding = -1;

View File

@ -762,7 +762,7 @@ ZEND_VM_HELPER(zend_binary_assign_op_obj_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV,
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
ZEND_VM_HELPER(zend_binary_assign_op_dim_helper, VAR|UNUSED|CV, CONST|TMPVAR|UNUSED|CV, binary_op_type binary_op)
ZEND_VM_HELPER(zend_binary_assign_op_dim_helper, VAR|CV, CONST|TMPVAR|UNUSED|CV, binary_op_type binary_op)
{
USE_OPLINE
zend_free_op free_op1, free_op2, free_op_data1;
@ -771,14 +771,8 @@ ZEND_VM_HELPER(zend_binary_assign_op_dim_helper, VAR|UNUSED|CV, CONST|TMPVAR|UNU
SAVE_OPLINE();
container = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var);
FREE_UNFETCHED_OP2();
HANDLE_EXCEPTION();
}
if (OP1_TYPE != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
ZEND_VM_C_LABEL(assign_dim_op_array):
SEPARATE_ARRAY(container);
ZEND_VM_C_LABEL(assign_dim_op_new_array):
@ -811,27 +805,25 @@ ZEND_VM_C_LABEL(assign_dim_op_new_array):
ZVAL_COPY(EX_VAR(opline->result.var), var_ptr);
}
} else {
if (OP1_TYPE != IS_UNUSED) {
if (EXPECTED(Z_ISREF_P(container))) {
container = Z_REFVAL_P(container);
if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
ZEND_VM_C_GOTO(assign_dim_op_array);
}
} else if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) {
container = GET_OP1_UNDEF_CV(container, BP_VAR_RW);
ZEND_VM_C_LABEL(assign_dim_op_convert_to_array):
ZVAL_NEW_ARR(container);
zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0);
ZEND_VM_C_GOTO(assign_dim_op_new_array);
if (EXPECTED(Z_ISREF_P(container))) {
container = Z_REFVAL_P(container);
if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
ZEND_VM_C_GOTO(assign_dim_op_array);
}
} else if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) {
container = GET_OP1_UNDEF_CV(container, BP_VAR_RW);
ZEND_VM_C_LABEL(assign_dim_op_convert_to_array):
ZVAL_NEW_ARR(container);
zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0);
ZEND_VM_C_GOTO(assign_dim_op_new_array);
}
dim = GET_OP2_ZVAL_PTR(BP_VAR_R);
if (OP1_TYPE == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
value = get_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1);
zend_binary_assign_op_obj_dim(container, dim, value, UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, binary_op);
} else if (OP1_TYPE != IS_UNUSED) {
} else {
if (UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) {
if (OP2_TYPE == IS_UNUSED) {
zend_throw_error(NULL, "[] operator not supported for strings");
@ -900,12 +892,11 @@ ZEND_VM_HANDLER(23, ZEND_ASSIGN_ADD, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEX
if (EXPECTED(opline->extended_value == 0)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, add_function);
}
# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, add_function);
} else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, add_function);
}
# endif
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, add_function);
#else
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, add_function);
#endif
@ -920,12 +911,11 @@ ZEND_VM_HANDLER(24, ZEND_ASSIGN_SUB, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEX
if (EXPECTED(opline->extended_value == 0)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, sub_function);
}
# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, sub_function);
} else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, sub_function);
}
# endif
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, sub_function);
#else
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, sub_function);
#endif
@ -940,12 +930,11 @@ ZEND_VM_HANDLER(25, ZEND_ASSIGN_MUL, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEX
if (EXPECTED(opline->extended_value == 0)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, mul_function);
}
# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, mul_function);
} else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, mul_function);
}
# endif
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, mul_function);
#else
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, mul_function);
#endif
@ -960,12 +949,11 @@ ZEND_VM_HANDLER(26, ZEND_ASSIGN_DIV, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEX
if (EXPECTED(opline->extended_value == 0)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, div_function);
}
# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, div_function);
} else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, div_function);
}
# endif
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, div_function);
#else
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, div_function);
#endif
@ -980,12 +968,11 @@ ZEND_VM_HANDLER(27, ZEND_ASSIGN_MOD, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEX
if (EXPECTED(opline->extended_value == 0)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, mod_function);
}
# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, mod_function);
} else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, mod_function);
}
# endif
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, mod_function);
#else
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, mod_function);
#endif
@ -1000,12 +987,11 @@ ZEND_VM_HANDLER(28, ZEND_ASSIGN_SL, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEXT
if (EXPECTED(opline->extended_value == 0)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, shift_left_function);
}
# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, shift_left_function);
} else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, shift_left_function);
}
# endif
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, shift_left_function);
#else
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, shift_left_function);
#endif
@ -1020,12 +1006,11 @@ ZEND_VM_HANDLER(29, ZEND_ASSIGN_SR, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NEXT
if (EXPECTED(opline->extended_value == 0)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, shift_right_function);
}
# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, shift_right_function);
} else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, shift_right_function);
}
# endif
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, shift_right_function);
#else
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, shift_right_function);
#endif
@ -1040,12 +1025,11 @@ ZEND_VM_HANDLER(30, ZEND_ASSIGN_CONCAT, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|
if (EXPECTED(opline->extended_value == 0)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, concat_function);
}
# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, concat_function);
} else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, concat_function);
}
# endif
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, concat_function);
#else
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, concat_function);
#endif
@ -1060,12 +1044,11 @@ ZEND_VM_HANDLER(31, ZEND_ASSIGN_BW_OR, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|N
if (EXPECTED(opline->extended_value == 0)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, bitwise_or_function);
}
# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, bitwise_or_function);
} else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, bitwise_or_function);
}
# endif
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, bitwise_or_function);
#else
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, bitwise_or_function);
#endif
@ -1080,12 +1063,11 @@ ZEND_VM_HANDLER(32, ZEND_ASSIGN_BW_AND, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|
if (EXPECTED(opline->extended_value == 0)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, bitwise_and_function);
}
# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, bitwise_and_function);
} else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, bitwise_and_function);
}
# endif
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, bitwise_and_function);
#else
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, bitwise_and_function);
#endif
@ -1100,12 +1082,11 @@ ZEND_VM_HANDLER(33, ZEND_ASSIGN_BW_XOR, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|
if (EXPECTED(opline->extended_value == 0)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, bitwise_xor_function);
}
# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, bitwise_xor_function);
} else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, bitwise_xor_function);
}
# endif
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, bitwise_xor_function);
#else
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, bitwise_xor_function);
#endif
@ -1120,12 +1101,11 @@ ZEND_VM_HANDLER(167, ZEND_ASSIGN_POW, VAR|UNUSED|THIS|CV, CONST|TMPVAR|UNUSED|NE
if (EXPECTED(opline->extended_value == 0)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_helper, binary_op, pow_function);
}
# endif
if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, pow_function);
} else /* if (EXPECTED(opline->extended_value == ZEND_ASSIGN_OBJ)) */ {
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, pow_function);
}
# endif
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, pow_function);
#else
ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, pow_function);
#endif
@ -1487,6 +1467,43 @@ ZEND_VM_HELPER(zend_fetch_var_address_helper, CONST|TMPVAR|CV, UNUSED, int type)
target_symbol_table = zend_get_target_symbol_table(execute_data, opline->extended_value & ZEND_FETCH_TYPE_MASK);
retval = zend_hash_find(target_symbol_table, name);
if (retval == NULL) {
if (UNEXPECTED(zend_string_equals(name, CG(known_strings)[ZEND_STR_THIS]))) {
zval *result;
ZEND_VM_C_LABEL(fetch_this):
result = EX_VAR(opline->result.var);
switch (type) {
case BP_VAR_R:
if (EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
ZVAL_OBJ(result, Z_OBJ(EX(This)));
Z_ADDREF_P(result);
} else {
ZVAL_NULL(result);
zend_error(E_NOTICE,"Undefined variable: this");
}
break;
case BP_VAR_IS:
if (EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
ZVAL_OBJ(result, Z_OBJ(EX(This)));
Z_ADDREF_P(result);
} else {
ZVAL_NULL(result);
}
break;
case BP_VAR_RW:
case BP_VAR_W:
zend_throw_error(NULL, "Cannot re-assign $this");
break;
case BP_VAR_UNSET:
zend_throw_error(NULL, "Cannot unset $this");
break;
EMPTY_SWITCH_DEFAULT_CASE()
}
if (OP1_TYPE != IS_CONST) {
zend_string_release(name);
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
switch (type) {
case BP_VAR_R:
case BP_VAR_UNSET:
@ -1508,6 +1525,9 @@ ZEND_VM_HELPER(zend_fetch_var_address_helper, CONST|TMPVAR|CV, UNUSED, int type)
} else if (Z_TYPE_P(retval) == IS_INDIRECT) {
retval = Z_INDIRECT_P(retval);
if (Z_TYPE_P(retval) == IS_UNDEF) {
if (UNEXPECTED(zend_string_equals(name, CG(known_strings)[ZEND_STR_THIS]))) {
ZEND_VM_C_GOTO(fetch_this);
}
switch (type) {
case BP_VAR_R:
case BP_VAR_UNSET:
@ -3596,7 +3616,7 @@ ZEND_VM_HANDLER(130, ZEND_DO_UCALL, ANY, ANY, SPEC(RETVAL))
}
call->prev_execute_data = execute_data;
i_init_func_execute_data(call, &fbc->op_array, ret, 0);
i_init_func_execute_data(call, &fbc->op_array, ret);
ZEND_VM_ENTER();
}
@ -3619,7 +3639,7 @@ ZEND_VM_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL))
}
call->prev_execute_data = execute_data;
i_init_func_execute_data(call, &fbc->op_array, ret, 0);
i_init_func_execute_data(call, &fbc->op_array, ret);
ZEND_VM_ENTER();
} else {
@ -3716,7 +3736,7 @@ ZEND_VM_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL))
}
call->prev_execute_data = execute_data;
i_init_func_execute_data(call, &fbc->op_array, ret, 1);
i_init_func_execute_data(call, &fbc->op_array, ret);
if (EXPECTED(zend_execute_ex == execute_ex)) {
ZEND_VM_ENTER();
@ -5547,7 +5567,7 @@ ZEND_VM_HANDLER(179, ZEND_UNSET_STATIC_PROP, CONST|TMPVAR|CV, UNUSED|CLASS_FETCH
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
ZEND_VM_HANDLER(75, ZEND_UNSET_DIM, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV)
ZEND_VM_HANDLER(75, ZEND_UNSET_DIM, VAR|CV, CONST|TMPVAR|CV)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
@ -5558,15 +5578,10 @@ ZEND_VM_HANDLER(75, ZEND_UNSET_DIM, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV)
SAVE_OPLINE();
container = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_UNSET);
if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP2();
HANDLE_EXCEPTION();
}
offset = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
do {
if (OP1_TYPE != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
ZEND_VM_C_LABEL(unset_dim_array):
@ -5616,7 +5631,7 @@ ZEND_VM_C_LABEL(num_index_dim):
zend_error(E_WARNING, "Illegal offset type in unset");
}
break;
} else if (OP1_TYPE != IS_UNUSED && Z_ISREF_P(container)) {
} else if (Z_ISREF_P(container)) {
container = Z_REFVAL_P(container);
if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
ZEND_VM_C_GOTO(unset_dim_array);
@ -5628,7 +5643,7 @@ ZEND_VM_C_LABEL(num_index_dim):
if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) {
offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R);
}
if (OP1_TYPE == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) {
zend_throw_error(NULL, "Cannot use object as array");
} else {
@ -6479,7 +6494,7 @@ ZEND_VM_C_LABEL(is_static_prop_return):
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
ZEND_VM_HANDLER(115, ZEND_ISSET_ISEMPTY_DIM_OBJ, CONST|TMPVAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, ISSET)
ZEND_VM_HANDLER(115, ZEND_ISSET_ISEMPTY_DIM_OBJ, CONST|TMPVAR|CV, CONST|TMPVAR|CV, ISSET)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
@ -6490,16 +6505,9 @@ ZEND_VM_HANDLER(115, ZEND_ISSET_ISEMPTY_DIM_OBJ, CONST|TMPVAR|UNUSED|THIS|CV, CO
SAVE_OPLINE();
container = GET_OP1_OBJ_ZVAL_PTR_UNDEF(BP_VAR_IS);
if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
zend_throw_error(NULL, "Using $this when not in object context");
FREE_UNFETCHED_OP2();
HANDLE_EXCEPTION();
}
offset = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
if (OP1_TYPE != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) {
HashTable *ht;
zval *value;
zend_string *str;
@ -6566,8 +6574,7 @@ ZEND_VM_C_LABEL(num_index_prop):
offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R);
}
if (OP1_TYPE == IS_UNUSED ||
(OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) {
if ((OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) {
if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) {
result =
((opline->extended_value & ZEND_ISSET) == 0) ^
@ -7910,8 +7917,7 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY)
if (UNEXPECTED(!fbc->op_array.run_time_cache)) {
init_func_run_time_cache(&fbc->op_array);
}
i_init_func_execute_data(call, &fbc->op_array,
ret, (fbc->common.fn_flags & ZEND_ACC_STATIC) == 0);
i_init_func_execute_data(call, &fbc->op_array, ret);
if (EXPECTED(zend_execute_ex == execute_ex)) {
ZEND_VM_ENTER();
} else {
@ -8071,6 +8077,34 @@ ZEND_VM_HANDLER(183, ZEND_BIND_STATIC, CV, CONST, REF)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
ZEND_VM_HANDLER(184, ZEND_FETCH_THIS, UNUSED, UNUSED)
{
USE_OPLINE
if (EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
zval *result = EX_VAR(opline->result.var);
ZVAL_OBJ(result, Z_OBJ(EX(This)));
Z_ADDREF_P(result);
ZEND_VM_NEXT_OPCODE();
} else {
SAVE_OPLINE();
zend_throw_error(NULL, "Using $this when not in object context");
HANDLE_EXCEPTION();
}
}
ZEND_VM_HANDLER(186, ZEND_ISSET_ISEMPTY_THIS, UNUSED, UNUSED)
{
USE_OPLINE
ZVAL_BOOL(EX_VAR(opline->result.var),
(opline->extended_value & ZEND_ISSET) ?
(Z_TYPE(EX(This)) == IS_OBJECT) :
(Z_TYPE(EX(This)) != IS_OBJECT));
ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_TYPE_SPEC_HANDLER(ZEND_ADD, (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG), ZEND_ADD_LONG_NO_OVERFLOW, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(NO_CONST_CONST,COMMUTATIVE))
{
USE_OPLINE
@ -8550,10 +8584,10 @@ ZEND_VM_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, (op1_info & (MAY_BE_UNDEF|MAY_BE_REF
if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) {
if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
ZEND_VM_C_GOTO(send_var_by_ref);
ZEND_VM_C_GOTO(send_var_by_ref_simple);
}
} else if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
ZEND_VM_C_LABEL(send_var_by_ref):
ZEND_VM_C_LABEL(send_var_by_ref_simple):
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SEND_REF);
}

File diff suppressed because it is too large Load Diff

View File

@ -949,7 +949,7 @@ function gen_handler($f, $spec, $kind, $name, $op1, $op2, $use, $code, $lineno,
if ($spec &&
isset($extra_spec["DIM_OBJ"]) &&
(($op2 == "UNUSED" && $extra_spec["DIM_OBJ"] != 1) ||
($op1 == "UNUSED" && $extra_spec["DIM_OBJ"] == 0))) {
($op1 == "UNUSED" && $extra_spec["DIM_OBJ"] != 2))) {
// Skip useless handlers
return;
}
@ -1184,7 +1184,7 @@ function gen_labels($f, $spec, $kind, $prolog, &$specs, $switch_labels = array()
return;
} else if (isset($extra_spec["DIM_OBJ"]) &&
(($op2 == "UNUSED" && $extra_spec["DIM_OBJ"] != 1) ||
($op1 == "UNUSED" && $extra_spec["DIM_OBJ"] == 0))) {
($op1 == "UNUSED" && $extra_spec["DIM_OBJ"] != 2))) {
// Skip useless handlers
gen_null_label($f, $kind, $prolog);
$label++;

View File

@ -21,7 +21,7 @@
#include <stdio.h>
#include <zend.h>
static const char *zend_vm_opcodes_names[184] = {
static const char *zend_vm_opcodes_names[187] = {
"ZEND_NOP",
"ZEND_ADD",
"ZEND_SUB",
@ -206,9 +206,12 @@ static const char *zend_vm_opcodes_names[184] = {
"ZEND_FETCH_CLASS_CONSTANT",
"ZEND_BIND_LEXICAL",
"ZEND_BIND_STATIC",
"ZEND_FETCH_THIS",
NULL,
"ZEND_ISSET_ISEMPTY_THIS",
};
static uint32_t zend_vm_opcodes_flags[184] = {
static uint32_t zend_vm_opcodes_flags[187] = {
0x00000000,
0x00000707,
0x00000707,
@ -284,7 +287,7 @@ static uint32_t zend_vm_opcodes_flags[184] = {
0x00106703,
0x08000007,
0x00030107,
0x00000751,
0x00000701,
0x00000751,
0x00002003,
0x03000001,
@ -324,7 +327,7 @@ static uint32_t zend_vm_opcodes_flags[184] = {
0x01000757,
0x01008773,
0x00030107,
0x00020757,
0x00020707,
0x00001003,
0x00001001,
0x01000703,
@ -393,6 +396,9 @@ static uint32_t zend_vm_opcodes_flags[184] = {
0x00000373,
0x00100101,
0x00100301,
0x00000101,
0x00000000,
0x00000101,
};
ZEND_API const char* zend_get_opcode_name(zend_uchar opcode) {

View File

@ -248,7 +248,9 @@ END_EXTERN_C()
#define ZEND_FETCH_CLASS_CONSTANT 181
#define ZEND_BIND_LEXICAL 182
#define ZEND_BIND_STATIC 183
#define ZEND_FETCH_THIS 184
#define ZEND_ISSET_ISEMPTY_THIS 186
#define ZEND_VM_LAST_OPCODE 183
#define ZEND_VM_LAST_OPCODE 186
#endif

View File

@ -72,9 +72,9 @@ typedef struct _literal_info {
info[n].u.num = (_num); \
} while (0)
#define LITERAL_INFO_OBJ(n, kind, merge, slots, related, _num) do { \
#define LITERAL_INFO_OBJ(n, kind, merge, slots, related) do { \
info[n].flags = (LITERAL_EX_OBJ | ((merge) ? LITERAL_MAY_MERGE : 0) | LITERAL_FLAGS(kind, slots, related)); \
info[n].u.num = (_num); \
info[n].u.num = (uint32_t)-1; \
} while (0)
static void optimizer_literal_obj_info(literal_info *info,
@ -92,7 +92,7 @@ static void optimizer_literal_obj_info(literal_info *info,
*/
if (Z_TYPE(op_array->literals[constant]) == IS_STRING &&
op_type == IS_UNUSED) {
LITERAL_INFO_OBJ(constant, kind, 1, slots, related, op_array->this_var);
LITERAL_INFO_OBJ(constant, kind, 1, slots, related);
} else {
LITERAL_INFO(constant, kind, 0, slots, related);
}
@ -421,9 +421,11 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
case IS_CONSTANT:
if (info[i].flags & LITERAL_MAY_MERGE) {
if (info[i].flags & LITERAL_EX_OBJ) {
int key_len = MAX_LENGTH_OF_LONG + sizeof("->") - 1 + Z_STRLEN(op_array->literals[i]);
int key_len = sizeof("$this->") - 1 + Z_STRLEN(op_array->literals[i]);
key = zend_string_alloc(key_len, 0);
ZSTR_LEN(key) = snprintf(ZSTR_VAL(key), ZSTR_LEN(key)-1, "%d->%s", info[i].u.num, Z_STRVAL(op_array->literals[i]));
memcpy(ZSTR_VAL(key), "$this->", sizeof("$this->") - 1);
memcpy(ZSTR_VAL(key) + sizeof("$this->") - 1, Z_STRVAL(op_array->literals[i]), Z_STRLEN(op_array->literals[i]) + 1);
ZSTR_LEN(key) = key_len;
} else if (info[i].flags & LITERAL_EX_CLASS) {
int key_len;
zval *class_name = &op_array->literals[(info[i].u.num < i) ? map[info[i].u.num] : info[i].u.num];

View File

@ -603,7 +603,6 @@ int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int
return (tmp->min <= tmp->max);
} else if (ssa->vars[var].definition < 0) {
if (var < op_array->last_var &&
var != EX_VAR_TO_NUM(op_array->this_var) &&
op_array->function_name) {
tmp->min = 0;
@ -3246,6 +3245,10 @@ static void zend_update_type_info(const zend_op_array *op_array,
}
UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def);
break;
case ZEND_FETCH_THIS:
UPDATE_SSA_OBJ_TYPE(op_array->scope, 1, ssa_ops[i].result_def);
UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN|MAY_BE_OBJECT, ssa_ops[i].result_def);
break;
case ZEND_FETCH_OBJ_R:
case ZEND_FETCH_OBJ_IS:
case ZEND_FETCH_OBJ_RW:
@ -4009,13 +4012,7 @@ int zend_ssa_inference(zend_arena **arena, const zend_op_array *op_array, const
}
} else {
for (i = 0; i < op_array->last_var; i++) {
if (i == EX_VAR_TO_NUM(op_array->this_var)) {
ssa_var_info[i].type = MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_OBJECT;
ssa_var_info[i].ce = op_array->scope;
ssa_var_info[i].is_instanceof = 1;
} else {
ssa_var_info[i].type = MAY_BE_UNDEF | MAY_BE_RCN;
}
ssa_var_info[i].type = MAY_BE_UNDEF | MAY_BE_RCN;
ssa_var_info[i].has_range = 0;
}
}

View File

@ -14,7 +14,11 @@ class TestClass {
public static function staticMethod() {
echo "Called staticMethod()\n";
var_dump($this);
try {
var_dump($this);
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}
}
private static function privateMethod() {
@ -103,9 +107,7 @@ NULL
Warning: ReflectionMethod::invokeArgs() expects parameter 1 to be object, boolean given in %s on line %d
NULL
Called staticMethod()
Notice: Undefined variable: this in %s on line %d
NULL
Exception: Using $this when not in object context
NULL
Private method:

View File

@ -22,7 +22,11 @@ class TestClass {
public static function staticMethod() {
echo "Called staticMethod()\n";
var_dump($this);
try {
var_dump($this);
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}
}
private static function privateMethod() {
@ -94,14 +98,10 @@ Static method:
Warning: ReflectionMethod::invoke() expects at least 1 parameter, 0 given in %s on line %d
NULL
Called staticMethod()
Notice: Undefined variable: this in %s on line %d
NULL
Exception: Using $this when not in object context
NULL
Called staticMethod()
Notice: Undefined variable: this in %s on line %d
NULL
Exception: Using $this when not in object context
NULL
Method that throws an exception:

View File

@ -1764,6 +1764,7 @@ PHP_FUNCTION(extract)
zend_ulong num_key;
int var_exists, count = 0;
int extract_refs = 0;
int exception_thrown = 0;
zend_array *symbol_table;
zval var_array;
@ -1846,12 +1847,6 @@ PHP_FUNCTION(extract)
if (var_exists && ZSTR_LEN(var_name) == sizeof("GLOBALS")-1 && !strcmp(ZSTR_VAL(var_name), "GLOBALS")) {
break;
}
if (var_exists && ZSTR_LEN(var_name) == sizeof("this")-1 && !strcmp(ZSTR_VAL(var_name), "this")) {
zend_class_entry *scope = zend_get_executed_scope();
if (scope && ZSTR_LEN(scope->name) != 0) {
break;
}
}
ZVAL_STR_COPY(&final_name, var_name);
break;
@ -1892,6 +1887,15 @@ PHP_FUNCTION(extract)
if (Z_TYPE(final_name) == IS_STRING && php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
zval *orig_var;
if (Z_STRLEN(final_name) == sizeof("this")-1 && !strcmp(Z_STRVAL(final_name), "this")) {
if (!exception_thrown) {
exception_thrown = 1;
zend_throw_error(NULL, "Cannot re-assign $this");
}
zval_dtor(&final_name);
continue;
}
if (extract_refs) {
ZVAL_MAKE_REF(entry);

View File

@ -4614,6 +4614,9 @@ PHP_FUNCTION(parse_str)
symbol_table = zend_rebuild_symbol_table();
ZVAL_ARR(&tmp, symbol_table);
sapi_module.treat_data(PARSE_STRING, res, &tmp);
if (UNEXPECTED(zend_hash_del(symbol_table, CG(known_strings)[ZEND_STR_THIS]) == SUCCESS)) {
zend_throw_error(NULL, "Cannot re-assign $this");
}
} else {
zval ret;

View File

@ -109,6 +109,25 @@ PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars
return;
}
if (var_len == sizeof("this")-1 && EG(current_execute_data)) {
zend_execute_data *ex = EG(current_execute_data);
while (ex) {
if (ex->func && ZEND_USER_CODE(ex->func->common.type)) {
if (ex->symbol_table == symtable1) {
if (memcmp(var, "this", sizeof("this")-1) == 0) {
zend_throw_error(NULL, "Cannot re-assign $this");
zval_dtor(val);
free_alloca(var_orig, use_heap);
return;
}
}
break;
}
ex = ex->prev_execute_data;
}
}
/* GLOBALS hijack attempt, reject parameter */
if (symtable1 == &EG(symbol_table) &&
var_len == sizeof("GLOBALS")-1 &&

View File

@ -54,21 +54,25 @@ try {
--EXPECTF--
Warning: The magic method __call() must have public visibility and cannot be static in %s on line 3
---> Invoke __call via simple method call.
NULL
object(A)#1 (0) {
}
Exception caught OK; continuing.
---> Invoke __call via scope resolution operator within instance.
NULL
object(A)#1 (0) {
}
Exception caught OK; continuing.
---> Invoke __call via scope resolution operator within child instance.
NULL
object(B)#2 (0) {
}
Exception caught OK; continuing.
---> Invoke __call via callback.
NULL
object(B)#2 (0) {
}
Exception caught OK; continuing.
==DONE==

View File

@ -28,12 +28,4 @@ TestClass::Test2(new stdClass);
?>
===DONE===
--EXPECTF--
Notice: Undefined variable: this in %sstatic_this.php on line %d
NULL
Notice: Undefined variable: this in %sstatic_this.php on line %d
NULL
object(stdClass)#%d (0) {
}
===DONE===
Fatal error: Cannot use $this as parameter in %sstatic_this.php on line 16