Improved memcache php classes
Today I rewrote the previous mysql - memcache classes, mentioned in my previous post.
I wrote a clas with static methods (why should you need an object at all?) with logic. supporting an easy way store items in a PHP application. A second DB class extends this one, for an easy way of caching complicated queries.
/** @author Bastiaan Welmers
*
* 2009-01-13
*
* Holds memcache instance for caching stuff
*/
class Welmers_Memcache
{
/**
* memcached ID for the ID list
*/
const IDLIST = '__idlist_RANDOMCHARS';
/**
* Holds the memcache instance
*/
private static $_memcache;
/**
* Holds the application key generated bij init()
* @see init()
*/
private static $_appKey;
/**
* TTL of the objects in memcached in seconds
*
*/
public static $timeout = 1800;
/**
* Get list of IDs
*
* @return array with queries as keys and unix timestamps as value
*/
public static function getIdList()
{
return self::get(self::IDLIST);
}
/**
* Get the queries that are cached in this object
* from memcache
*
* @param string unique ID to identify object
* @return mixed|bool object in cache, or false on error
*/
public static function get($id)
{
if (!self::$_memcache instanceof Memcache)
self::init();
return self::$_memcache->get(self::$_appKey . $id);
}
/**
* Store object into memcache
*
* @param string unique ID to identify object
* @param mixed object to store
* @return bool true on succes, false on failure
*/
public static function set($id, $value, $timeout = null)
{
if ($timeout == null)
$timeout = self::$timeout;
if (!self::$_memcache instanceof Memcache)
self::init();
if ($id != self::IDLIST)
{ // save ID
$idList = self::get(self::IDLIST);
if (!is_array($idList))
$idList = array();
$idList[$id] = time() + $timeout;
self::set(self::IDLIST, $idList, $timeout + 2);
}
return self::$_memcache->set(self::$_appKey . $id, $value, 0, $timeout);
}
/**
* expires (deletes) items from memcache
*
* @param string unique ID to identify object, ommit to flush entire cache (removes every stored object)
*/
public static function expire($id = null)
{
if (!self::$_memcache instanceof Memcache)
self::init();
$idList = self::get(self::IDLIST);
if (!is_array($idList))
$idList = array();
if ($id == null)
{ // remove all IDS
foreach($idList as $expireId => $timeout)
{
self::$_memcache->delete($expireId);
}
$idList = array();
self::set(self::IDLIST, $idList, self::$timeout);
} else { //specific item
if (isset($idList[$id]))
{
self::$_memcache->delete(self::$_appKey . $id);
unset($idList[$id]);
self::set(self::IDLIST, $idList, self::$timeout);
}
}
}
/**
* Initiates memcache.
*
* @param int $timeout TTL of every stored object, if ommitted, self::$timeout will be used, 1800 seconds.
*/
public static function init($timeout = null)
{
if ($timeout != null)
self::$timeout = $timeout;
self::$_memcache = new Memcache();
self::$_memcache->connect('localhost', 11211) or die ("Could not connect to memcache");
self::$_appKey = md5(__FILE__);
}
}
Example for using this class:
function get_difficult_var()
{
if (!$difficult_var = Welmers_Memcache::get('difficult_var'))
{
// difficult logic to create difficult var
Welmers_Memcache::set('difficult_var', $difficult_var, 3600 /*timeout*/);
}
return $difficult_var
}
To remove this var, use
Welmers_Memcache::expire('difficult_var')
To expire everything inserted in this application:
Welmers_Memcache::expire()
The next class is useable for complicated MySQL queries, and replaces mysql_query.
/** @author Bastiaan Welmers
*
* 2009-01-13
*
* Cache mysql queries
*
*/
class Welmers_Memcache_Db extends Welmers_Memcache
{
/**
* IDPREFIX - salt prefix for memcached ID for queries
* should be random value
*/
const IDPREFIX = 'aasd79tpbo1234';
/**
* QUERIES_PREFIX - prefix for memcached ID storing cached queries
*/
const QUERIES_PREFIX = '__queries';
/**
* Get the queries that are cached in this object
*
* @return array with queries as keys
*/
public static function getQueries()
{
return self::get(self::IDPREFIX . self::QUERIES_PREFIX);
}
/**
* Get the queries that are cached in this object
*
* @return array with queries as keys
*/
private static function _saveQuery($sql)
{
$queries = self::get(self::IDPREFIX . self::QUERIES_PREFIX);
if (!is_array($queries))
$queries = array();
$queries[$sql] = true;
self::set(self::IDPREFIX . self::QUERIES_PREFIX, $queries, self::$timeout + 2);
}
/**
* Helper function for mysql_query and delete* functions
*
* @param string $sql SQL query
* @return string with ID for memcached for this query
*/
private static function _getQueryKey($sql)
{
return md5(self::IDPREFIX . $sql);
}
/**
* The cached version of the plain mysql_query funtion
* Returns multidimensional array with al return values.
*
* @param string $sql SQL query
* @param resource $linkIdentifier optional link identifier of the mysql connection
* @return array multidimensional array with all returned records
*/
public static function mysql_query($sql, $linkIdentifier = false)
{
if (!($cache = self::get(self::_getQueryKey($sql))))
{
$cache = array();
$r = ($linkIdentifier !== false) ? mysql_query($sql, $linkIdentifier) : mysql_query($sql);
if (is_resource($r) && (($rows = mysql_num_rows($r)) != 0))
{
for ($i=0; $i < $rows; $i++) {
$fields = mysql_num_fields($r);
$row = mysql_fetch_array($r);
for ($j = 0; $j $true)
{
if (strpos($query, $searchItem) !== false)
{
self::expire(self::_getQueryKey($query));
unset($queries[$query]);
}
}
}
self::set(self::IDPREFIX . self::QUERIES_PREFIX, $queries, self::$timeout + 2);
}
/**
* Deletes all queries that are cached with this application
* Only use this for a global flush functionality, i.e. not for
* every change of a particular table.
* (then use deleteMatching('tablename') instead)
*
*/
public function deleteAll()
{
$queries = self::get(self::IDPREFIX . self::QUERIES_PREFIX);
if (!is_array($queries))
$queries = array();
foreach($queries as $query => $true)
{
self::expire(self::_getQueryKey($query));
unset($queries[$query]);
}
self::set(self::IDPREFIX . self::QUERIES_PREFIX, $queries, self::$timeout + 2);
}
}
Example of this use would be easy:
// example query, quite complicated for mysql to calculate
$query = "SELECT
DATE_FORMAT(lb.datum, '%Y') AS year,
DATE_FORMAT(lb.datum, '%d-%m-%Y') AS datum_formatted,
DATE_FORMAT(lb2.datum, '%d-%m-%Y') AS datum_tot_formatted,
lp.lidnummer, lp.bedrijfsnaam, lp.voornaam, lp.tussenvoegsel, lp.achternaam
FROM leden_betaalmethodearchief lb
LEFT JOIN leden_persoonlijk lp ON lp.lidnummer = lb.lidnummer
LEFT JOIN leden_betaalmethodearchief lb2 ON
lb.lidnummer = lb2.lidnummer AND
lb2.datum > lb.datum AND
lb2.betaalmethode != 'acceptgiro'
WHERE lb.betaalmethode = 'acceptgiro' ORDER BY lb.datum ASC
";
$result = Welmers_Memcache_Db::mysql_query($query);
// instead of $result = mysql_query($query)
foreach($result as $row) {
// instead of while ($row = mysql_fetch_assoc($result) {
print $row['field'];
// …..
}
Have a lot of fun!
September 23rd, 2009 at 3:33 pm
# foreach($result2 as $lid) {
# // instead of while ($row = mysql_fetch_assoc($result) {
# print $row['field'];
# // …..
# }
that meant to be like $result as $row ?
September 23rd, 2009 at 4:15 pm
You are right about that, it’s fixed in the post. Thanks.
June 13th, 2010 at 12:42 am
You should edit your blog post;
if (is_resource($r) &amp;amp;&amp;amp; (($rows = mysql_num_rows($r)) != 0))
Also error on line 83
Parse error: syntax error, unexpected T_VARIABLE, expecting ‘;’ in D:\w\serv\www\_a\mc\index.php on line 83