From 8dcb8f268f102c7c659702bc26290b1f23548ac1 Mon Sep 17 00:00:00 2001 From: doctrine Date: Tue, 30 May 2006 08:58:43 +0000 Subject: [PATCH] Added a folder remotely --- Doctrine/Cache/File.php | 260 +++++++++++++++++++++++++++++ Doctrine/Cache/Manager.class.php | 5 + Doctrine/Cache/Memcache.class.php | 5 + Doctrine/Cache/Sqlite.php | 265 ++++++++++++++++++++++++++++++ Doctrine/Cache/stats.cache | 0 5 files changed, 535 insertions(+) create mode 100644 Doctrine/Cache/File.php create mode 100644 Doctrine/Cache/Manager.class.php create mode 100644 Doctrine/Cache/Memcache.class.php create mode 100644 Doctrine/Cache/Sqlite.php create mode 100644 Doctrine/Cache/stats.cache diff --git a/Doctrine/Cache/File.php b/Doctrine/Cache/File.php new file mode 100644 index 000000000..329b093ba --- /dev/null +++ b/Doctrine/Cache/File.php @@ -0,0 +1,260 @@ +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(); + + } +} +?> diff --git a/Doctrine/Cache/Manager.class.php b/Doctrine/Cache/Manager.class.php new file mode 100644 index 000000000..22e04db79 --- /dev/null +++ b/Doctrine/Cache/Manager.class.php @@ -0,0 +1,5 @@ + diff --git a/Doctrine/Cache/Memcache.class.php b/Doctrine/Cache/Memcache.class.php new file mode 100644 index 000000000..988d3804f --- /dev/null +++ b/Doctrine/Cache/Memcache.class.php @@ -0,0 +1,5 @@ + diff --git a/Doctrine/Cache/Sqlite.php b/Doctrine/Cache/Sqlite.php new file mode 100644 index 000000000..2d8028dd4 --- /dev/null +++ b/Doctrine/Cache/Sqlite.php @@ -0,0 +1,265 @@ +table = $table; + $dir = $this->table->getSession()->getAttribute(Doctrine::ATTR_CACHE_DIR); + + + if( ! is_dir($dir)) + mkdir($dir, 0777); + + $this->path = $dir.DIRECTORY_SEPARATOR; + $this->dbh = $this->table->getSession()->getCacheHandler(); + + try { + $this->dbh->query("CREATE TABLE ".$this->table->getTableName()." (id INTEGER UNIQUE, object TEXT)"); + } catch(PDOException $e) { + + } + /** + * create stats file + */ + if( ! file_exists($this->path.self::STATS_FILE)) + touch($this->path.self::STATS_FILE); + + } + /* + * stores a Doctrine_Record into cache + * @param Doctrine_Record $record record 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; + + $clone = clone $record; + $id = $clone->getID(); + + $stmt = $this->dbh->query(sprintf(self::INSERT,$this->table->getTableName())); + $stmt->execute(array($id, serialize($clone))); + + + return true; + } + /** + * fetches a Doctrine_Record from the cache + * @param mixed $id + * @return mixed false on failure, Doctrine_Record on success + */ + public function fetch($id) { + $stmt = $this->dbh->query(sprintf(self::SELECT,$this->table->getTableName(),"= ?")); + $stmt->execute(array($id)); + $data = $stmt->fetch(PDO::FETCH_NUM); + + if($data === false) + throw new InvalidKeyException(); + + $this->fetched[] = $id; + + $record = unserialize($data[0]); + + if(is_string($record)) { + $this->delete($id); + throw new InvalidKeyException(); + } + + return $record; + } + /** + * fetches multiple records from the cache + * @param array $keys + * @return mixed false on failure, an array of Doctrine_Record objects on success + */ + public function fetchMultiple(array $keys) { + $count = (count($keys)-1); + $keys = array_values($keys); + $sql = sprintf(self::SELECT,$this->table->getTableName(),"IN (".str_repeat("?, ",$count)."?)"); + $stmt = $this->dbh->query($sql); + + $stmt->execute($keys); + + while($data = $stmt->fetch(PDO::FETCH_NUM)) { + $array[] = unserialize($data[0]); + } + + $this->fetched = array_merge($this->fetched, $keys); + + if( ! isset($array)) + return false; + + return $array; + } + /** + * deletes all records from cache + * @return void + */ + public function deleteAll() { + $stmt = $this->dbh->query("DELETE FROM ".$this->table->getTableName()); + return $stmt->rowCount(); + } + /** + * @param mixed $id + * @return void + */ + public function delete($id) { + $stmt = $this->dbh->query(sprintf(self::DELETE,$this->table->getTableName(),"= ?")); + $stmt->execute(array($id)); + + if($stmt->rowCount() > 0) + return true; + + return false; + } + /** + * count + * @return integer + */ + public function count() { + $stmt = $this->dbh->query("SELECT COUNT(*) FROM ".$this->table->getTableName()); + $data = $stmt->fetch(PDO::FETCH_NUM); + + // table has two columns so we have to divide the count by two + return ($data[0] / 2); + } + /** + * @param array $keys + * @return integer + */ + public function deleteMultiple(array $keys) { + if(empty($keys)) + return 0; + + $keys = array_values($keys); + $count = (count($keys)-1); + $sql = sprintf(self::DELETE,$this->table->getTableName(),"IN (".str_repeat("?, ",$count)."?)"); + $stmt = $this->dbh->query($sql); + $stmt->execute($keys); + + return $stmt->rowCount(); + } + /** + * 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); + } + /** + * clean + * @return void + */ + public function clean() { + $stats = $this->getStats(); + + asort($stats); + $size = $this->table->getAttribute(Doctrine::ATTR_CACHE_SIZE); + + $count = count($stats); + + if($count <= $size) + return 0; + + $e = $count - $size; + + $keys = array(); + foreach($stats as $id => $count) { + if( ! $e--) + break; + + $keys[] = $id; + } + return $this->deleteMultiple($keys); + } + /** + * saves statistics + * @return boolean + */ + public function saveStats() { + if( ! empty($this->fetched)) { + $fp = fopen($this->path.self::STATS_FILE,"a"); + fwrite($fp,":".implode(":",$this->fetched)); + fclose($fp); + $this->fetched = array(); + return true; + } + return false; + } + /** + * autoClean + * $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) + * @return boolean + */ + public function autoClean() { + $ttl = $this->table->getAttribute(Doctrine::ATTR_CACHE_TTL); + + $l1 = (mt_rand(1,$ttl) / $ttl); + $l2 = (1 - 1/$ttl); + + if($l1 > $l2) { + $this->clean(); + return true; + } + return false; + } + /** + * @param mixed $id + */ + public function addDelete($id) { + $this->delete[] = $id; + } + /** + * destructor + * the purpose of this destructor is to save all the fetched + * primary keys into the cache stats and to clean cache if necessary + * + */ + public function __destruct() { + $this->saveStats(); + $this->autoClean(); + } +} +?> diff --git a/Doctrine/Cache/stats.cache b/Doctrine/Cache/stats.cache new file mode 100644 index 000000000..e69de29bb