| 
<?php
// Name: CachedArray.class.php
 // Author: RReverser
 // Description: Cached virtual array for PHP
 // Version: 1.1
 // Created: 27.03.2011
 
 class CachedArrayIterator implements Iterator
 {
 private $indexArray, $cachedArray;
 
 function __construct(CachedArray $ca)
 {
 $this->cachedArray = $ca;
 $this->indexArray = $ca->items();
 }
 
 function current() { return $this->valid() ? $this->cachedArray[$this->key()] : NULL; }
 function key() { return current($this->indexArray); }
 function next() { next($this->indexArray); return $this->current(); }
 function rewind() { reset($this->indexArray); return $this->current(); }
 function valid() { return key($this->indexArray) !== NULL; }
 }
 
 // Cached array class - implements full array-style or object-style access to elements
 
 abstract class CachedArray implements ArrayAccess, Serializable, IteratorAggregate
 {
 const sDELETED = -2, sEXISTS = -1, sUNCHANGED = 0, sMODIFIED = 1, sNEW = 2;
 
 protected $cache = array(), $addCount = 0, $listed = FALSE, $autoFlush = TRUE;
 
 // Your functions (for overriding)
 abstract function getItem($index);
 abstract function setItem($index, $value);
 abstract function addItem($value = NULL);
 function delItem($index) { $this->setItem($index, NULL); }
 function hasItem($index) { return $this[$index] !== NULL; }
 function getList() { return array(); }
 
 function __construct($autoFlush = TRUE) { $this->autoFlush = $autoFlush; }
 function __destruct() { $this->autoFlush ? $this->flush() : FALSE; }
 
 // Functions for object-style access
 function __get($index) { return $this[$index]; }
 function __set($index, $value) { $this[$index] = $value; }
 function __isset($index) { return isset($this[$index]); }
 function __unset($index) { unset($this[$index]); }
 
 // Functions for array-style access
 function offsetGet($index)
 {
 if (!isset($this->cache[$index]) || $this->cache[$index][0] == self::sEXISTS)
 {
 $this->cache[$index] = array(self::sUNCHANGED, $this->getItem($index));
 }
 
 return $this->cache[$index][0] >= 0 ? $this->cache[$index][1] : NULL;
 }
 
 function offsetSet($index, $value)
 {
 if ($index === NULL)
 {
 $index = 'new_' . $this->addCount++;
 $State = self::sNEW;
 } else
 {
 $State = isset($this[$index]) ? self::sMODIFIED : self::sNEW;
 }
 
 $this->cache[$index] = array($State, $value);
 }
 
 function offsetExists($index)
 {
 if (isset($this->cache[$index]) && $this->cache[$index][0] != self::sDELETED)
 {
 return isset($this->cache[$index][1]);
 } else
 {
 $exists = $this->hasItem($index);
 $this->cache[$index] = array($exists ? self::sEXISTS : self::sUNCHANGED, NULL);
 return $exists;
 }
 }
 
 function offsetUnset($index)
 {
 $this->cache[$index][0] = self::sDELETED;
 }
 
 // Functions for serialization
 function serialize() { return serialize(array($this->cache, $this->addCount, $this->listed, $this->autoFlush)); }
 function unserialize($str) { list($this->cache, $this->addCount, $this->listed, $this->autoFlush) = unserialize($str); }
 
 // Returning iterator object
 function getIterator() { return new CachedArrayIterator($this); }
 
 // Flushing procedure
 function flush()
 {
 $movements = array();
 
 foreach ($this->cache as $index => &$data)
 {
 switch ($data[0])
 {
 case self::sDELETED:
 $this->delItem($index);
 $data = array(self::sUNCHANGED, NULL);
 break;
 
 case self::sMODIFIED:
 $this->setItem($index, $data[1]);
 $data[0] = self::sUNCHANGED;
 break;
 
 case self::sNEW:
 $newIndex = $this->addItem($data[1]);
 $movements[$index] = $newIndex;
 break;
 }
 }
 
 foreach ($movements as $from => $to)
 {
 $this->cache[$to] = array(self::sUNCHANGED, $this->cache[$from][1]);
 unset($this->cache[$from]);
 }
 
 $this->addCount = 0;
 }
 
 function drop()
 {
 $this->cache = array();
 $this->addCount = 0;
 $this->listed = FALSE;
 }
 
 // Listing of real and unflushed items
 function items()
 {
 if (!$this->listed)
 {
 $result = $this->getList();
 
 foreach ($result as $innerIndex => $index)
 {
 if (!isset($this->cache[$index])) { $this->cache[$index] = array(self::sEXISTS, NULL); }
 elseif ($this->cache[$index][0] == self::sDELETED) { unset($result[$innerIndex]); }
 }
 
 foreach ($this->cache as $index => $data)
 {
 if ($data[0] == self::sNEW) { $result[] = $index; }
 }
 
 $this->listed = TRUE;
 }
 else
 {
 $result = array();
 
 foreach ($this->cache as $index => $data)
 {
 if ($data[0] >= self::sEXISTS) { $result[] = $index; }
 }
 }
 
 return $result;
 }
 
 }
 |