Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how static variables in traits don't lose its value when used inside class in PHP?

How don't the static properties in traits lose its value when used by classes Although Traits are language assisted Copy/paste? let's consider the following example,

     trait Test{
    public static $var = 1;

   public static  function increment(){
        self::$var ++;
        var_dump(self::$var);
    }
}


Test::increment();//2



class Test2{

    use Test;

}

var_dump(Test2::$var);//2
Test2::increment();//3

This behaviour is correct in inheritance where the child extends the parent, so the child uses the parent static variables, but according to traits where copy and paste are applied, how does this work and correct?


This is an update to add another reason to the confusion I have, if the static values are reserved to specific contexts like specific class or function, so how do I able to use updated values of static properties from a different context ( the new class). 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? this is against the contexts approach we understand.


Update 3: all of this confusion depends on if the use keyword is importing the trait members in class or copy/paste?

like image 625
youhana Avatar asked Nov 18 '22 21:11

youhana


1 Answers

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:

  1. A trait alone always has its own context with its own state of current values.
  2. 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.
  3. 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
like image 169
goulashsoup Avatar answered Dec 20 '22 00:12

goulashsoup