Fix inconsistent $this behavior #1918

Closed
wants to merge 13 commits into
from

6 participants

@dstogov

No description provided.

@dstogov dstogov changed the title from Fixed inconsistent $this behavior to Fix inconsistent $this behavior May 23, 2016
@laruence laruence commented on the diff May 24, 2016
ext/standard/array.c
@@ -1889,6 +1884,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")) {
@laruence
php.net member

it's better to use zend_string_equals(Z_STR(final_name), CG(known_string)[THIS_STR[)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@dstogov dstogov Merge branch 'master' into this_var
* master:
  Fixed bug #72213 (Finally leaks on nested exceptions).
  Forbid dynamic calls to scope introspection functions
  Allow empty property names
  Ensure no entry predecessors for SSA construction
  Replace BB end with BB len
  Fixed white-spaces
d03a9a1
@nikic

From a quick scan, it looks like handling for by-reference fetches of $this is missing. A by-ref fetch should not throw the undefined variable notice if $this does not exist. (And for function arguments by-ref may be dynamic, so do we also need a FUNC_ARG variant of this?)

@dstogov

@nikic it's possible to pass or assign $this by reference, but this assignment is actually done by value and changes to reference won't affect value of $this.

@nikic

@dstogov Yeah, I did understand that. What I'm referring to is that reference assignment usually suppresses the "undefined variable" notice, but in this case it would still be there, right? Though probably this doesn't matter anyway.

@dstogov
@nikic

@dstogov Tangentially related, as it would solve the issue with references as well: Shouldn't any access to $this, apart from isset($this), throw the same error exception you get on $this->foo and similar? It seems inconsistent that $this->foo; and $that = $this; $that->foo; behave very differently if $this is not defined. In one case you get an error exception, in the other you get a notice and warning. As long as isset($this) does not throw, this still keeps BC with the "can use method statically and non-statically" pattern.

@laruence laruence added the RFC label May 28, 2016
dstogov added some commits May 31, 2016
@dstogov dstogov Merge branch 'master' into this_var
* master: (45 commits)
  Re-Fixed bug #72155 (use-after-free caused by get_zval_xmlrpc_type)
  Revert "fix #72155 (use-after-free caused by get_zval_xmlrpc_type)"
  Split ZEND_SEND_VAR_NO_REF into ZEND_SEND_VAR_NO_REF and ZEND_SEND_VAR_NO_REF_EX (similar to ZEND_SEND_VAL) and remove ZEND_ARG_* flags.
  Initialize only the necessary fields.
  fix condition
  update NEWS
  Fixed bug #72284 (phpdbg fatal errors with coverage)
  fix test title
  Add test for bug #72258
  update UPGRADING
  Expose missing flags from libzip at least >= 0.11.x
  update UPGRADING
  Expose missing flags from libzip at least >= 0.11.x
  fix #72155 (use-after-free caused by get_zval_xmlrpc_type)
  This is exported at implementation site, but no forward declaration can cause compile warnings
  Fix bug #71604
  Forbid "yield from" in force closed generators
  Added NEWS Entry
  Test for bug #72221, segfault in zend_memnstr_ex
  Fix bug #72221 (segfault, past-the-end access)
  ...
605a237
@dstogov dstogov Changed "Notice: Undefined variable: this" into "Exception: Using $th…
…is when not in object context".
fa08813
@dstogov

@nikic, good point. I followed your suggestion and changed RFC and implementation accordingly. Now we will get "Using $this when not in object context" exception everywhere.

@ml-
<?php
error_reporting(E_ALL);

class Foo {
    function __construct() {
        $this->bar($this);
        var_dump($this);
    }

    public function bar(&$that) {
        $that = "Bar";
    }
}

$q = new Foo();
master
string(3) "Bar"
dstogov/this_var
object(Foo)#1 (0) {
}

Works.
However, no Warning emitted or Exception thrown in this case.

@dstogov
@staabm
staabm commented May 31, 2016 edited

Should it also emit a warning/notice in this case?

@nikic nikic commented on the diff Jun 6, 2016
ext/opcache/Optimizer/zend_inference.c
@@ -3844,13 +3843,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;
@nikic
nikic added a note Jun 6, 2016

This should add inference for the new FETCH_THIS opcodes.

@dstogov
dstogov added a note Jun 15, 2016

