Reputation: 624
I want to implement a class, which caches some internal results. These results are the same for all instances of the class, i.e. it might be wise to share this cache among all instances.
However, these results may be different for sub-classes, i.e. the cache shall not be shared with sub-classes. Since the cache is a good idea for all sub-classes as well, the mechanism shall be inherited nevertheless. But each sub-class has to use a different static array.
I can think of various hacks and complicated patterns to attain this goal, but none looks really sane. Does anybody know of an efficient pattern in PHP?
Upvotes: 2
Views: 1597
Reputation: 624
This is what I distilled from these answers and I think it may be worth to share. Too large for a comment and I didn't want to do such a substantial edit to Jon's answer.
The solution has a use counter, which even frees the caches, when the last class instance is unset. And it seems to work from the base class without any hassle in the children.
class A {
private static $aCacheAll = array();
protected $aCache;
function __construct(){
$type = get_class($this);
if(!isset(self::$aCacheAll[$type])){
self::$aCacheAll[$type] = array(0,array());
}
self::$aCacheAll[$type][0]++;
$this->aCache =& self::$aCacheAll[$type][1];
}
function __destruct(){
$type = get_class($this);
if(!isset(self::$aCacheAll[$type])) return;
self::$aCacheAll[$type][0]--;
if(self::$aCacheAll[$type][0] <= 0){
unset(self::$aCacheAll[$type]);
// for testing
print "Freed memory for $type cache\n";
}
}
/**
* Brain dead test functions
*/
function set($x){
$this->aCache[$x] = 1;
}
function get() {
var_dump($this->aCache);
}
}
class B extends A {}
/*
* Test it
*/
$a1 = new A();
print "Empty on start: ";
$a1->get();
$a1->set('a');
print "Set a for A: ";
$a1->get();
$a2 = new A();
print "... and have it in \$a2, too: ";
$a2->get();
$b = new B();
print "But not in B: ";
$b->get();
$b->set('b');
print "Set b for B: ";
$b->get();
print "... is not in A: ";
$a2->get();
unset($a1);
print "Cache is retained, if \$a1 is freed, but vanishes when the final instance is unset: ";
unset($a2);
print "B cache still exists, until the program exits: ";
Upvotes: 1
Reputation: 44376
Simply don't use static at all - in most cases it's not a correct solution. Create some storage object (could be even ArrayObject) and pass it around all MyClass
instances:
$sharedStorage = new ArrayObject();
$anotherStorage = new ArrayObject();
$instanceA = new MyClass($sharedStorage);
$instanceB = new MyClass($sharedStorage);
$instnceC = new MySubClass($anotherStorage);
It's simplier, it's way easier to test or maintenance and what's most important, it doesn't use any hacks, only common, well-known constructions.
And if you're affraid that it could be uses wrong, e.g.:
$instanceA = new MyClass($sharedStroage);
$instanceB = new MyClass($anotherStroage);
Create a factory class that will take the responsibility for creating new objects.
By the way, why do you need such a strange requirement - a common storage for all instances of given class but not a subtypes of that class?
Upvotes: 0
Reputation: 437336
A combination of a private static
variable to hold the cached data for all subclasses and protected
accessor functions sounds like it would work, and it's not too complicated:
class Base {
private static $cache = array();
protected getCacheItem($key) {
$type = get_class($this);
if (!isset(self::$cache[$type])) {
self::$cache[$type] = array();
}
// add checks to taste
return self::$cache[$type][$key];
}
protected function setCacheItem($key, $value) {
// similar to the above
}
}
Taking it from here, you can get fancy so that accessing the cache is very convenient at the price of becoming somewhat evil:
class Base {
private static $cache = array();
// WARNING! QUESTIONABLE PRACTICE: __get returns by reference
// Also, overriding __get ONLY might not lead to an intuitive experience
// As always, documenting non-standard behavior is paramount!
public function &__get($name) {
if ($name != 'cache') {
// error handling
}
$type = get_class($this);
if (!isset(self::$cache[$type])) {
self::$cache[$type] = array();
}
return self::$cache[$type];
}
}
You would then be able to use this like
$b = new Base;
$b->cache['foo'] = 'bar';
Upvotes: 4
Reputation: 14782
Static class variables?
http://php.net/manual/de/language.oop5.static.php
class Test {
static protected $sharedInClassInstances = array();
private $myOwnInstanceVariable = array();
public function __construct() {}
}
All Instances of Test
should be able to Access Test::$sharedInClassInstances
that is shared with all other instances. While $this->myOwnInstanceVariable
is only available for the single object.
Upvotes: 0