*/ class EAcceleratorCacheFunction { /** * To cache or not to cache, that is the question. * @var boolean $_caching */ var $_caching = true; /** * Debug flag * @var boolean $_debug */ var $_debug = false; /** * Don't save to cache if result is false * @var boolean $_dontCacheWhenTheResultIsFalse */ var $_dontCacheWhenTheResultIsFalse = false; /** * Don't save to cache if result is NULL * @var boolean $_dontCacheWhenTheResultIsNull */ var $_dontCacheWhenTheResultIsNull = false; /** * Disable / Tune the automatic cleaning process. * * The automatic cleaning process destroy too old (for the given life time) * cache files when a new cache file is written. * 0 => no automatic cache cleaning * 1 => systematic cache cleaning * x (integer) > 1 => automatic cleaning randomly 1 times on x cache write * * @var int $_automaticCleaning */ var $_automaticCleaningFactor = 100; /** * Cache lifeTime in seconds. * @var int $_lifeTime */ var $_lifeTime = 1800; /** * TTL value to use for underlying eaccelerator_put(). Optimally, * this value should always be set the highest potential lifeTime * value ever used, but not so high than it risks filling up cache * limits. * @var int $_maxTTL */ var $_maxTTL = 1800; /** * Error log file for debugging output * @var string $_errorlog */ var $_errorlog = '/tmp/cache.log'; /** Total hits by this cache object. * @var int $hitCount */ var $hitCount = 0; /** Total hits by this cache object. * @var int $missCount */ var $missCount = 0; /** * Constructor. Meant to be backward-compatible with Cache_Lite's * options; this means irrelevant options are simply ignored. * * $options is an assoc. Available options are : * $options = array( * 'debug' => set debug mode, * 'caching' => enable / disable caching (boolean), * 'lifeTime' => cache lifetime in seconds (int), * 'automaticCleaningFactor' => disable / tune automatic cleaning process (int), * 'errorlog' => filename to write debugging output to, including full path, * 'dontCacheWhenTheResultIsFalse' => as it says, * 'dontCacheWhenTheResultIsNull' => as it says, * 'maxTTL' => TTL value to use for underlying eaccelerator_put() * ); * * @param array $options options */ function EAcceleratorCacheFunction($options=array()) { $availableOptions = array('caching', 'debug', 'lifeTime', 'automaticCleaningFactor', 'errorlog', 'dontCacheWhenTheResultIsFalse', 'dontCacheWhenTheResultIsNull', 'maxTTL'); foreach($options as $key => $value) { if (in_array($key, $availableOptions)) { $property = '_' . $key; $this->$property = $value; } } } /** * Removes expired items from cache. */ function clean() { $this->log("clean() called."); eaccelerator_clean(); } /** * Delete an id from cache. */ function delete($id) { eaccelerator_rm($id); } /** * Checks if a timestamp is within the current lifeTime of this cache object. * @return boolean */ function is_within_threshold($ts) { if(!$this->_lifeTime) { return false; } return time() - $ts <= $this->_lifeTime; } /** * Fetches the data for an id. * @return mixed */ function get($id) { if (! $this->_caching) { return false; } $raw = eaccelerator_get($id); if (! $raw) { return NULL; } $struct = @unserialize($raw); if(is_array($struct)) { if($this->is_within_threshold($struct['timestamp'])) { $this->log("Cache hit for $id"); $this->hitCount++; } else { $this->log("Outdated content in cache found for $id, deleting."); $this->delete($id); return NULL; } } else { $this->log("Error: unserialize() failed in call(): " . error_get_last()); $this->delete($id); return NULL; } return $struct['content']; } /** * Writes a message to log for debugging. Only does something if * debug flag is set to true in constructor. */ function log($msg) { if($this->_debug) { error_log(date("F j, Y, g:i a") . " $msg\n", 3, $this->_errorlog); } } /** * Saves a chunk of data to cache, with a given id. Takes care of * serializing for EAccelerator. * @param string $id id * @param string $data data to be stored */ function save($data, $id) { if (! $this->_caching) { return; } if ($this->_automaticCleaningFactor>0) { $rand = rand(1, $this->_automaticCleaningFactor); if ($rand==1) { $this->log('Doing autocleaning.'); $this->clean(); } } $this->delete($id); // wrap the data inside a struct with a timestamp $struct = array(); $struct['timestamp'] = time(); $struct['content'] = $data; $result = eaccelerator_put($id, serialize($struct), $this->_maxTTL); $this->log("Saved data for $id."); } /** * Calls a cacheable function or method (or not if there is already * a cache for it). Copied from Cache_Lite_Function, with slight * modifications. * * Arguments of this method are read with func_get_args. So it doesn't appear * in the function definition. Synopsis : * call('functionName', $arg1, $arg2, ...) * (arg1, arg2... are arguments of 'functionName') * * @return mixed result of the function/method */ function call() { $arguments = func_get_args(); $id = $this->_makeId($arguments); $data = $this->get($id); $hit = false; if ($data != NULL) { $output = $data['output']; $result = $data['result']; $hit = true; $this->hitCount++; } if(!$hit) { $this->log("Cache miss for $id"); $this->missCount++; ob_start(); ob_implicit_flush(false); $target = array_shift($arguments); if (is_array($target)) { // in this case, $target is for example array($obj, 'method') $object = $target[0]; $method = $target[1]; $result = call_user_func_array(array(&$object, $method), $arguments); } else { if (strstr($target, '::')) { // classname::staticMethod list($class, $method) = explode('::', $target); $result = call_user_func_array(array($class, $method), $arguments); } else if (strstr($target, '->')) { // object->method // use a stupid name ($objet_123456789 because) of problems where the object // name is the same as this var name list($object_123456789, $method) = explode('->', $target); global $$object_123456789; $result = call_user_func_array(array($$object_123456789, $method), $arguments); } else { // function $result = call_user_func_array($target, $arguments); } } $output = ob_get_contents(); ob_end_clean(); if ($this->_dontCacheWhenTheResultIsFalse) { if ((is_bool($result)) && (!($result))) { echo($output); return $result; } } if ($this->_dontCacheWhenTheResultIsNull) { if (is_null($result)) { echo($output); return $result; } } $array['output'] = $output; $array['result'] = $result; $this->save($array, $id); } echo($output); return $result; } /** * Make an id for the cache * * @var array result of func_get_args for the call() or the remove() method * @return string id */ function _makeId($arguments) { $id = serialize($arguments); // Generate a cache id $id = md5($id); return $id; } } ?>