跨实例的PHP全局函数中的静态变量是什么?

时间:2011-05-16 10:15:18

标签: php caching multiple-instances static-variables

如果我的代码使用static变量进行缓存,请执行以下操作:

class BossParty
{
    // ...

    public function getTemplate()
    {
        static $template;

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

        return $template;
    }

    // ...
}

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

3 个答案:

答案 0 :(得分: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值。

答案 1 :(得分: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),然后随每个单独的实例递增。

如果将类A扩展为B,并实例化其中一个(使其他所有内容保持不变),则会出现相同的行为:

<?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"
?>

仍然是相同的输出。

,将A扩展为B,并保留相同的结构:

<?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中的错误,还是预期的行为。 The PHP docs on static variable scope说:

  

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

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

答案 2 :(得分:1)

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