Dynamically reference to $this should not work, but it does
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?
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:
-
$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). - 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. Sothis_var
stays uninitialized. - In the last case you have a plain
$this
usage, as suchthis_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.