From f80adb95d9f3ead949be2518f100ccab43cb1e39 Mon Sep 17 00:00:00 2001 From: Nikos M Date: Wed, 24 Oct 2018 14:54:19 +0300 Subject: [PATCH] v.1.0.0 --- readme.md | 36 +-- src/php/Unicache.php | 68 +++++ src/php/adapters/UnicacheApc.php | 20 ++ src/php/adapters/UnicacheApcu.php | 21 ++ src/php/adapters/UnicacheFile.php | 76 +++++ src/php/adapters/UnicacheMemcached.php | 35 +++ src/php/adapters/UnicacheMemory.php | 30 ++ src/php/adapters/UnicacheRedis.php | 41 +++ src/php/adapters/UnicacheXCache.php | 21 ++ src/php/adapters/drivers/redis.php | 323 ++++++++++++++++++++++ src/php/cache-this.php | 102 +++---- src/php/unicache-apccache.class.php | 24 -- src/php/unicache-cache.class.php | 8 - src/php/unicache-config.php | 63 ++--- src/php/unicache-factory.class.php | 36 --- src/php/unicache-filecache.class.php | 91 ------ src/php/unicache-memcachedcache.class.php | 46 --- 17 files changed, 735 insertions(+), 306 deletions(-) create mode 100644 src/php/Unicache.php create mode 100644 src/php/adapters/UnicacheApc.php create mode 100644 src/php/adapters/UnicacheApcu.php create mode 100644 src/php/adapters/UnicacheFile.php create mode 100644 src/php/adapters/UnicacheMemcached.php create mode 100644 src/php/adapters/UnicacheMemory.php create mode 100644 src/php/adapters/UnicacheRedis.php create mode 100644 src/php/adapters/UnicacheXCache.php create mode 100644 src/php/adapters/drivers/redis.php delete mode 100644 src/php/unicache-apccache.class.php delete mode 100644 src/php/unicache-cache.class.php delete mode 100644 src/php/unicache-factory.class.php delete mode 100644 src/php/unicache-filecache.class.php delete mode 100644 src/php/unicache-memcachedcache.class.php diff --git a/readme.md b/readme.md index 221d216..cc67b2d 100644 --- a/readme.md +++ b/readme.md @@ -1,11 +1,11 @@ -#UNICACHE +# UNICACHE -__An agnostic universal caching framework for PHP, Node/JS, Python__ +__An agnostic, caching framework for PHP, Node/JS, Python__ python,node implementations in progress.. -###Contents +### Contents * [How to use](#how-to-use) * [Types of Caching Supported](#types-of-caching-supported) @@ -14,8 +14,9 @@ python,node implementations in progress.. * [Notes](#notes) -###How to Use -This is a caching framework for applications that is universal and agnostic and total. +### How to Use + +This is a caching framework for applications that is agnostic and total. This means that one can use it easily in her web applications that use any given framework or not use any framework at all. @@ -26,30 +27,31 @@ The framework is configured by a config file which easily gets together all para A demo is included with the package. One simply adds an include directive and bang you have the most advanced caching. -###Types of Caching Supported +### Types of Caching Supported + * File-based caching * APC +* APCU +* XCache * Memcached -* it is very easy to extend to other methods as well (eg xCache). +* Redis +* it is very easy to extend to other methods as well. -###TODO +### TODO ^ add node/js, python implementations -* add support for Redis -* add support for xCache +* add support for Redis [DONE] +* add support for xCache [DONE] -###ChangeLog +### ChangeLog -###Notes -part of the code is based on code from: http://www.rooftopsolutions.nl/blog/107 +### Notes +Part of the code is based on code from: http://www.rooftopsolutions.nl/blog/107 -*UNICACHE* is also part of PHP classes http://www.phpclasses.org/package/7530-PHP-Cache-data-in-files-APC-or-Memcached.html +*UNICACHE* is also part of PHP classes http://www.phpclasses.org/package/7530-PHP-Cache-data-in-files-APC-or-Memcached.html -*URL* [Nikos Web Development](http://nikos-web-development.netai.net/ "Nikos Web Development") -*URL* [UNICACHE blog post](http://nikos-web-development.netai.net/blog/unicache-universal-caching-framework-for-php/ "UNICACHE blog post") -*URL* [WorkingClassCode](http://workingclasscode.uphero.com/ "Working Class Code") diff --git a/src/php/Unicache.php b/src/php/Unicache.php new file mode 100644 index 0000000..9a4896d --- /dev/null +++ b/src/php/Unicache.php @@ -0,0 +1,68 @@ +setCacheDir( $config['FILE']['cacheDir'] ); + break; + case 'APC': + require_once(dirname(__FILE__).'/adapters/UnicacheApc.php'); + $cache = new UNICACHE_APCCache(); + break; + case 'APCU': + require_once(dirname(__FILE__).'/adapters/UnicacheApcu.php'); + $cache = new UNICACHE_APCUCache(); + break; + case 'XCACHE': + require_once(dirname(__FILE__).'/adapters/UnicacheXCache.php'); + $cache = new UNICACHE_XCache(); + break; + case 'MEMCACHED': + require_once(dirname(__FILE__).'/adapters/UnicacheMemcached.php'); + $cache = new UNICACHE_MemcachedCache(); + foreach ((array)$config['MEMCACHED']['servers'] as $srv) + { + $cache->addServer( $srv['host'], $srv['port'], $srv['weight'] ); + } + break; + case 'REDIS': + require_once(dirname(__FILE__).'/adapters/UnicacheRedis.php'); + $cache = new UNICACHE_RedisCache(); + $cache->server( $config['REDIS']['server']['host'], $config['REDIS']['server']['port'] ); + break; + default: + // default in-memory cache + require_once(dirname(__FILE__).'/adapters/UnicacheMemory.php'); + $cache = new UNICACHE_MemoryCache(); + break; + } + return $cache; + } +} +} diff --git a/src/php/adapters/UnicacheApc.php b/src/php/adapters/UnicacheApc.php new file mode 100644 index 0000000..7fbbdb8 --- /dev/null +++ b/src/php/adapters/UnicacheApc.php @@ -0,0 +1,20 @@ +getFileName($key),'a+'); + if (!$ch) + throw new \Exception('UNICACHE: Could not save to cache'); + + flock($ch,LOCK_EX); // exclusive lock, will get released when the file is closed + fseek($ch,0); // go to the start of the file + // truncate the file + ftruncate($ch,0); + + // Serializing along with the TTL + $data = serialize(array(time()+(int)$ttl,$data)); + if (false === fwrite($ch,$data)) + throw new \Exception('UNICACHE: Could not save to cache'); + fclose($ch); + } + + public function get( $key ) + { + $filename = $this->getFileName($key); + if (!file_exists($filename)) return false; + $ch = fopen($filename,'r'); + + if (!$ch) return false; + + // Getting a shared lock + flock($ch,LOCK_SH); + + $data = file_get_contents($filename); + fclose($ch); + + $data = @unserialize($data); + if (!$data) + { + unlink($filename); + return false; + } + + if (time() > $data[0]) + { + // Unlinking when the file was expired + unlink($filename); + return false; + } + return $data[1]; + } + + public function remove( $key ) + { + $filename = $this->getFileName($key); + if (file_exists($filename)) + return unlink($filename); + else + return false; + } + + public function setCacheDir( $dir ) + { + $this->cachedir = rtrim((string)$dir, '/\\'); + if ( !(file_exists($this->cachedir) && is_dir($this->cachedir)) ) + @mkdir($this->cachedir); + } + + protected function getFileName( $key ) + { + return $this->cachedir . DIRECTORY_SEPARATOR . md5($key); + } +} diff --git a/src/php/adapters/UnicacheMemcached.php b/src/php/adapters/UnicacheMemcached.php new file mode 100644 index 0000000..0cc46aa --- /dev/null +++ b/src/php/adapters/UnicacheMemcached.php @@ -0,0 +1,35 @@ +connection = new MemCache( ); + } + + public function put( $key, $data, $ttl ) + { + return $this->connection->set( $key, $data, 0, $ttl); + } + + public function get( $key ) + { + return $this->connection->get( $key ); + } + + public function remove( $key ) + { + return $this->connection->delete( $key ); + } + + public function addServer( $host, $port=11211, $weight=10 ) + { + $this->connection->addServer( $host, $port, true, $weight ); + return $this; + } + +} diff --git a/src/php/adapters/UnicacheMemory.php b/src/php/adapters/UnicacheMemory.php new file mode 100644 index 0000000..4a869af --- /dev/null +++ b/src/php/adapters/UnicacheMemory.php @@ -0,0 +1,30 @@ +_cache[$key] = array(time()+(int)$ttl,$data); + } + + public function get( $key ) + { + + if ( !isset($this->_cache[$key]) ) return false; + + $data = $this->_cache[$key]; + if ( !$data ) return false; + + if ( time() > $data[0] ) return false; + return $data[1]; + } + + public function remove( $key ) + { + if ( !isset($this->_cache[$key]) ) return false; + unset($this->_cache[$key]); + return true; + } +} diff --git a/src/php/adapters/UnicacheRedis.php b/src/php/adapters/UnicacheRedis.php new file mode 100644 index 0000000..f27c218 --- /dev/null +++ b/src/php/adapters/UnicacheRedis.php @@ -0,0 +1,41 @@ +redis->cmd('SET', $key, $data)->cmd('EXPIRE', $key, (int)$ttl)->set(); + } + + public function get( $key ) + { + $data = $this->redis->cmd('GET', $key)->get(); + if ( !$data ) return false; + $data = @unserialize($data); + if ( !$data ) return false; + if ( time() > $data[0] ) + { + $this->redis->cmd('DEL', $key)->set(); + return false; + } + return $data[1]; + } + + public function remove( $key ) + { + return $this->redis->cmd('DEL', $key)->set(); + } + + public function server( $host, $port=6379 ) + { + $this->redis = new redis_cli( $host, $port ); + return $this; + } + +} diff --git a/src/php/adapters/UnicacheXCache.php b/src/php/adapters/UnicacheXCache.php new file mode 100644 index 0000000..e81ea75 --- /dev/null +++ b/src/php/adapters/UnicacheXCache.php @@ -0,0 +1,21 @@ +connect($host, $port, $silent_fail, $timeout); + } + } + + //Main method to establish connection + public function connect($host = '127.0.0.1', $port = 6379, $silent_fail = false, $timeout = 60) + { + $this->host = $host; + $this->port = $port; + $this->silent_fail = $silent_fail; + $this->timeout = $timeout; + + if ($silent_fail) { + $this->handle = @fsockopen($host, $port, $errno, $errstr, $this->connect_timeout); + + if (!$this->handle) { + $this->handle = false; + } + } else { + $this->handle = fsockopen($host, $port, $errno, $errstr, $this->connect_timeout); + } + + if (is_resource($this->handle)) { + stream_set_timeout($this->handle, $this->timeout); + } + } + + public function reconnect() + { + $this->__destruct(); + $this->connect($this->host, $this->port, $this->silent_fail); + } + + public function __destruct() + { + if (is_resource($this->handle)) { + fclose($this->handle); + } + } + + //Returns all commands array + public function commands() + { + return $this->commands; + } + + //Used to push single command to queue + public function cmd() + { + if (!$this->handle) { + return $this; + } + + $args = func_get_args(); + $rlen = count($args); + + $output = '*'. $rlen . self::NL; + + foreach ($args as $arg) { + $output .= '$'. strlen($arg) . self::NL . $arg . self::NL; + } + + $this->commands[] = $output; + + return $this; + } + + //Used to push many commands at once, almost always for setting something + public function set() + { + if (!$this->handle) { + return false; + } + + //Total size of commands + $size = $this->exec(); + $response = array(); + + for ($i=0; $i<$size; $i++) { + $response[] = $this->get_response(); + } + + if ($this->force_reconnect) { + $this->reconnect(); + } + + return $response; + } + + //Used to get command response + public function get($line = false) + { + if (!$this->handle) { + return false; + } + + $return = false; + + if ($this->exec()) { + $return = $this->get_response(); + + if ($this->force_reconnect) { + $this->reconnect(); + } + } + + return $return; + } + + //Used to get length of the returned array. Most useful with `Keys` command + public function get_len() + { + if (!$this->handle) { + return false; + } + + $return = null; + + if ($this->exec()) { + $char = fgetc($this->handle); + + if ($char == self::BULK) { + $return = sizeof($this->bulk_response()); + } elseif ($char == self::MULTIBULK) { + $return = sizeof($this->multibulk_response()); + } + + if ($this->force_reconnect) { + $this->reconnect(); + } + } + + return $return; + } + + //Forces to reconnect after every get() or set(). Use this with extreme caution + public function set_force_reconnect($flag) + { + $this->force_reconnect = $flag; + return $this; + } + + //Used to parse single command single response + private function get_response() + { + $return = false; + + $char = fgetc($this->handle); + + switch ($char) { + case self::INLINE: + $return = $this->inline_response(); + break; + case self::INTEGER: + $return = $this->integer_response(); + break; + case self::BULK: + $return = $this->bulk_response(); + break; + case self::MULTIBULK: + $return = $this->multibulk_response(); + break; + case self::ERROR: + $return = $this->error_response(); + break; + } + + return $return; + } + + //For inline responses only + private function inline_response() + { + return trim(fgets($this->handle)); + } + + //For integer responses only + private function integer_response() + { + return ( int ) trim(fgets($this->handle)); + } + + //For error responses only + private function error_response() + { + $error = fgets($this->handle); + + if ($this->error_function) { + call_user_func($this->error_function, $error .'('. $this->last_used_command .')'); + } + + return false; + } + + //For bulk responses only + private function bulk_response() + { + $return = trim(fgets($this->handle)); + + if ($return === '-1') { + $return = null; + } else { + $return = $this->read_bulk_response($return); + } + + return $return; + } + + //For multibulk responses only + private function multibulk_response() + { + $size = trim(fgets($this->handle)); + $return = false; + + if ($size === '-1') { + $return = null; + } else { + $return = array(); + + for ($i = 0; $i < $size; $i++) { + $return[] = $this->get_response(); + } + } + + return $return; + } + + //Sends command to the redis + private function exec() + { + $size = sizeof($this->commands); + + if ($size < 1) { + return null; + } + + if ($this->error_function) { + $this->last_used_command = str_replace(self::NL, '\\r\\n', implode(';', $this->commands)); + } + + $command = implode(self::NL, $this->commands) . self::NL; + fwrite($this->handle, $command); + + $this->commands = array(); + return $size; + } + + //Bulk response reader + private function read_bulk_response($tmp) + { + $response = null; + + $read = 0; + $size = ((strlen($tmp) > 1 && substr($tmp, 0, 1) === self::BULK) ? substr($tmp, 1) : $tmp); + + while ($read < $size) { + $diff = $size - $read; + + $block_size = $diff > 8192 ? 8192 : $diff; + + $chunk = fread($this->handle, $block_size); + + if ($chunk !== false) { + $chunkLen = strlen($chunk); + $read += $chunkLen; + $response .= $chunk; + } else { + fseek($this->handle, $read); + } + } + + fgets($this->handle); + + return $response; + } + + public function set_error_function($func) + { + $this->error_function = $func; + } +} diff --git a/src/php/cache-this.php b/src/php/cache-this.php index acbd3f0..dfaceb3 100644 --- a/src/php/cache-this.php +++ b/src/php/cache-this.php @@ -1,51 +1,51 @@ -put($key,$webpage,$ttl); - - ob_end_clean(); - // show it first time - echo $webpage; - } - - // Is cache data still fresh? If so, serve it. - $data=$cache->get($key); - if ($data!=false) { - echo $data; - exit(0); - } - // start caching - ob_start(); - ob_implicit_flush(false); - register_shutdown_function("UNICACHE_cacheOutput"); -} -?> \ No newline at end of file +put($key,$webpage,$ttl); + + ob_end_clean(); + // show it first time + echo $webpage; + } + + // Is cache data still fresh? If so, serve it. + $data=$cache->get($key); + if ($data!=false) { + echo $data; + exit(0); + } + // start caching + ob_start(); + ob_implicit_flush(false); + register_shutdown_function("UNICACHE_cacheOutput"); +} diff --git a/src/php/unicache-apccache.class.php b/src/php/unicache-apccache.class.php deleted file mode 100644 index 0473620..0000000 --- a/src/php/unicache-apccache.class.php +++ /dev/null @@ -1,24 +0,0 @@ - \ No newline at end of file diff --git a/src/php/unicache-cache.class.php b/src/php/unicache-cache.class.php deleted file mode 100644 index 8392835..0000000 --- a/src/php/unicache-cache.class.php +++ /dev/null @@ -1,8 +0,0 @@ - \ No newline at end of file diff --git a/src/php/unicache-config.php b/src/php/unicache-config.php index 525771a..708f150 100644 --- a/src/php/unicache-config.php +++ b/src/php/unicache-config.php @@ -1,33 +1,30 @@ - 60, // one minute cache time to live - 'cacheType' => 'FILE', // file based cache - 'FILE' => array( - 'cacheDir'=>'c:\xampp\htdocs\unicache\cache' // the cache directory - ), - 'MEMCACHED' => array( - 'servers'=>array( - array('host'=>'localhost','port'=>11211,'weight'=>10) // memcached servers - ) - ), - 'filter_func'=>'my_filter', // user defined post cache filter function - 'cache_func'=>'my_cache' // user defined function to disable caching -); - -// require the cache classes -require_once('unicache-factory.class.php'); - -// filter content after caching -function my_filter($content) -{ - return $content.'
FILE '.date('y/m/d, H:i:s', time()); -} -// disable caching under certain conditions -function my_cache() -{ - if (isset($_GET['foo'])) - return false; - return true; -} -?> \ No newline at end of file + 60, // one minute cache time to live + 'cacheType' => 'FILE', // file based cache + 'FILE' => array( + 'cacheDir'=>dirname(__FILE__).'/cache' // the cache directory + ), + 'MEMCACHED' => array( + 'servers'=>array( + array('host'=>'localhost','port'=>11211,'weight'=>10) // memcached servers + ) + ), + 'REDIS' => array( + 'server'=>array('host'=>'127.0.0.1','port'=>6379) // redis server + ), + 'filter_func' => 'my_filter', // user defined post cache filter function + 'cache_func' => 'my_cache' // user defined function to disable caching +); + +// filter content after caching +function my_filter($content) +{ + return $content.'
FILE '.date('y/m/d, H:i:s', time()); +} +// disable caching under certain conditions +function my_cache() +{ + return isset($_GET['foo']) ? false : true; +} diff --git a/src/php/unicache-factory.class.php b/src/php/unicache-factory.class.php deleted file mode 100644 index 9cb8e2e..0000000 --- a/src/php/unicache-factory.class.php +++ /dev/null @@ -1,36 +0,0 @@ -setCacheDir($uniconf['FILE']['cacheDir']); - break; - case 'APC': - require_once 'unicache-apccache.class.php'; - $cache=new UNICACHE_APCCache(); - break; - case 'MEMCACHED': - require_once 'unicache-memcachedcache.class.php'; - $cache=new UNICACHE_MemcachedCache(); - foreach ($uniconf['MEMCACHED']['servers'] as $srv) - { - $cache->addServer($srv['host'],$srv['port'],$srv['weight']); - } - break; - } - return ($cache); - } -} -?> \ No newline at end of file diff --git a/src/php/unicache-filecache.class.php b/src/php/unicache-filecache.class.php deleted file mode 100644 index 67f7b66..0000000 --- a/src/php/unicache-filecache.class.php +++ /dev/null @@ -1,91 +0,0 @@ -cachedir=dirname(__FILE__).DIRECTORY_SEPARATOR.'cache'; - //clearstatcache(); - //if (!(file_exists($this->cachedir) && is_dir($this->cachedir))) - //@mkdir($this->cachedir); - } - - public function put($key,$data,$ttl) { - - // Opening the file in read/write mode - $ch = fopen($this->getFileName($key),'a+'); - if (!$ch) throw new Exception('UNICACHE: Could not save to cache'); - - flock($ch,LOCK_EX); // exclusive lock, will get released when the file is closed - fseek($ch,0); // go to the start of the file - // truncate the file - ftruncate($ch,0); - - // Serializing along with the TTL - $data = serialize(array(time()+$ttl,$data)); - if (fwrite($ch,$data)===false) { - throw new Exception('UNICACHE: Could not save to cache'); - } - fclose($ch); - - } - - public function get($key) { - - $filename = $this->getFileName($key); - if (!file_exists($filename)) return false; - $ch = fopen($filename,'r'); - - if (!$ch) return false; - - // Getting a shared lock - flock($ch,LOCK_SH); - - $data = file_get_contents($filename); - fclose($ch); - - $data = @unserialize($data); - if (!$data) { - unlink($filename); - return false; - } - - if (time() > $data[0]) { - - // Unlinking when the file was expired - unlink($filename); - return false; - - } - return $data[1]; - } - - public function remove( $key ) { - - $filename = $this->getFileName($key); - if (file_exists($filename)) { - return unlink($filename); - } else { - return false; - } - - } - - public function setCacheDir($dir) - { - $this->cachedir=$dir; - if (!(file_exists($this->cachedir) && is_dir($this->cachedir))) - @mkdir($this->cachedir); - } - - protected function getFileName($key) { - - return $this->cachedir .DIRECTORY_SEPARATOR. md5($key); - - } -} -?> \ No newline at end of file diff --git a/src/php/unicache-memcachedcache.class.php b/src/php/unicache-memcachedcache.class.php deleted file mode 100644 index 83f99a4..0000000 --- a/src/php/unicache-memcachedcache.class.php +++ /dev/null @@ -1,46 +0,0 @@ -connection = new MemCache(); - - } - - public function UNICACHE_MemcachedCache() - { - $this->__construct(); - } - - public function put($key, $data, $ttl) { - - return $this->connection->set($key,$data,0,$ttl); - - } - - public function get($key) { - - return $this->connection->get($key); - - } - - public function remove($key) { - - return $this->connection->delete($key); - - } - - public function addServer($host,$port = 11211, $weight = 10) { - - $this->connection->addServer($host,$port,true,$weight); - - } - -} -?> \ No newline at end of file