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

发布于 2024-11-07 15:20:35 字数 439 浏览 1 评论 0原文

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

class BossParty
{
    // ...

    public function getTemplate()
    {
        static $template;

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

        return $template;
    }

    // ...
}

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

If I have code which uses a static variable for caching purposes like this:

class BossParty
{
    // ...

    public function getTemplate()
    {
        static $template;

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

        return $template;
    }

    // ...
}

Will $template persist across different instances of BossParty? I've tried checking php.net, but all I can find is info about static class variables.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

兮颜 2024-11-14 15:20:35

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

示例:

<?php

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

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

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

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

Yes, the static variable will persist across instances of the class.

Example:

<?php

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

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

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

Note that this is true for descendant classes too. If we had a class Child that extended Test, calling parent::__construct (whether explicitly or implicitly) will use the same value of $foo.

小嗲 2024-11-14 15:20:35

@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 中的错误,或者是否是预期的行为。 有关静态变量作用域的 PHP 文档 说:

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

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

@lonesomeday, that seems to be mostly correct. However, in particular related to your followup comment regarding inheritance, the behavior of static variable scope inside functions seems to be more complex. I'm using PHP 5.3.16 for all my examples.

The summary: the behavior of the static keyword when used to scope a variable within an instance function seems to vary based both on inheritance and where in the function call stack you are.

Here are a few examples.

First, this is similar to your initial example: instantiate a static variable within the __construct() method of a class, create two instances of that class, and see how that variable behaves.

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

So far, no surprises. The variable is instantiated once (to 0) and then incremented with each separate instance.

If you extend class A as B, and instantiate one of each (leaving everything else the same), you get the same behavior:

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

Once again, no surprises. Let's go back to the first example and tweak it a bit. First we move the static variable instantiation/increment call to a member method. Note that I've removed class B for this one:

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

Still the same output.

However, when you extend A as B, and leave the same structure in place:

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

Note the output: in all other cases, $o2->i became 2, except for this one.

So it seems that if we extend A and move the static instantiation to an instance method, we now seem to have introduced a new scope for the $i variable, whereas in all other cases the instances have shared a scope for that static variable.

Even more confusing, consider this example; it's identical to the previous one, but in this case, class B gets its own implementation of setI(), which simply yields to its parent class's implementation:

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

As you can see, $o2->i is now back to being set to 2, which is what we got nearly everywhere (except Example #4)

This, to me, seems very counterintuitive. I would expect that either different instances get their own scopes for that variable, or all instances (including instances of extended classes) share the same scope.

I can't tell if this is a bug in PHP, or if it's expected behavior. The PHP docs on static variable scope say:

A static variable exists only in a local function scope, but it does not lose its value when program execution leaves this scope.

They don't go into detail as to how 'function scope' is defined in the context of object instances, so I'm not sure if the edge case in Example #4 is expected or not.

韶华倾负 2024-11-14 15:20:35

是的,它也记录在文档中,但它位于 变量 部分下范围而不是静态关键字,这可能是你没有找到它的原因。

Yes it will, it's also documented in the docs, but it's under the section Variables Scope instead of the Static Keyword, which might be the reason you didn't find it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文