done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@nikic nikic commented on the diff Jun 6, 2016
ext/standard/string.c
@@ -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");
+ }
@nikic
nikic added a note Jun 6, 2016

What about mb_parse_str?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@nikic nikic and 1 other commented on an outdated diff Jun 6, 2016
Zend/zend_vm_def.h
+ }
+}
+
+ZEND_VM_HANDLER(185, ZEND_FETCH_THIS_IS, UNUSED, UNUSED)
+{
+ USE_OPLINE
+ zval *result = EX_VAR(opline->result.var);
+
+ if (EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
+ ZVAL_OBJ(result, Z_OBJ(EX(This)));
+ Z_ADDREF_P(result);
+ } else {
+ ZVAL_NULL(result);
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
@nikic
nikic added a note Jun 6, 2016 edited

This opcode comes up only for isset($this[foo]), but not isset($this->foo), right?

Right now FETCH_OBJ_IS will throw an Error if $this does not exist. Maybe isset($this[foo]) should as well? In this case we may save this opcode.

@dstogov
dstogov added a note Jun 15, 2016

Thanks for idea. Done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Majkl578 Majkl578 commented on the diff Jun 8, 2016
Zend/tests/this_in_foreach_001.phpt
@@ -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
@Majkl578
Majkl578 added a note Jun 8, 2016

This message is a bit strange. Since there is no $this in current scope, re-assign in the message doesn't make sense.

@dstogov
dstogov added a note Jun 15, 2016

we used the same message for similar problems in 7.0 and below.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Majkl578
Majkl578 commented Jun 8, 2016 edited

This RFC targets 7.1 although introduces mutliple BC breaks. It seems like a violation of PHP's own release process:

x.y.z to x.y+1.z

  • Backward compatibility must be kept
dstogov added some commits Jun 15, 2016
@dstogov dstogov Merge branch 'master' into this_var
* master: (134 commits)
  These bugs are also in 7.1-alpha
  Fixed(attempt to) bug #72405 (mb_ereg_replace - mbc_to_code (oniguruma) - oob read access)
  Maybe fix bug #72011
  Fix #50845: exif_process_IFD_TAG: Use the right offset if reading from stream
  Improve the signature
  Unused var
  C89 compatibility
  Fix bug #72138 - Integer Overflow in Length of String-typed ZVAL
  Only allow single comma in tail
  Fixed bug #72399 (Use-After-Free in MBString (search_re))
  Implemented FR #72385 (Update SQLite bundle lib(3.13.0))
  Cleanup
  Add support for "instanceof" pi nodes
  Use union for pi constraints
  Cleanup
  Fixed bug #72395 (list() regression)
  Switch zend_print_zval_r to use smart_str
  fix test portability
  Fixed bug #72306 (Heap overflow through proc_open and $env parameter)
  update NEWS
  ...
b8579f8
@dstogov dstogov Restored PHP-7 behavior of isset($this->foo).
It throws exception if not in object context.
Removed useless opcode handlers.
73f8d14
@dstogov dstogov Added type inference rule for FETCH_THIS opcode 59a9a6c
@dstogov dstogov Protection from $this reassign through mb_parse_str() cf749c4
@nikic nikic commented on an outdated diff Jun 15, 2016
Zend/zend_vm_def.h
+ 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 *result = EX_VAR(opline->result.var);
+
+ ZVAL_BOOL(EX_VAR(opline->result.var),
@nikic
nikic added a note Jun 15, 2016

This should probably use result (or the result variable can be dropped instead).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
dstogov added some commits Jun 15, 2016
@dstogov dstogov Merge branch 'master' into this_var
* master:
  Updated to version 2016.5 (2016e)
  Updated to version 2016.5 (2016e)
  Updated to version 2016.5 (2016e)
ae7663c
@dstogov dstogov Removed unused variable 2f1d7c8
@dstogov dstogov Merge branch 'master' into this_var
* master:
  Fix type inference bugs
  Added specialized handlers for SEND_VAR/SEND_VAR_EX opcodes.
  Fixed mistakes in type inference rules.
  Fixed expected test outcome due to rule changes
09e676f
@dstogov dstogov Fixed GOTO VM bdd3b68
@dstogov

Merged into master.

@dstogov dstogov closed this Jun 16, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment