Inheritance of static members in PHP

19,886

Solution 1

Classic example of why using statics as globals (functions in this case) is a bad idea no matter the language.

The most robust method is to create multiple implementation sub classes of an abstract base "Action" class.

Then to try and remove some of the annoyance of instantiating an instance of the class just to call it's methods, you can wrap it in a factory of some sort.

For example:

abstract class AbstractAction {
  public abstract function do();
}

class FooAction extends AbstractAction {
  public function do() {
    echo "Do Foo Action";
  }
}

class BarAction extends AbstractAction {
  public function do() {
    echo "Do Bar Action";
  }
}

Then create a factory to "aid" in instantiation of the function

class ActionFactory {
  public static function get($action_name) {
    //... return AbstractAction instance here
  }  
}

Then use it as:

ActionFactory::get('foo')->do();

Solution 2

Actually I think it is not true: you can ovverride static propeties (you need >=5.3 PHP for that). But you have to be careful when refrencing for that static property (and this is the mistake in the original code)

You need to use static::$myStaticProperty instead of using self::$myStaticProperty

self:: will refrence to the current class so if you are inside an inherited static method this will refrence the static property of that class defined that method! While using reference keyword static:: will act like $this - when you are using instance methods/propeties.

doSomething() is an inherited static method in class Bar in your example. Since you used self:: there, it will reference to the static property of class Foo. This is the reason why you didn't see any difference... Try to change self:: to static::!

Here is a code example - I used it myself to test those things. We have static property/method inheritance, override and value change in it - run it and you will see the result!

class A {

    // a static property - we will test override with it
    protected static $var = 'class A var - override';
    // a static property - we will test value overwrite with it
    protected static $var2 = 'class A var2 - value overwrite';


    public static function myStaticOverridePropertyTest() {
        return static::$var;
    }
    public static function myStaticValueOverwritePropertyTest() {
        return static::$var2;
    }

    /**
     * This method is defined only here - class B will inherit this one!
     * We use it to test the difference btw self:: and static::
     * 
     * @return string
     */
    public static function myStaticMethodTest() {
        //return self::getValue();
        return static::getValue();
    }

    /**
     * This method will be overwritten in class B
     * @return string
     */
    protected static function getValue() {
        return 'value from class A';
    }
}


class B extends A {

    // we override this inherited static property
    protected static $var = 'class B var - override';

    /**
     * This method is overwritten from class A
     * @return string
     */
    protected static function getValue() {
        return 'value from class B';
    }

    /**
     * We modify the value of the inherited $var2 static property
     */
    public static function modStaticProperty() {
        self::$var2 = 'class B - altered value! - value overwrite';
    }
}

echo ("-- testing class A:\n");
echo (A::myStaticOverridePropertyTest(). "\n");
echo (A::myStaticValueOverwritePropertyTest(). "\n");
echo (A::myStaticMethodTest(). "\n");

echo ("-- now testing class B:\n");
echo (B::myStaticOverridePropertyTest(). "\n");
echo (B::myStaticValueOverwritePropertyTest(). "\n");
echo ("  now invoking B::modStaticProperty()     .\n");
B::modStaticProperty();
echo (B::myStaticValueOverwritePropertyTest(). "\n");

echo ("-- now re-testing class A:\n");
echo (A::myStaticOverridePropertyTest(). "\n");
echo (A::myStaticValueOverwritePropertyTest(). "\n");
echo (A::myStaticMethodTest(). "\n");

This will output:

-- testing class A:
class A var - override
class A var2 - value overwrite
value from class A
-- now testing class B:
class B var - override
class A var2 - value overwrite
now invoking B::modStaticProperty() ...
class B - altered value! - value overwrite
-- now re-testing class A:
class A var - override
class B - altered value! - value overwrite
value from class A

And here we are, you can see the difference between overriden and only value overwritten static properties... look the output line I marked with bold! When we invoked the modStaticProperty() of class B, it changed the value of that static variable in class A too. Since that static property was inherited and was not overriden! Think about it...

Solution 3

The forthcoming PHP 5.3.0 release includes late static binding, which might help. Using this feature you could use a static variable inside a static method, and let the late static binding take care of finding the "right" method.

class Foo {
    public static function getDefault() {
        static $default = 'DEFAULT';
        return $default;
    }
    public static function doSomething ($param) {
        $default=static::getDefault(); // here is the late static binding
        $param = ($param === FALSE) ? $default : $param;
        return $param;

    }
}

class Bar extends Foo {
     public static function getDefault() {
        static $default = 'NEW DEFAULT FOR CHILD CLASS';
        return $default;
    }
}
Share:
19,886
PartialOrder
Author by

PartialOrder

Yup. It's me.

Updated on June 03, 2022

Comments

  • PartialOrder
    PartialOrder about 2 years

    In PHP, if a static attribute is defined in the parent class, it cannot be overridden in a child class. But I'm wondering if there's any way around this.

    I'm trying to write a wrapper for someone else's (somewhat clunky) function. The function in question can be applied to lots of different data types but requires different flags and options for each. But 99% of the time, a default for each type would suffice.

    It would be nice if this could be done with inheritance, without having to write new functions each time. For example:

    class Foo {
        public static $default = 'DEFAULT';
    
        public static function doSomething ($param = FALSE ) {
            $param = ($param === FALSE) ? self::$default : $param;
            return $param;
        }
    }
    
    class Bar extends Foo {
        public static $default = 'NEW DEFAULT FOR CHILD CLASS';
    }
    
    echo Foo::doSomething() . "\n"; 
    // echoes 'DEFAULT'
    
    echo Bar::doSomething() . "\n"; 
    // echoes 'DEFAULT' not 'NEW DEFAULT FOR CHILD CLASS' 
    // because it references $default in the parent class :(
    
  • PartialOrder
    PartialOrder over 15 years
    This would be lovely and thanks for the info. But alas, I need something that will run in my current production environment (5.2.6). THANKS!
  • Павел Иванов
    Павел Иванов about 7 years
    Will not work if fields are public and changed outside. sandbox.onlinephpfunctions.com/code/…
  • T30
    T30 over 6 years
    TL;DR: You need to use static::$myStaticProperty instead of using self::$myStaticProperty ;)