objTable = $objTable; $name = $this->getTable()->getTableName(); $manager = Doctrine_Manager::getInstance(); $dir = $manager->getAttribute(Doctrine::ATTR_CACHE_DIR); if( ! is_dir($dir)) mkdir($dir, 0777); if( ! is_dir($dir.DIRECTORY_SEPARATOR.$name)) mkdir($dir.DIRECTORY_SEPARATOR.$name, 0777); $this->path = $dir.DIRECTORY_SEPARATOR.$name.DIRECTORY_SEPARATOR; /** * create stats file */ if( ! file_exists($this->path.self::STATS_FILE)) touch($this->path.self::STATS_FILE); } /** * @return Doctrine_Table */ public function getTable() { return $this->objTable; } /** * @return integer number of cache files */ public function count() { $c = -1; foreach(glob($this->path."*.cache") as $file) { $c++; } return $c; } /** * getStats * @return array an array of fetch statistics, keys as primary keys * and values as fetch times */ public function getStats() { $f = file_get_contents($this->path.self::STATS_FILE); // every cache file starts with a ":" $f = substr(trim($f),1); $e = explode(":",$f); return array_count_values($e); } /** * store store a Doctrine_Record into file cache * @param Doctrine_Record $record data access object to be stored * @return boolean whether or not storing was successful */ public function store(Doctrine_Record $record) { if($record->getState() != Doctrine_Record::STATE_CLEAN) return false; $file = $this->path.$record->getID().".cache"; if(file_exists($file)) return false; $clone = clone $record; $id = $clone->getID(); $fp = fopen($file,"w+"); fwrite($fp,serialize($clone)); fclose($fp); $this->fetched[] = $id; return true; } /** * clean * @return void */ public function clean() { $stats = $this->getStats(); arsort($stats); $size = $this->objTable->getAttribute(Doctrine::ATTR_CACHE_SIZE); $count = count($stats); $i = 1; $preserve = array(); foreach($stats as $id => $count) { if($i > $size) break; $preserve[$id] = true; $i++; } foreach(glob($this->path."*.cache") as $file) { $e = explode(".",basename($file)); $c = count($e); $id = $e[($c - 2)]; if( ! isset($preserve[$id])) @unlink($this->path.$id.".cache"); } $fp = fopen($this->path.self::STATS_FILE,"w+"); fwrite($fp,""); fclose($fp); } /** * @param integer $id primary key of the DAO * @return string filename and path */ public function getFileName($id) { return $this->path.$id.".cache"; } /** * @return array an array of fetched primary keys */ public function getFetched() { return $this->fetched; } /** * fetch fetch a Doctrine_Record from the file cache * @param integer $id */ public function fetch($id) { $name = $this->getTable()->getComponentName(); $file = $this->path.$id.".cache"; if( ! file_exists($file)) throw new InvalidKeyException(); $data = file_get_contents($file); $record = unserialize($data); if( ! ($record instanceof Doctrine_Record)) { // broken file, delete silently $this->delete($id); throw new InvalidKeyException(); } $this->fetched[] = $id; return $record; } /** * exists check the existence of a cache file * @param integer $id primary key of the cached DAO * @return boolean whether or not a cache file exists */ public function exists($id) { $name = $this->getTable()->getComponentName(); $file = $this->path.$id.".cache"; return file_exists($file); } /** * deleteAll * @return void */ public function deleteAll() { foreach(glob($this->path."*.cache") as $file) { @unlink($file); } $fp = fopen($this->path.self::STATS_FILE,"w+"); fwrite($fp,""); fclose($fp); } /** * delete delete a cache file * @param integer $id primary key of the cached DAO */ public function delete($id) { $file = $this->path.$id.".cache"; if( ! file_exists($file)) return false; @unlink($file); return true; } /** * deleteMultiple delete multiple cache files * @param array $ids an array containing cache file ids * @return integer the number of files deleted */ public function deleteMultiple(array $ids) { $deleted = 0; foreach($ids as $id) { if($this->delete($id)) $deleted++; } return $deleted; } /** * destructor * the purpose of this destructor is to save all the fetched * primary keys into the cache stats */ public function __destruct() { if( ! empty($this->fetched)) { $fp = fopen($this->path.self::STATS_FILE,"a"); fwrite($fp,":".implode(":",$this->fetched)); fclose($fp); } /** * * cache auto-cleaning algorithm * $ttl is the number of page loads between each cache cleaning * the default is 100 page loads * * this means that the average number of page loads between * each cache clean is 100 page loads (= 100 constructed Doctrine_Managers) * */ $ttl = $this->objTable->getAttribute(Doctrine::ATTR_CACHE_TTL); $l1 = (mt_rand(1,$ttl) / $ttl); $l2 = (1 - 1/$ttl); if($l1 > $l2) $this->clean(); } }