PHP Memcached Wrapper
An easy-to-use Memcached wrapper in PHP with advanced logic to ensure valid data is always returned. If it is not possible to retrieve new data, the previously cached result will be returned. Part of the valhalla-core-utils-lib (see demo link).
Check it out here: Demo Link
<?php /** * Memcached Class * Wrapper for the Memcached library. * * @author ynori7 * @copyright Copyright (c) 2014, halls-of-valhalla.org * @license http://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0 International License. * * Example Usage: use Valhalla\CoreUtilities\Cache\Memcached; Class Test{ public function getData(array $parameters = array()){ return array( 'a' => 'blah', 'b' => 'werwer', 'c' => 'blorp', ); } public function testCall(){ $serverConfig = array( 'servers' => array( array('localhost', 11211), ), 'default_ttl' => 600, 'max_age' => 604800, ); $c = new Memcached($serverConfig); var_dump($c->getDataWithCaching($this, 'getData')); } } * */ namespace Valhalla\CoreUtilities\Cache; use \Memcached as MemcachedBase; class Memcached { const PROJECT_CACHE_PREFIX = ''; private $_cache; private $_serverConfig; private $_defaultTtl; private $_maxAge; /** * The cache config should appear as follows: * 'servers' => array( * array('memcached.server1', 11219), * array('memcached.server2', 11219), * ), * 'default_ttl' => 600, * 'max_age' => 604800, * * default_ttl is the default time which cache should remain valid. * When the cache is invalid, new data should be retrieved. If it is not * possible to retrieve new data, the previously cached result will be returned * until that cached result has reached max_age at which point the entry will * disappear. * @param array $cacheConfig */ public function __construct(array $cacheConfig){ $this->_serverConfig = $cacheConfig['servers']; $this->_cache = new MemcachedBase(); $this->_cache->addServers($this->_serverConfig); $this->_defaultTtl = $cacheConfig['default_ttl']; $this->_maxAge = $cacheConfig['max_age']; } /** * Generates an id for inserting into cache. * * @param $classname * @param array $parameters * @return string */ public function createCacheKey($classname, array $parameters = array()){ return hash('crc32', self::PROJECT_CACHE_PREFIX . '_' . $classname . '_' . implode('_', $parameters)); } /** * Check if an entry exists in cache with a given id. * * @param $id * @return bool */ public function contains($id) { return (false !== $this->_cache->get($id)); } /** * Replace an existing cache entry. * * @param $id * @param $payload * @param int $lifeTime * @return bool */ public function replace($id, $payload, $lifeTime = 0) { if($lifeTime == 0) { $lifeTime = $this->_defaultTtl; } $data = array( 'timestamp' => gmdate('U'), 'ttl' => $lifeTime, 'payload' => $payload, ); return $this->_cache->replace($id, serialize($data), $this->_maxAge); } /** * Create a new cache entry. * * @param $id * @param $payload * @param int $lifeTime * @return bool */ public function save($id, $payload, $lifeTime = 0) { if($lifeTime == 0) { $lifeTime = $this->_defaultTtl; } $data = array( 'timestamp' => gmdate('U'), 'ttl' => $lifeTime, 'payload' => $payload, ); return $this->_cache->set($id, serialize($data), $this->_maxAge); } /** * Get a cached entry from cache. * * @param $id * @return mixed */ public function fetch($id){ return unserialize($this->_cache->get($id)); } /** * Checks if the data retrieved from cache is still within its time-to-live. * * @param $cachedData * @return bool */ public function isStillValid($cachedData){ return (!empty($cachedData) and (($cachedData['timestamp'] + $cachedData['ttl']) > gmdate('U'))); } /** * Wrapper function for handling all the caching around data-retrieval * * @param $class The class which is requesting data * @param $function The function defined in $class for retreiving the desired data * @param array $parameters Arguments to $class->$function * @param int $ttl The user-defined lifetime for the cache * @return mixed */ public function getDataWithCaching($class, $function, array $parameters = array(), $ttl = 0){ $cacheId = $this->createCacheKey(get_class($class), $parameters); $cachedData = $this->contains($cacheId) ? $this->fetch($cacheId) : array(); //If there's a cached response and it's still alive, return it if($this->isStillValid($cachedData)){ return $cachedData['payload']; } else { try{ $newData = $class->{$function}($parameters); if(empty($newData)){ //Return the last cached response if this data is invalid return $cachedData['payload']; } if(!empty($cachedData)){ //Replace existing cache entry with updated data $this->replace($cacheId, $newData, $ttl); } else { //Insert new entry into cache $this->save($cacheId, $newData, $ttl); } return $newData; } catch(\Exception $e){ //If there was an error retrieving data, return previous cached data return $cachedData['payload']; } } } }

This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
Download this code in plain text format here