非静态方法中的静态在实例之间共享

时间:2010-02-23 20:19:11

标签: php

我遇到了一些意外的行为,在对象方法中定义的静态变量是跨实例共享的。这可能是已知的行为,但是当我浏览PHP文档时,我无法在对象方法中找到静态定义变量的实例。

这是我遇到的行为的减少:

<?php

class Foo {
  public function dofoo() {
    static $i = 0;
    echo $i++ . '<br>';
  }
}

$f = new Foo;
$g = new Foo;

$f->dofoo(); // expected 0, got 0
$f->dofoo(); // expected 1, got 1
$f->dofoo(); // expected 2, got 2

$g->dofoo(); // expected 0, got 3
$g->dofoo(); // expected 1, got 4
$g->dofoo(); // expected 2, got 5

现在,我希望$i是每个实例的静态 ,但实际上$i在实例之间共享。对于我自己的启发,有人可以详细说明为什么会这样,并在php.net上记录它的位置?

5 个答案:

答案 0 :(得分:6)

这是静态的very definition

如果您希望成员特定于对象的实例,则使用class properties

e.g。

<?php

class Foo
{
    protected $_count = 0;
    public function doFoo()
    {
        echo $this->_count++, '<br>';
    }
}

编辑:好的,我将文档链接到OOP静态属性。但这个概念是一样的。如果您阅读了variable scope文档,则会看到:

  

注意:静态声明在编译时解析。

因此,当您的脚本被编译时(在执行之前),静态是“设置”(不确定要使用的术语)。无论您实例化多少个对象,当该函数被“构建”时,静态变量引用与其他人相同的副本。

答案 1 :(得分:2)

我同意当前的PHP文档对于“范围”对非静态方法中的静态变量的确切含义并不十分清楚。

当然,正确(如hobodave所示)“静态”通常表示“每个类”,但静态类属性与静态完全相同(非静态)方法中的变量,后者是通过方法“作用域”的(类中的每个方法都可以有自己的静态$ foo变量,但最多只能有一个名为$ foo的静态类成员)。 / p>

我认为虽然PHP 5行为一致(“静态”总是意味着“每个类一个共享实例”),但不是唯一的方法PHP 可以行为。

例如,大多数人使用静态函数变量来跨函数调用持久化状态,对于全局函数,PHP行为正是大多数人所期望的。因此,有可能想象一个PHP解释器在方法调用中维护某些方法变量的状态,并且“按实例”这样做,这实际上是我第一次将本地方法变量声明为静态时也应该发生的事情

答案 2 :(得分:1)

这就是静态,它是所有类的实例中的相同变量。

您希望编写此代码,以便该变量是该类实例的私有成员。

class Foo {
  private $i = 0;
  public function dofoo() {
    echo $this->i++ . '<br>';
  }
}

答案 3 :(得分:1)

静态关键字可以使用with variables,也可以使用with class methods and properties。静态变量是在PHP 4中引入的(我想,它可能早一些)。 PHP 5中引入了静态类成员/方法。

因此,根据手册,一个静态变量

  

变量范围的另一个重要特征是静态   变量。静态变量仅存在于本地函数中   范围,但它在程序执行时不会丢失其值   离开了这个范围。

这与您描述的行为一致。如果需要每个实例变量,请使用常规类成员。

答案 4 :(得分:0)

很长一段时间7年了,但无论如何它都在这里。

所有类都有默认构造函数为什么我这样说?!? 因为如果在构造函数中定义默认行为,则该类的每个实例都将受到影响。

示例:

namespace Statics;

class Foo
{
    protected static $_count;

    public function Bar()
    {
        return self::$_count++;
    }

    public function __construct()
    {
        self::$_count = 0;
    }
}

导致:

require 'Foo.php';

use Statics\Foo;

$bar = new Foo();
echo $bar->bar().'<br>';
echo $bar->bar().'<br>';
echo $bar->bar().'<br>';

$barcode = new Foo();
echo $barcode->bar().'<br>';
echo $barcode->bar().'<br>';
echo $barcode->bar().'<br>';

0
1
2
0
1
2

上层的每个新实例都将从0开始! 静态计数行为不会在多个实例之间共享,因为它将从构造函数中指定的值开始。

如果需要跨多个实例共享数据,您只需要定义一个静态变量并在构造函数外部分配默认数据!

示例:

namespace Statics;

class Foo
{
    //default value
    protected static $_count = 0;

    public function Bar()
    {
        return self::$_count++;
    }

    public function __construct()
    {
       //do something else
    }
}

导致:

require 'Foo.php';

use Statics\Foo;

$bar = new Foo();
echo $bar->bar().'<br>';
echo $bar->bar().'<br>';
echo $bar->bar().'<br>';

$barcode = new Foo();
echo $barcode->bar().'<br>';
echo $barcode->bar().'<br>';
echo $barcode->bar().'<br>';

0
1
2
3
4
5

正如您所看到的结果完全不同,类实例之间的内存空间分配是相同的,但它可以根据您定义默认值的方式产生不同的结果。

我希望它有所帮助,而不是上述答案是错误的,但我觉得从这个角度理解所有概念很重要。

问候,来自葡萄牙!