Reputation: 146660
In an existing code base, I have a static builder method that returns an instance. Here's a simplified example:
class Grandparent{
}
class Parent extends Grandparent{
}
class Child extends Parent{
public static fetchChildById($id){
// ...
return new Child;
}
}
In real code I have a single Grandparent
class and several subclasses similar to Parent
and Child
(not just Parent
and Child
).
I now need to implement a new method at Grandparent
to be used at fetchChildById()
. Such method needs to make use of certain piece of data that's common to all children from the same parent. Since I don't have a class instance yet I'm forced to make everything static but, of course, that won't work properly because it isn't possible to overrite static properties and methods:
class Grandparent{
protected static $data = array(
'default',
);
protected static function filter(){
foreach(self::$data as $i){ // <!-- Will always be `default'
// ...
}
}
}
class Parent extends Grandparent{
protected static $data = array(
'one',
'two',
);
}
class Child extends Parent{
public static fetchChildById($id){
self::filter();
// ...
return new Child;
}
}
I believe it's a use case for late static binding but code needs to run on PHP/5.2.0 :(
I'm not very fond of the obvious workarounds I've thought about:
Creating a separate builder class suggest more refacting than I can afford at this time:
$builder = new ChildBuilder;
$bart = $builder->fetchChildById(1);
Creating additional instances looks ugly (and implies many changes as well):
$builder = new Child;
$bart = $builder->fetchChildById(1);
Global variables... Oh well, I'm not so desperate yet.
Am I overlooking some obvious mechanism to customize $data
?
Upvotes: 0
Views: 147
Reputation: 437854
Here's an alternative using reflection. It will require modification of all fetchChildById
implementations, but it's trivial enough to be done with global find/replace:
self::filter(__CLASS__); // this is the modification
Then filter
would become:
protected static function filter($className){
$reflect = new ReflectionClass($className);
$data = $reflect->getStaticPropertyValue('data');
foreach($data as $i){
// ...
}
}
Update: The property $data
needs to be public for the above to work (apologies -- I wrote public
during exploration). But there's an equivalent version that doesn't have this requirement:
$reflect = new ReflectionProperty($className, 'data');
$reflect->setAccessible(true);
$data = $reflect->getValue();
Upvotes: 1