TL;DR 版本
静态属性$a
在每个类中都是不同的符号,但实际上它是相同的变量$a = 1; $b = &$a;
, $a
and $b
是相同的变量(即它们位于相同的参考集上)。进行简单作业时($b = $v;
),两个符号的值都会改变;通过引用进行分配时($b = &$v;
), only $b
会受到影响。
原版
首先,让我们了解静态属性是如何“继承”的。zend_do_inheritance
迭代超类静态属性调用inherit_static_prop
:
zend_hash_apply_with_arguments(&parent_ce->default_static_members TSRMLS_CC,
(apply_func_args_t)inherit_static_prop, 1, &ce->default_static_members);
其定义为:
static int inherit_static_prop(zval **p TSRMLS_DC, int num_args,
va_list args, const zend_hash_key *key)
{
HashTable *target = va_arg(args, HashTable*);
if (!zend_hash_quick_exists(target, key->arKey, key->nKeyLength, key->h)) {
SEPARATE_ZVAL_TO_MAKE_IS_REF(p);
if (zend_hash_quick_add(target, key->arKey, key->nKeyLength, key->h, p,
sizeof(zval*), NULL) == SUCCESS) {
Z_ADDREF_PP(p);
}
}
return ZEND_HASH_APPLY_KEEP;
}
我们来翻译一下这个。 PHP 使用写时复制,这意味着如果值具有相同的内容,它将尝试共享值的相同实际内存表示形式 (zval)。inherit_static_prop
为每个超类静态属性调用,以便可以将其复制到子类。实施inherit_static_prop
确保子类的静态属性将是 PHP 引用,无论父类的 zval 是否共享(特别是,如果超类有引用,则子类将共享 zval,如果没有,则 zval 将共享被复制并且新的 zval 将被引用;第二种情况我们在这里并不真正感兴趣)。
所以基本上,当 A、B 和 C 形成时,$a
对于每个类来说,将是不同的符号(即,每个类都有其属性哈希表,并且每个哈希表都有自己的条目$a
),但底层 zval 将是相同的并且它将是一个引用。
你有类似的东西:
A::$a -> zval_1 (ref, reference count 3);
B::$a -> zval_1 (ref, reference count 3);
C::$a -> zval_1 (ref, reference count 3);
因此,当你做正常的作业时
static::$a = $v;
由于所有三个变量共享相同的 zval 及其引用,因此所有三个变量都将采用该值$v
。如果你这样做的话,结果是一样的:
$a = 1;
$b = &$a;
$a = 2; //both $a and $b are now 1
另一方面,当你这样做时
static::$a =& $v;
你将打破参考集。假设您在 A 类中执行此操作。您现在拥有:
//reference count is 2 and ref flag is set, but as soon as
//$v goes out of scope, reference count will be 1 and
//the reference flag will be cleared
A::$a -> zval_2 (ref, reference count 2);
B::$a -> zval_1 (ref, reference count 2);
C::$a -> zval_1 (ref, reference count 2);
类似的是
$a = 1;
$b = &$a;
$v = 3;
$b = &$v; //$a is 1, $b is 3
解决方法
正如戈登现已删除的答案中所指出的那样,三个类的属性之间的引用集也可以通过重新声明每个类中的属性来破坏:
class B extends A { protected static $a; }
class C extends A { protected static $a; }
这是因为如果重新声明该属性,则不会从超类复制到子类(请参阅条件if (!zend_hash_quick_exists(target, key->arKey, key->nKeyLength, key->h))
in inherit_static_prop
).