5

如果我有使用static变量进行缓存的代码,如下所示:

class BossParty
{
    // ...

    public function getTemplate()
    {
        static $template;

        if ($template == null)
        {
            $template = BossTemplate::get($this->templateID);
        }

        return $template;
    }

    // ...
}

$template在不同的实例中持续存在BossParty?我试过检查 php.net,但我能找到的只是关于静态类变量的信息。

4

3 回答 3

7

是的,静态变量将在类的实例中持续存在。

例子:

<?php

class Test {
    public function __construct() {
        static $foo;

        if ($foo) {
            echo 'found';
        } else {
            $foo = 'foobar';
        }
    }
}

$test1 = new Test();
$test2 = new Test();
// output "found"

请注意,后代类也是如此。如果我们有一个Child扩展的类Test,调用parent::__construct(无论是显式还是隐式)将使用相同的$foo.

于 2011-05-16T10:19:36.380 回答
2

@ lonesomeday,这似乎大多是正确的。但是,特别是与您关于继承的后续评论有关,函数内部静态变量范围的行为似乎更复杂。我的所有示例都使用 PHP 5.3.16。

摘要static:当用于在实例函数中限定变量时,关键字的行为似乎因继承和您在函数调用堆栈中的位置而异。

这里有一些例子。

首先,这类似于您的初始示例:在类的__construct()方法中实例化一个静态变量,创建该类的两个实例,然后查看该变量的行为方式。

<?php
// Example 1
class A {
  public function __construct() {
    static $i = 0;
    $i++;
    $this->i = $i;
  }
}
$o1 = new A();
$o2 = new A();
printf("\$o1->i: %d\n", $o1->i); // outputs "$o1->i: 1"
printf("\$o2->i: %d\n", $o2->i); // outputs "$o2->i: 2"
?>

到目前为止,没有任何意外。该变量被实例化一次(到 0),然后随着每个单独的实例递增。

如果您将类扩展AB,并实例化其中的一个(保持其他所有内容相同),您将获得相同的行为:

<?php
// Example 2
class A {
  public function __construct() {
    static $i = 0;
    $i++;
    $this->i = $i;
  }
}
class B extends A {}
$o1 = new A();
$o2 = new B();
printf("\$o1->i: %d\n", $o1->i); // outputs "$o1->i: 1"
printf("\$o2->i: %d\n", $o2->i); // outputs "$o2->i: 2"
?>

再一次,没有惊喜。让我们回到第一个例子,稍微调整一下。首先,我们将静态变量实例化/增量调用移至成员方法。请注意,我已经删除B了这个类:

<?php
// Example 3
class A {
  public function __construct() {
    $this->setI();
  }

  public function setI() {
    static $i = 0;
    $i++;
    $this->i = $i;
  }
}
$o1 = new A();
$o2 = new A();
printf("\$o1->i: %d\n", $o1->i); // outputs "$o1->i: 1"
printf("\$o2->i: %d\n", $o2->i); // outputs "$o2->i: 2"
?>

还是一样的输出。

但是,当您扩展AB并保留相同的结构时:

<?php
// Example 4
class A {
  public function __construct() {
    $this->setI();
  }

  public function setI() {
    static $i = 0;
    $i++;
    $this->i = $i;
  }
}
class B extends A {}
$o1 = new A();
$o2 = new B();
printf("\$o1->i: %d\n", $o1->i); // outputs "$o1->i: 1"
printf("\$o2->i: %d\n", $o2->i); // outputs "$o2->i: 1"
?>

注意输出:在所有其他情况下,都$o2->i变成了 2,除了这个。

因此,如果我们将静态实例化扩展A 移动到一个实例方法,我们现在似乎已经为$i变量引入了一个新的范围,而在所有其他情况下,实例都共享了该静态变量的范围。

更令人困惑的是,考虑这个例子;它与前一个相同,但在这种情况下,类B有自己的实现setI(),它简单地屈服于其父类的实现:

<?php
// Example 5
class A {
  public function __construct() {
    $this->setI();
  }
  public function setI() {
    static $i = 0;
    $i++;
    $this->i = $i;
  }
}
class B extends A {
  public function setI() {
    parent::setI();
  }
}
$o1 = new A();
$o2 = new B();
printf("\$o1->i: %d\n", $o1->i); // outputs "$o1->i: 1"
printf("\$o2->i: %d\n", $o2->i); // outputs "$o2->i: 2"
?>

如您所见,$o2->i现在又设置为 2,这几乎是我们在任何地方都得到的(示例 #4 除外)

在我看来,这似乎非常违反直觉。我希望不同的实例有自己的变量范围,或者所有实例(包括扩展类的实例)共享相同的范围。

我不知道这是否是 PHP 中的错误,或者它是否是预期的行为。静态变量范围的 PHP 文档说:

静态变量只存在于局部函数作用域中,但当程序执行离开此作用域时,它不会丢失其值。

他们没有详细说明如何在对象实例的上下文中定义“函数范围”,所以我不确定示例 #4 中的边缘情况是否是预期的。

于 2012-11-05T16:36:13.027 回答
1

是的,它也记录在文档中,但它位于Variables Scope而不是Static Keyword部分下,这可能是您没有找到它的原因。

于 2011-05-16T10:21:25.563 回答