Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically reference to $this should not work, but it does

Tags:

php

As per PHP documentation about Variable variables:

$this is a special variable that cannot be referenced dynamically

However it seems that it's false, at least on the version of PHP, I've tested (5.5.12).

class ThisIsBugged
{
    public function __construct()
    {
        ${'this'}->doSomething(); // This works, while it shouldn't
    }
}

Question #1: How can it work? According to the documentation it should not.

But there's more.

class ThisIsBugged
{
    public function __construct()
    {
        // This does not work, but it could. See below.
        ${'th' . 'is'}->doSomething();
    }
}

It stops the execution as expected:

PHP Notice: Undefined variable: this

PHP Fatal error: Call to a member function doSomething() on a non-object.

Note that the statement {'th' . 'is'} has been evaluated: "Undefined variable: this".

However (this is the strangest thing), referencing explicitly the special variable $this, fixes all the dynamic references used before or after that within the method.

class ThisIsBugged
{
    public function __construct()
    {
        // Now it works while it shouldn't
        ${'th' . 'is'}->doSomething();

        // This fixes both the previous and the subsequent calls
        $unused = $this;

        // Now it works while it shouldn't
        ${'th' . 'is'}->doSomething();
    }
}

Question #2: How an explicit reference to $this can fix all the other dynamic references to $this present in the whole method?

like image 311
Demis Palma ツ Avatar asked Feb 09 '15 21:02

Demis Palma ツ


1 Answers

PHP uses a concept we call the compiled variables (CV) optimization. This means that instead of using a hashtable which maps variable names to their values, we use a plain array and index into it. The compiler knows which variable name corresponds to which index. Performing array index lookups is significantly faster than doing hashtable lookups.

The $this variable will also be stored this way and its index is specially remembered as op_array->this_var. If no $this use is found this value is left uninitialized at -1. When pushing a new execution context onto the VM stack PHP will check op_array->this_var and, if it isn't -1, initialize the $this variable entry.

When a variable variable is accessed, PHP will walk through the CV table and construct a proper symbol hashtable from it. Of course it will only add variables which actually are in the CV table, so if it doesn't contain $this you'll end up with an undefined variable lookup.

Now consider your three cases:

  1. $this and ${"this"} are the same as far as the PHP compiler is concerned (after all the variable name is known at compile time in both cases).
  2. As the PHP 5.x compiler does not yet perform constant expression folding, it will however not be able to detect that ${"th"."is"} is a $this access as well. So this_var stays uninitialized.
  3. In the last case you have a plain $this usage, as such this_var will be set and also accessible through a variable-variable lookup.

Note that the situation is different in PHP 7 - we'll always set this_var on a variable variable lookup, so indirect $this lookups should always work.

like image 75
NikiC Avatar answered Oct 23 '22 03:10

NikiC