Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP Reference to `$this`

Tags:

php

How does PHP interpret &$this ? Why is it allowed?

I came to this question with the following issues, which looks like a bug in PHP 7.1 and 7.2. It occurs with &$this references and cross-namespace calls and call_user_func_array. I think &$this is pretty weird and should not be allowed, but WordPress uses it for example.

Consider this code:

<?php
namespace N {
    function callNeedRef( $a ) {
        var_dump( $a );
        call_user_func_array( 'needRef', $a );
    }
}

namespace {
    function needRef( &$r ) { } 
    function callNeedRef( $a ) {
        var_dump( $a );
        call_user_func_array( 'needRef', $a );
    }   

    class C {
        function f() {
            $a = $this;
            callNeedRef( array( &$a ) );        // no warning (expected), OK!
            N\callNeedRef( array( &$a ) );      // no warning (expected), OK!
            callNeedRef( array( &$this ) ); // no warning (expected), but 7.1,7.2: var_dump prints no '&'           
            N\callNeedRef( array( &$this ) );   // 7.1,7.2: warn and var_dump prints no '&'         
        }
    }

    echo "<pre>";
    echo phpversion() . PHP_EOL;
    $o = new C();
    $o->f();
}

And its output:

7.2.0RC2
array(1) {
  [0]=>
  &object(C)#1 (0) {
  }
}
array(1) {
  [0]=>
  &object(C)#1 (0) {
  }
}
array(1) {
  [0]=>
  object(C)#1 (0) {
  }
}
array(1) {
  [0]=>
  object(C)#1 (0) {
  }
}


Warning:  Parameter 1 to needRef() expected to be a reference, value given in reftest.php on line 6

As already stated in the code, the two last var_dumps dont mark the object as reference. And the last call even produces as warning.

like image 641
Fabian Schlieper Avatar asked Nov 07 '22 16:11

Fabian Schlieper


1 Answers

The val ($this) is not global, the scope changes, therefore it get's ZCAL_STR_COPYied.

case EXTR_OVERWRITE:
        /* GLOBALS protection */
        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;

Thank's for making me look into PHP Source Code for array.c :)

like image 151
Unamata Sanatarai Avatar answered Nov 14 '22 21:11

Unamata Sanatarai