13

Quick code with the question included:

abstract class ClassParent {
    public static $var1 = "ClassParent";
}

class ClassChild1 extends ClassParent{
    public static function setvar1(){
        ClassChild1::$var1 = "ClassChild1";     
    }
}

class ClassChild2 extends ClassParent{
    public static function setvar1(){
        ClassChild2::$var1 = "ClassChild2";
    }
}


ClassChild1::setvar1();

echo ClassChild2::$var1;
// Returns "ClassChild1". Shouldn't this still be "ClassParent"?

I am assuming that the above is expected behaviour and not a PHP bug. In that case, how could I declare a static variable in the parent class which will be handled separately for the child classes. In other words, I want to have separate static values PER CHILD CLASS. Must I declare the static variable specifically in the child classes or is there perhaps another way?

Thanks!

miken32
  • 42,008
  • 16
  • 111
  • 154
Aron
  • 3,419
  • 3
  • 30
  • 42

3 Answers3

17

EDIT: On further investigation, I think what you're asking is not directly possible, even with late static binding. Actually, I am a little surprised.

The answer to this question provides some workarounds.

Original answer:


In a parent class, if you refer to a static variable in the form:

self::$var

It will use that same variable in all inherited classes (so all child classes will be still accessing the variable in the parent class).

This is because the binding for the self keyword is done at compile-time, not run-time.

As of PHP 5.3, PHP supports late static binding, using the static keyword. So, in your classes, refer to the variable with:

static::$var

And 'static' will be resolved to the child class at run-time, so there will be a separate static variable for each child class.

Community
  • 1
  • 1
Lauren
  • 1,480
  • 1
  • 13
  • 36
  • Thanks for the answer. I am using 5.3. This should not be any different from what I coded above - I just explicitly stated the class name instead of using 'static'. And indeed, changing them to static produces the same result. – Aron Apr 01 '11 at 13:04
  • ...the only thing that seems to work is if I explicitly declare the variable in the child class. But - if possible - I would like to avoid this solution... – Aron Apr 01 '11 at 13:05
  • When I posted my answer your code snippet wasn't complete. I don't think there is a clean solution to this. I updated my answer with more information. – Lauren Apr 01 '11 at 13:07
  • Cool thanks. I'll end up using a static array which is ugly but solves my problem for now. – Aron Apr 01 '11 at 13:12
5

Thanks for this question! I had some problems I couldn't track and this has helped me solve them. :)

You might be interested to know that there is a bug report for this behavior which includes the workaround. In your case this would be:

class ClassChild1 extends ClassParent{
    public static function setvar1(){
        $tmp = 'x';
        static::$var1 =& $tmp; // break reference
        // and now this works as expected: (changes only ClassChild1::$var1)
        static::$var1 = "ClassChild1";     
    }
}
// do the same in ClassChild2...

Ugly as hell, I agree - but PHP works as expected this way, plus it has no side effects.

This is indeed a very doubtful (and poorly documented) "feature" in my eyes - let's hope they change it someday.

johndodo
  • 17,247
  • 15
  • 96
  • 113
  • 1
    Please note this was declared as 'Not a bug'. Even if it is a feature, it should be documented :). – Mark Leighton Fisher Feb 19 '13 at 13:32
  • Well, `magic_quotes` and `safe_mode` were not bugs either, but boy was I glad when they removed them. ;) PHP devs have a history of some (in my eyes, of course) poor design decisions and this is certainly one of them (again: IMnsHO). – johndodo Feb 22 '13 at 13:34
  • I'm expecting (didn't test) that redeclaring the $var1 as a static in both subclasses should also do the job. And it would be a hell of a lot less ugly ;) – Tommy Bravo Feb 24 '16 at 15:57
  • 1
    While I agree with you that it would be "less ugly", the meaning of redeclaration would be less obvious, so code readability would actually be lower. It is very easy to just not set some variable and then spend hours debugging the unusual behavior. With this approach it is more difficult for programmer to make this (easy to make) mistake. – johndodo Feb 25 '16 at 07:55
  • 1
    Doesn't work anymore in PHP 7.3 and above – EvE Jan 10 '22 at 22:59
0

Ugly solution, but it works.

I've moved static $var1 to a trait that is required to be used in child classes.

It is essentially the same as declaring $var1 in each child class. However, using this method there is no chance you forget to declare $var1.

trait Var1Trait
{
    public static $var1 = "ClassParent";

    protected function requiresVar1Trait()
    {
    }
}

abstract class ClassParent
{
    abstract protected function requiresVar1Trait(); // make sure that Var1Trait is used in child classes
}

class ClassChild1 extends ClassParent
{
    use Var1Trait;

    public static function setvar1()
    {
        ClassChild1::$var1 = "ClassChild1";
    }
}

class ClassChild2 extends ClassParent
{
    use Var1Trait;

    public static function setvar1()
    {
        ClassChild2::$var1 = "ClassChild2";
    }
}


ClassChild1::setvar1();

echo ClassChild2::$var1;
// Returns "ClassParent" as requested
EvE
  • 734
  • 3
  • 13