*/ /** A cache manager in file */ class cachefile { /** Where to store the cached information */ public $directory = "./cache"; /** If TRUE : no information is cached */ public $nocache = false; /** Getter/setter for $directory cache * @param string|null $val The directory to use * @return $this or $this->directory value */ public function directory ($val = null) // {{{ { if ($val === null) return $this->directory; if (! is_string ($val) || trim ($val) === "") throw new \Exception ("CacheFile directory not a string or empty", 500); $this->directory = $val; return $this; } // }}} /** Check if there is some garbage to clean */ public function garbage () // {{{ { try { $this->cachedir (); } catch (\Exception $e) { throw new \Exception ($e->getMessage(), $e->getCode()); } $fileCache = $this->directory."/".sha1 ("Garbage-Lock"); $res = false; if (file_exists ($fileCache)) { $data = file_get_contents ($fileCache); $data = unserialize ($data); if (($data["createTime"] + $data["ttl"]) >= time ()) { $res = $data["data"]; } } if ($res === false) { $data = array ("ttl" => 24*60*60, "createTime" => time(), "data" => "CACHE-Garbage"); file_put_contents ($fileCache, serialize ($data)); chmod ($fileCache, 0666); $files = glob ($this->directory."/*", GLOB_NOSORT); foreach ($files as $fileCache) { $data = file_get_contents ($fileCache); if ($data === false) { unlink ($fileCache); continue; } $data = unserialize ($data); if (! isset ($data["ttl"]) || ! isset ($data["data"]) || ! isset ($data["createTime"])) { unlink ($fileCache); continue; } if (($data["createTime"] + $data["ttl"]) <= time ()) { unlink ($fileCache); } } } } // }}} /** This function check if the cachedir exists and create it if it is not the * case. * Check if the cache dir is writable and readable * @return true if OK * @throw Exception if an error occured */ public function cachedir () // {{{ { if (! isset ($this->directory) || $this->directory === "") throw new \Exception (dgettext ("domframework", "No cache directory defined"), 500); if (! file_exists ($this->directory)) { // Need to create the cache dir $parent = realpath (dirname ($this->directory)); if (! is_writeable (dirname ($this->directory))) throw new \Exception (sprintf (dgettext ("domframework", "Directory %s is not writable : can not create cache directory"), $parent), 500); if (!mkdir ($this->directory)) throw new \Exception (sprintf (dgettext ("domframework", "Can not create cache directory %s"), $this->directory), 500); chmod ($this->directory, 0777); } if (! is_writable ($this->directory)) throw new \Exception (sprintf (dgettext ("domframework", "Cache directory %s is not writable"), $this->directory), 500); if (! is_readable ($this->directory)) throw new \Exception (sprintf (dgettext ("domframework", "Cache directory %s is not readable"), $this->directory), 500); if (!file_exists ($this->directory."/.htaccess")) file_put_contents ($this->directory."/.htaccess", "deny from all\n"); return true; } // }}} /** This function write data in cache * @param string $id Cache identifier (add the authentication, the METHOD...) * @param mixed $data The data to save * @param integer|null $ttl The cache Time to Leave in seconds (3600s by * default) * @return true * @throw if an error occured */ public function write ($id, $data, $ttl = 3600) // {{{ { if ($this->nocache !== false) return false; try { $this->cachedir (); } catch (\Exception $e) { throw new \Exception ($e->getMessage(), $e->getCode()); } if (! is_integer ($ttl) || $ttl <= 0) throw new \Exception ("CacheFile TTL invalid : not integer or negative". " : '$ttl'", 500); if (! is_string ($id) || ! is_integer ($id)) throw new \Exception ("CacheFile write ID not a string : ".gettype ($id), 500); $this->garbage (); $fileCache = $this->directory."/".sha1 ($id); touch ($fileCache.".lock"); $data = array ("ttl" => $ttl, "createTime" => time(), "data" => $data); file_put_contents ($fileCache, serialize ($data)); unlink ($fileCache.".lock"); chmod ($fileCache, 0666); return true; } // }}} /** This function read data from cache. Return FALSE in case of empty or too * older cache * @param string $id Cache identifier (add the authentication, the METHOD) * @return false if the cache is empty or too old * @return mixed The data stored in cache */ public function read ($id) // {{{ { if ($this->nocache !== false) return false; try { $this->cachedir (); } catch (\Exception $e) { throw new \Exception ($e->getMessage(), $e->getCode()); } $this->garbage (); if (! is_string ($id) || ! is_integer ($id)) throw new \Exception ("CacheFile read ID not a string : ".gettype ($id), 500); $fileCache = $this->directory."/".sha1 ($id); if (!file_exists ($fileCache)) return false; if (!is_readable ($fileCache)) throw new \Exception (sprintf (dgettext ("domframework", "File cache %s is not readable"), $fileCache), 500); if (!is_writable ($fileCache)) throw new \Exception (sprintf (dgettext ("domframework", "File cache %s is not writable"), $fileCache), 500); // Lock : waiting the reconstruction of the cache by another process // Waiting 10s maximum to re-create the cache file $startTime = time (); while (file_exists ($fileCache.".lock") && time () < $startTime + 10) { usleep (100000); } // The lock is pending 10s (stale) : removing it to read the data quicker // next time if (file_exists ($fileCache.".lock")) unlink ($fileCache.".lock"); $data = file_get_contents ($fileCache); if ($data === false) { unlink ($fileCache); return false; } $data = unserialize ($data); if (! isset ($data["ttl"]) || ! isset ($data["data"]) || ! isset ($data["createTime"])) { unlink ($fileCache); return false; } if (($data["createTime"] + $data["ttl"]) >= time ()) { if (file_exists ($fileCache.".lock")) unlink ($fileCache.".lock"); return $data["data"]; } return false; } // }}} /** This function delete an id in cache * @param string $id Cache identifier (add the authentication, the METHOD...) * @return boolean The cache is well deleted */ public function delete ($id) // {{{ { $fileCache = $this->directory."/".sha1 ($id); if (!file_exists ($fileCache)) return false; unlink ($fileCache); return true; } // }}} }