动态引用$this不应该起作用,但它确实起作用


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

根据关于变量的PHP文档:

$这是一个特殊变量,不能动态引用

然而,这似乎是错误的,至少在PHP版本上,我已经测试过了(5.5.12)

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

问题#1:它是如何工作的?根据文件,它不应该。

但还有更多。

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

它按预期停止执行:

PHP注意:未定义的变量:这个

PHP致命错误:调用非对象。

请注意,语句{'th' . 'is'}已求值:"未定义的变量:this"

然而(这是最奇怪的事情),显式引用特殊变量$this,修复了方法中在此之前或之后使用的所有动态引用。

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();
    }
}

问题2:对$this的显式引用如何修复整个方法中存在的对$this的所有其他动态引用?

PHP使用了一个我们称之为编译变量(CV)优化的概念。这意味着,我们不使用将变量名映射到其值的哈希表,而是使用一个普通数组并对其进行索引。编译器知道哪个变量名对应于哪个索引。执行数组索引查找要比执行哈希表查找快得多。

$this变量也将以这种方式存储,并且其索引被特别记住为op_array->this_var。如果未发现$this使用,则该值在-1处未初始化。当将新的执行上下文推送到VM堆栈上时,PHP将检查op_array->this_var,如果不是-1,则初始化$this变量条目。

当一个变量变量被访问时,PHP会遍历CV表,并从中构建一个合适的符号哈希表。当然,它只会添加实际在CV表中的变量,所以如果它不包含$this,你最终会得到一个未定义的变量查找。

现在考虑您的三种情况:

  1. 就PHP编译器而言,$this${"this"}是相同的(毕竟在这两种情况下,在编译时变量名都是已知的)
  2. 由于PHP 5.x编译器还没有执行常量表达式折叠,因此它将无法检测到${"th"."is"}也是$this访问。因此this_var保持未初始化状态
  3. 在最后一种情况下,您有一个普通的$this用法,因此this_var将被设置,也可以通过变量变量查找进行访问

请注意,PHP7中的情况有所不同——我们总是在变量变量查找上设置this_var,因此间接$this查找应该始终有效。