Sideskipping PHP’s Late Static Binding until 5.3

Posted: June 30th, 2009 | Author: | Filed under: PHP | Comments Off on Sideskipping PHP’s Late Static Binding until 5.3

bindingThose of you who have experience with mixing inheritance and static members have undoubtedly run into problems regarding late static binding. When some parent class higher up in the inheritance tree has static members (methods or attibutes), getting these to work with child-classes is impossible, e.g.:

class A {
    private static $sValue = 'show from class A';
    public static function show(){
        echo self::$sValue;
    }
}
class B extends A {
    private static $sValue = 'show from class B';
}
B::show(); // echos "show from class A", not "show from class B"

This behaviour is noted as “bug” and will be changed in version 5.3.

This “quirk” is caused by the php compiler, as static members are joined to their surroundings at compile time. This means that the self keyword will always refer to the class it appears in, in the case of the example class A.

This issue is dealt with in php 5.3, where late static binding is added to the language. Aside from the usual self keyword, that is still tied to the class it appears in, the static keyword gets a new meaning. In a static context it is tied to the class that the static call is made on. This is however, not here until 5.3.

The reason for me to need this, is that in some cases I need values that apply to all instances of a class: classic static values you may think. But here is the trick: I need the values to be set and maintained in a common parent class, but each extending class must have it’s own values (same for all instances of that particular child, but different from other child classes). So this is impossible to handle “the right way” until late static binding.

One often proposed solution is to just supply a non-static member and give it the same values for all instances. This is exactly what I don’t want, and I find it to be a bad practice. Consider the value in question to be a rather lengthy multi-dimensional array or tree or other data structure, and I have 200 instances: this approach will leave me with 200 (hopefully) identical structures, each consuming it’s own resources, while I need only one. Obviously, there may be workarounds here too, such as keeping 200 references rather than 200 values, but the thought of all this bookkeeping alone brings me pain.

The workaround I use for this setting may also be far from beautiful, but desperate times call for desperate measures. All attempts to use any kind of static behaviour are out the door, using only member functions. Imagine a variable $sMember (String) which we like to keep our one value in, accessible to all instances of the class. Create a get/set method for it in the parent class:

protected function sMember($sVal = null){
    static $sMember;
    if (false === is_null($sVal)) $sMember = $sVal;
    return $sMember;
}

Now this is magical trick: the $asMember variable is not an instance or a class member, but it is tied to the class that calls it, acting like a static member. Moreover, each extending class can apply it’s own values without disturbing the values set by other extending classes:

class A {
    public function sMember($sVal = null){ // public for demonstration purposes
        static $sMember;
        if (false === is_null($sVal)) $sMember = $sVal;
        return $sMember;
    }
}
class B extends A {}
class C extends A {}

$b1 = new B;
$b2 = new B;
$c = new C;

echo $b1->sMember('test'); // test
echo $b1->sMember(); // test
echo $b2->sMember(); // test

echo $b2->sMember('example'); // example
echo $b1->sMember(); // example
echo $b2->sMember(); // example

echo $c->sMember('c - member'); // c - member
echo $b1->sMember(); // example
echo $b2->sMember(); // example
echo $c->sMember(); // c - member

Obviously, there is a catch. The sMember value is tied to the class calling it, and therefore does not inherit well (= not at all), indicating:

class D extends C {}
$d = new D;
var_dump($d->sMember()); // NULL

Too bad, but close enough for me.

As always, I am very interested in other/better ways to do this. For now, we can only hope that the next versions of php do not change the behaviour of the static keyword as it is used here…


Comments are closed.