Location: PHPKode > scripts > PHP MySQL Caching Wrapper > phpMysqlCachingWrapper.class.php
<?php
/**
  * License: GNU LGPL (http://www.opensource.org/licenses/lgpl-license.html)
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  * or download it here http://www.opensource.org/licenses/lgpl-license.html
  *
  * @project: phpMysqlCachingWrapper
  * @date: 04-12-2010
  * @version: 0.2 - php 4 / 5
  * @author Bilge Hauser
  * @email: hide@address.com
  * @copyright: 2010 Bilge Hauser
  *  
  *  
  *  history:[
  *     08.12.2010 - Version 0.1 finished
  *     12.12.2010 - Implemented Class Registry
  *     31.12.2010 - Implemented Event Handling -> v 0.2
  *  ]
  **/
 
 // EVENTS that can be handled
 
 // CLASS_WRAPPER_DESTRUCT
 // CLASS_WRAPPER_QUERY_ERROR
 // CLASS_WRAPPER_CONNECT_ERROR
 // CLASS_WRAPPER_DBSELECT_ERROR
 // CLASS_WRAPPER_QUERY_BEFORE
 // CLASS_WRAPPER_QUERY_AFTER
 // CACHE_WRITE_BEFORE
 // CACHE_WRITE_AFTER
 // CACHE_READ_BEFORE
 // CACHE_READ_AFTER
 
// Register event handlers like
// $onDBQueryError = "mail('hide@address.com', \$_SERVER['SERVER_NAME'].': Database query error', 'Warning: Database queries had an error on '.\$_SERVER['SERVER_NAME']);";
// $dbh->register_event('CLASS_WRAPPER_QUERY_ERROR',  $onDBQueryError);

// Or to access class functions
// $onBeforeCacheRead = "\$self->setMessageQue('Injected Text Before Cache Read');";
// $dbh->register_event('CACHE_READ_BEFORE', $onBeforeCacheRead);

// php < 5.2 needs json class 
// i will use json format to cache db queries in files.
// http://pear.php.net/pepr/pepr-proposal-show.php?id=198

if (!function_exists('json_encode')) {
	require_once($SITE_CONF['CLASS_PATH']   . 'json/json.class.php');
	$json = new Services_JSON();
	function json_encode($input = false) {
		return $json->encode($input);
	}
	
	function json_decode($input = false) {
		return $json->decode($input);
	}
}


class wrapper_registry{
	var $events						= array();
	
	// Register event for action
	function register_event($event=false, $function=false){
		if(!$event)return;
		if(!$function)return;
		// can handle multiple actions
		$this->events[$event]['actions'][] = create_function ('&$self', $function);
		return true;
	}
	// unregister event
	function unregister_event($event=false){
		if(!$event)return;
		if(!array_key_exists($event, $this->events))return;
		unset($this->events[$event]);
        return true;
	}
	
	function call_event($event=false){
		if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' - CALL EVENT:'.$event);
		if(!$event)return;
		// check if event exist
		if(!isset($this->events[$event])) return;
		//check if any actions registered
		if(!isset($this->events[$event]['actions'])) return;
		if(count($this->events[$event]['actions']) < 1) return;

		foreach($this->events[$event]['actions'] as $action){
			if(function_exists($action)){
				if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' - Registered Function:'.$action.' found');
			    $action(&$this);	
				if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' - Registered Function:'.$action.' executed');
			}else{
				if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' - Registered Function:'.$action.' not found');
			}
		}
		return true;
	}
	
}

 
class cache_controller extends wrapper_registry{
	var $connected 					= false;
	var $cache_dir    				= false;
	var $cache_ttl    				= 120;
	var $cache_id	  				= false;
	var $cache_suffix 				= 'cache';
	var $cache_content				= array();
	var $debug  		 			= false;
	var $messageQue             	= array();
    var $use_cache_controller		= false;


	function is_inDebugMode(){
		return $this->debug;
	}

	function getMessageQue(){
		return $this->messageQue;
	}
	
	function setMessageQue($message){
		$this->messageQue[] = $message;
	}
	
	function read_cache($query=false){
		if (!$query) return false;
		if (file_exists($this->cache_dir.$this->cache_id.'.'.$this->cache_suffix)){
		 	$this->get_resultFromCache();
			$this->cache_id=false;
			return $this->cache_content;	
		}else{
			return false;
		}
		
	}
  	
