静态函数变量与静态 class 变量

static function variable vs static class variable

考虑以下几点:

class Super
{
    static protected $class_var = 0;

    public function __construct()
    {
        static $function_var = 0;

        static::$class_var++;
        $function_var++;

        echo sprintf("class_name : %s, class_var : %s, function_var : %s\n", static::class, static::$class_var, $function_var);
    }

    public static function test()
    {
        static $function_var = 0;

        static::$class_var++;
        $function_var++;

        echo sprintf("class_name : %s, class_var : %s, function_var : %s\n", static::class, static::$class_var, $function_var);
    }
}

class A extends Super {}
class B extends Super {}

echo "Object calls\n";
new A();
new A();
new B();
echo "Class calls\n";
A::test();
A::test();
B::test();

PHP 7.2.7 输出:

Object calls
class_name : A, class_var : 1, function_var : 1
class_name : A, class_var : 2, function_var : 2
class_name : B, class_var : 3, function_var : 3
Class calls
class_name : A, class_var : 4, function_var : 1
class_name : A, class_var : 5, function_var : 2
class_name : B, class_var : 6, function_var : 1

为什么 A 和 B 在 class 上调用时有自己的静态函数 var。而 A 和 B 在调用实例时共享静态函数 var。或者更笼统地说,它们在内部究竟是如何工作的?

一般行为可以这样解释(下面有一个关于构造函数的附带条件):静态 class 属性的作用域是针对 class 的,其中它们是 定义的,静态方法变量的范围针对 class,它们被 调用

您的 test 方法被声明为静态的事实在这里并不重要,因此我将其缩减为以下测试脚本:

class Super
{
    static protected $class_var = 0;

    public function test()
    {
        static $function_var = 0;

        static::$class_var++;
        $function_var++;

        echo sprintf("class_name : %s, class_var : %s, function_var : %s", static::class, static::$class_var, $function_var), PHP_EOL;
    }
}

class A extends Super {}
class B extends Super {}

因为它在 class 上 定义 ,所以 $class_var 将始终链接到该范围。然而许多 classes 扩展 Super,它们将引用同一个变量(除非它们覆盖它)。

另一方面,

$function_var 的范围是 class(es),其中 test 方法被 调用 。对 A 的所有实例的调用将共享一个,对 B 的所有实例的调用将共享一个。

$a = new A;
$b = new B;

$a->test(); // class_name : A, class_var : 1, function_var : 1
$a->test(); // class_name : A, class_var : 2, function_var : 2
$b->test(); // class_name : B, class_var : 3, function_var : 1

构造函数(前提条件):

您问题中的另一个问题是构造函数的行为不同于任何其他 class 方法,因为尽管在语法上相似,但它根本不是一种方法。与 class 属性类似,在构造函数中声明为静态的任何变量的范围都在 class 范围内,其中它是 defined:

如果我们加入:

public function __construct()
{
    static $constructor_var = 0;
    $constructor_var++;

    echo sprintf("class_name : %s, constructor_var : %s", static::class, $constructor_var), PHP_EOL;
}

那么我们可以演示如下:

$a = new A; // class_name : A, constructor_var : 1
$b = new B; // class_name : B, constructor_var : 2

除非任何子class重写构造函数,否则它们将共享同一个 $constructor_var.

实例

不幸的是,我无法在文档中找到关于这些内容的任何一个很好的简单摘要,而且我也不确定其中有多少是设计使然的。构造函数中的静态变量尤其不是我认为我遇到过的东西,静态函数变量本身也越来越少见。但是知道事情是如何运作的很有趣,这是个好问题。