虽然 Traits 是语言辅助的复制/粘贴,但 Traits 中的静态属性在被类使用时如何不会失去其价值?
特征有自己的上下文,独立于使用该特征的类,至少对于静态属性而言。如果一个类使用带有静态属性的 trait,则该类会获得自己的上下文,即“它自己的”静态属性,其初始值是从 trait 复制而来的:
trait T {
// '$staticProp' exists in the context of T
public static $staticProp = "in T\n";
}
// Without the need of a class that implements the trait, you can already access its value explicitly:
echo T::$staticProp; // in T
class A {
// Now 'A' gets its own '$staticProp', the initial value is copied from the context of the trait 'T'
use T;
}
// Now you can access 'A's '$staticProp' explicitly:
echo A::$staticProp; // in T
// Now we can explicitly change the value of 'A's '$staticProp'. Because 'A' has its own context, 'T::$staticProp' stays untouched:
A::$staticProp = "in A\n";
echo A::$staticProp; // in A
echo T::$staticProp; // in T
这是一个更新,为我的困惑添加了另一个原因......我的意思是,如果调用 trait Test 的上下文,例如 Context1,并且类 Test2 上下文称为 Context2,我如何访问保留值在另一个上下文中的第一个上下文?
正如我所展示的,您始终可以使用类/特征名称和范围解析运算符 (::) 访问特定上下文的静态属性:
T::$staticProp; // Access context of trait T
A::$staticProp; // Access context of class A
所有这些混淆取决于 use 关键字是在类中导入特征成员还是复制/粘贴?
我认为理解行为的最佳方式如下:
- 单独的 trait 总是有自己的上下文和自己的当前值状态。
- 当一个类使用该特征时,所有成员都将复制其当前值。现在类和特征有两个独立的上下文,每个上下文都有自己的状态。
- 更改一个上下文的值不会影响另一个上下文。
让我们考虑以下示例,
这是您的示例中发生的情况:
对于类,子类包括其父类的上下文:
class A {
public static $staticProp = "I am in debt\n";
}
class B extends A {}
echo A::$staticProp; // I am in debt
echo B::$staticProp; // I am in debt
A::$staticProp = "Even more debts\n";
echo A::$staticProp; // Even more debts
echo B::$staticProp; // Even more debts
B::$staticProp = "Paid debts, now debt-free\n";
echo A::$staticProp; // Paid debts, now debt-free
echo B::$staticProp; // Paid debts, now debt-free
self 如果我们尝试访问继承的成员,通常会引用它所在的类或其父类:
class A {
public static $aProp = 0;
}
class B extends A {
public static $bProp = 0;
public static function setProps() {
// Because B has a non inherited property '$bProp' 'self' will reference the context of class 'B':
self::$bProp = 12;
// Because B inherits a property '$aProp' 'self' will reference the inherited context of class 'A':
self::$aProp = 23;
}
public static function printProps() {
echo 'self::$bProp: ' . self::$bProp . "\n";
echo 'self::$aProp: ' . self::$aProp . "\n";
}
}
B::setProps();
B::printProps();
// self::$bProp: 12
// self::$aProp: 23
A::$aProp; // 23
B::$aProp; // 23
// Again 'A' and 'B' share the same context:
A::$aProp = 0;
echo B::$aProp; // 0
当使用特征self 时,要么引用特征上下文,要么引用一个类的独立副本。这就是您的示例中发生的情况:
trait Test {
public static $var = 1;
public static function increment() {
self::$var++;
var_dump(self::$var);
}
}
// This will increment '$var' in the context of trait 'Test'
Test::increment(); // 2
// Access '$var' of trait context 'Test':
var_dump(Test::$var); // 2
class Test2 {
// Members of 'Test' are copied with current values (2) in the context of class 'Test2'
use Test;
}
// Access '$var' of class context 'Test2':
var_dump(Test2::$var); // 2
// This will increment '$var' in the context of class 'Test2'
Test2::increment(); // 3
// '$var' of trait context 'Test' has not changed:
var_dump(Test::$var); // 2