	function is_cacheValid($query=false){
  		if (!$query) return false;
		if (!$this->cache_id) $this->generateCacheId($query);
		if (!$this->cache_id) return false;
		$fileAge = $this->getCacheAge();
		
		if ($fileAge){
			if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' - Cache ID:'.$this->cache_id.' - CACHE FOUND');
			// 
			if ($fileAge < $this->cache_ttl){ 
				if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' - Cache ID:'.$this->cache_id.' - CACHE VALID');
				return true;
		    }else if($fileAge && !$this->connected){
				if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' - Cache ID:'.$this->cache_id.' - CACHE VALID DUE DATABASE DISCONNECTED');
				return true;
			}else{
				if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' - Cache ID:'.$this->cache_id.' - CACHE NOT VALID');
				return false;
			}
		}else{
			if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' - Cache ID:'.$this->cache_id.' - ID NOT FOUND');
			return false;
		}
		
  	}
  
  
	
	function generateCacheId($query=''){
		if (!$this->use_cache_controller && !$this->cache_dir) return false;
		if(stristr($query, 'SELECT') === FALSE){
			$this->cache_id = false;
     		if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' - Cache ID:'.$this->cache_id.' - NOT GENERATED ID, NO SELECT');
		}else{
			$this->cache_id = md5($query);
     		if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' - Cache ID:'.$this->cache_id.' - GENERATED ID');
		}
		 
	}
	// get cache file age in seconds
    function getCacheAge(){
		if (file_exists($this->cache_dir.$this->cache_id.'.'.$this->cache_suffix) && is_file($this->cache_dir.$this->cache_id.'.'.$this->cache_suffix)){
			// get file date
			$FileCreationTime = filemtime($this->cache_dir.$this->cache_id.'.'.$this->cache_suffix);
			// Calculate file age in seconds
			$FileAge = time() - $FileCreationTime;
			if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' - Cache ID:'.$this->cache_id.' - GET FILE AGE : '.$FileAge.' seconds');
			return $FileAge;
		}else{
			 return false;
		}
    }

    function is_cachable($query){
    	// check for cache id
    	if (!$this->cache_id) return false;
		// Only selects are cachable 		
		if(stristr($query, 'SELECT') === FALSE)  return false;
		$fileAge = $this->getCacheAge();
		// no file so cache anyway
		if (!$fileAge) return true;
		// if cache file and file is older then ttl cache it
		if ($fileAge > $this->cache_ttl)return true;
		// else cache is valid
		return false;
    }
	
	function write_cache($query=false, $result=false){
		if (!$query && !$result) return false;
		if (!$this->cache_id) return false;
		if ($this->is_cachable($query)){
			$this->cache_content['id'] = $this->cache_id;
			$this->cache_content['ttl'] = $this->cache_ttl;
			$this->cache_content['timestamp'] = time();
			$this->cache_content['data'] = $result;
		 	$this->put_resultToCache();
			$this->cache_id=false;
			return true;	
		}else{
			return false;
		}
		
	}

	function put_resultToCache(){
		if ($this->cache_id){
		if (file_exists($this->cache_dir.$this->cache_id.'.'.$this->cache_suffix) && is_file($this->cache_dir.$this->cache_id.'.'.$this->cache_suffix)){
			if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' - Cache ID:'.$this->cache_id.' - DELETE');
			unlink($this->cache_dir.$this->cache_id.'.'.$this->cache_suffix);
		}
		if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' - Cache ID:'.$this->cache_id.' - WRITE');
		$this->call_event('CACHE_WRITE_BEFORE');
		file_put_contents($this->cache_dir.$this->cache_id.'.'.$this->cache_suffix, json_encode($this->cache_content));
		$this->call_event('CACHE_WRITE_AFTER');
		}
	}
	
	function get_resultFromCache(){
		if ($this->cache_id){
			if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' - Cache ID:'.$this->cache_id.' - READ');
			$this->call_event('CACHE_READ_BEFORE');
			$this->cache_content = json_decode(file_get_contents($this->cache_dir.$this->cache_id.'.'.$this->cache_suffix), true);
			$this->call_event('CACHE_READ_AFTER');
		}
	}

} // End class cache_controller

