How don't the static properties in traits lose its value when used by classes Although Traits are language assisted Copy/paste?
A trait has its own context independent of classes that use the trait, at least for static properties. If a class uses a trait with a static property, that class gets its own context, that is "its own" static property with the initial value copied from the 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
This is an update to add another reason to the confusion I have ... I mean, if the context of trait Test is called for example Context1, and the class Test2 context is called Context2, how do I access the reserved values of the first context in another context?
Like I have shown, you can always access a static property of a specific context with the class/trait name and the scope resolution operator (::
):
T::$staticProp; // Access context of trait T
A::$staticProp; // Access context of class A
all of this confusion depends on if the use keyword is importing the trait members in class or copy/paste?
I think the best way to understand the behavior is as follows:
- A trait alone always has its own context with its own state of current values.
- When a class uses the trait, all members are copied with their current values. Now the class and the trait have two independent contexts each with its own state.
- Changing the value of one context does not effect the other context.
let's consider the following example,
Here is what happens in your example:
With classes a child class includes the context of its parent:
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
normally references the class it is used in or its parent if we try to access an inherited member:
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
When using traits self
either references the traits context or the copied independent one of a class. Thats what happens in your example:
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