// Events:
// CLASS_WRAPPER_DESTRUCT
// CLASS_WRAPPER_QUERY_ERROR
// CLASS_WRAPPER_CONNECT_ERROR
// CLASS_WRAPPER_DBSELECT_ERROR
// CLASS_WRAPPER_QUERY_BEFORE
// CLASS_WRAPPER_QUERY_AFTER
 

class mysql_wrapper extends cache_controller{
	var $mysql_user   				= false;
	var $mysql_pass  				= false;
	var $mysql_host  				= false;
	var $mysql_db	 				= false;
	var $mysql_persistent  			= false;
	var $mysql_socket   			= false;
	var $mysql_use_socket   		= false;
	var $result	 					= array();
	var $last_result				= array();
	var $num_rows					= false;
	var $last_num_rows				= false;
    var $db_connection   			= false;
	var $db_error  		 			= false;
	var $log_path 					= false;
	var $last_query					= array();
	
    // constructor
	function __construct($SITE_CONF) {
		$this->mysql_user   		= $SITE_CONF['DB_USER'];
		$this->mysql_pass   		= $SITE_CONF['DB_PASS'];
		$this->mysql_host   		= $SITE_CONF['DB_HOST'];
		$this->mysql_db	  			= $SITE_CONF['DB_DATABASE'];
		$this->mysql_persistent 	= $SITE_CONF['DB_PERSISTENT'];
		$this->mysql_socket   		= $SITE_CONF['DB_SOCKET_PATH'];
		$this->mysql_use_socket 	= $SITE_CONF['DB_USE_SOCKET'] ;
	    $this->cache_dir 			= $SITE_CONF['DB_CACHE_PATH'];
		$this->cache_ttl 			= $SITE_CONF['DB_CACHE_TTL']; 
		$this->debug				= $SITE_CONF['DEBUG'] ;
		$this->use_cache_controller	= $SITE_CONF['DB_USE_CACHE']; 
		$this->log_path 			= $SITE_CONF['DEBUG_LOGPATH'];
		// Connect
		if (!$this->connected && $this->mysql_persistent && $this->mysql_use_socket) $this->_socketPconnect(); 
		if (!$this->connected && !$this->mysql_persistent && $this->mysql_use_socket) $this->_socketConnect(); 
		if (!$this->connected && !$this->mysql_persistent && !$this->mysql_use_socket) $this->_connect(); 
		if (!$this->connected && $this->mysql_persistent && !$this->mysql_use_socket) $this->_pconnect(); 
        // select db
		$sel = false;
		if ($this->connected && $this->db_connection) $sel = @mysql_select_db($this->mysql_db, $this->db_connection);
		if ($this->connected && $this->db_connection &&!$sel) $this->_setError(2, 'Mysql selecting DB '.$this->mysql_db.' Failed.');
		if (!$this->db_connection) $this->connected = false;
  	}
    
	//destructor
  	function __destruct(){
		if (!$this->mysql_persistent && $this->db_connection) mysql_close($this->db_connection) || $this->_setError(4, 'Mysql Disconnect Error : '.mysql_error());
		if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' - - WRITE LOG: '.$this->log_path);
		$this->_writeLog();
		// Call Event handler
        $this->call_event('CLASS_WRAPPER_DESTRUCT');
	}	
	// Constructor php 4
    function mysql_wrapper($SITE_CONF){
    	register_shutdown_function(array(&$this, "__destruct"));
    	return $this->__construct($SITE_CONF); // forward php4 to __construct
    }
	
	# retrieve one row
	function get_row($query='',$counter=0){
		if ($query){
			$this->query($query);
		}
		$this->cache_id = false;
		if (count($this->result) > 0){
		   return $this->result[$counter];	
		}else{
		   return false;
		}
		
	}

	# retrieve multiple rows
	function get_rows($query=null, $counter=0){
		if ($query){
			$this->query($query);
		}
		$this->cache_id = false;
	    return $this->result;
	}
	
	# retrieve num rows
	function get_num_rows(){
	    return count($this->result);
	}

	# retrieve last result
	function get_last_result(){
	    return $this->last_result;
	}


	// query
	function query($query){
		// Save query
		$this->last_query = $query;
      	// lookup cache
        $is_cached = $this->is_cacheValid($query);
		// if not connected  and not cached return empty array
		if (!$this->connected && !$is_cached) return array();
		if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' -  - SQL QUERY: '. $query);
		// if not cached query database 
		if(!$is_cached) $this->call_event('CLASS_WRAPPER_QUERY_BEFORE');
		if(!$is_cached) $result = @mysql_query($query,$this->db_connection);
		if(!$is_cached) $this->call_event('CLASS_WRAPPER_QUERY_AFTER');
        // is in cache
		if($is_cached){
			// read cache
        	$this->get_resultFromCache();
		    // save last result	
			if($this->result)$this->last_result = $this->result;
			//get Result
			if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' -  - QUERY RESULT FROM CACHE');
        	$this->result =  $this->cache_content['data'];
			$this->num_rows = count($this->result);		
   			$result = false;
		}
		
		if ($result ){
		    // save last result	
			if($this->result)$this->last_result = $this->result;
			$this->result=array();	
			$i=0;
			// convert result to array, cause cached results are arrays too
			// so we dont have to care about arrays or result instances 
			while(($this->result[] = @mysql_fetch_assoc($result)) || array_pop($this->result));	
			// write to cache
			$this->write_cache($query, $this->result);
			// count result
			$this->num_rows = count($this->result);		
			if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' -  - QUERY RESULT: '. $this->num_rows);
			mysql_free_result($result);
			if($this->num_rows){
				return true;
			}else{
				return false;
			}
		}else{
			return false;
		}
	}
	
	
	// return last error
	function getError(){
		return $this->db_error;
	}


	// return connected
	function is_connected(){
		return $this->connected;
	}

	// private
	// write / append log to logdir
    function _writeLog(){
    	if($this->is_inDebugMode()){
    		file_put_contents($this->log_path, "\n".implode("\n", $this->getMessageQue()), FILE_APPEND);
		}
    }
    // normal connect (tcp)
    function _connect(){
    	$this->db_connection = @mysql_connect($this->mysql_host, $this->mysql_user, $this->mysql_pass); 
		if (!$this->db_connection) $this->_setError(1, mysql_error());
		if (!$this->db_error) $this->_setConnected();
    }

    // persistent connect (tcp)
    function _pconnect(){
    	$this->db_connection = @mysql_pconnect($this->mysql_host, $this->mysql_user, $this->mysql_pass);
		if (!$this->db_connection) $this->_setError(1, mysql_error());
		if (!$this->db_error) $this->_setConnected();
    }

    // normal connect (socket)
    function _socketConnect(){
    	$this->db_connection = @mysql_connect('localhost:'.$this->$mysql_socket, $this->mysql_user, $this->mysql_pass);
		if (!$this->db_connection) $this->_setError(1, mysql_error());
		if (!$this->db_error) $this->_setConnected();
    }

    // persistent connect (socket)
    function _socketPconnect(){
    	$this->db_connection = @mysql_pconnect('localhost:'.$this->$mysql_socket, $this->mysql_user, $this->mysql_pass);
		if (!$this->db_connection) $this->_setError(1, mysql_error());
		if (!$this->db_error) $this->_setConnected();
    }
	
	function _setError($type=1, $message){
		if ($type == 1){ // not connected
			$this->connected = false;
			$this->db_connection = false;
			// Call Event handler
    	    $this->call_event('CLASS_WRAPPER_CONNECT_ERROR');


		}else if ($type == 2){ // db select error
			// Call Event handler
    	    $this->call_event('CLASS_WRAPPER_DBSELECT_ERROR');

		}else if ($type == 3){ // db query error
			// Call Event handler
    	    $this->call_event('CLASS_WRAPPER_QUERY_ERROR');

		}else if ($type == 4){ // db disconnect error
			$this->connected = false;
			$this->db_connection = false;
		}
		$this->db_error = $message;
		if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' -  - '.$message);
	}

	function _setConnected(){
		$this->connected = true;
		if($this->is_inDebugMode())$this->setMessageQue(date("d-m-Y h:i:s").'- Line :' . __LINE__.' -  - DATABASE CONNECTED');
	}
} // End class mysql_wrapper
?>
Return current item: PHP MySQL Caching Wrapper