Initial commit of Caching Driver.
diff --git a/system/libraries/Cache/Cache.php b/system/libraries/Cache/Cache.php
new file mode 100644
index 0000000..ea31942
--- /dev/null
+++ b/system/libraries/Cache/Cache.php
@@ -0,0 +1,216 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 4.3.2 or newer
+ *
+ * @package		CodeIgniter
+ * @author		ExpressionEngine Dev Team
+ * @copyright	Copyright (c) 2006 - 2010 EllisLab, Inc.
+ * @license		http://codeigniter.com/user_guide/license.html
+ * @link		http://codeigniter.com
+ * @since		Version 2.0
+ * @filesource	
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * CodeIgniter Caching Class 
+ *
+ * @package		CodeIgniter
+ * @subpackage	Libraries
+ * @category	Core
+ * @author		ExpressionEngine Dev Team
+ * @link		
+ */
+class Cache extends CI_Driver_Library {
+	
+	protected $valid_drivers 	= array(
+				'cache_apc', 'cache_file', 'cache_memcached', 'cache_dummy'
+		);
+
+	protected $_cache_path		= NULL;		// Path of cache files (if file-based cache)
+	protected $_adapter			= 'dummy';
+	protected $_backup_driver;
+	
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Constructor
+	 *
+	 * @param array
+	 */
+	public function __construct($config = array())
+	{
+		if ( ! empty($config))
+		{
+			$this->_initialize($config);
+		}
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Get 
+	 *
+	 * Look for a value in the cache.  If it exists, return the data 
+	 * if not, return FALSE
+	 *
+	 * @param 	string	
+	 * @return 	mixed		value that is stored/FALSE on failure
+	 */
+	public function get($id)
+	{	
+		return $this->{$this->_adapter}->get($id);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Cache Save
+	 *
+	 * @param 	string		Unique Key
+	 * @param 	mixed		Data to store
+	 * @param 	int			Length of time (in seconds) to cache the data
+	 *
+	 * @return 	boolean		true on success/false on failure
+	 */
+	public function save($id, $data, $ttl = 60)
+	{
+		return $this->{$this->_adapter}->save($id, $data, $ttl);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Delete from Cache
+	 *
+	 * @param 	mixed		unique identifier of the item in the cache
+	 * @return 	boolean		true on success/false on failure
+	 */
+	public function delete($id)
+	{
+		return $this->{$this->_adapter}->delete($id);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Clean the cache
+	 *
+	 * @return 	boolean		false on failure/true on success
+	 */
+	public function clean()
+	{
+		return $this->{$this->_adapter}->clean();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Cache Info
+	 *
+	 * @param 	string		user/filehits
+	 * @return 	mixed		array on success, false on failure	
+	 */
+	public function cache_info($type = 'user')
+	{
+		return $this->{$this->_adapter}->cache_info($type);
+	}
+
+	// ------------------------------------------------------------------------
+	
+	/**
+	 * Get Cache Metadata
+	 *
+	 * @param 	mixed		key to get cache metadata on
+	 * @return 	mixed		return value from child method
+	 */
+	public function get_metadata($id)
+	{
+		return $this->{$this->_adapter}->get_metadata($id);
+	}
+	
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Initialize
+	 *
+	 * Initialize class properties based on the configuration array.
+	 *
+	 * @param	array 	
+	 * @return 	void
+	 */
+	private function _initialize($config)
+	{        
+		$default_config = array(
+				'adapter',
+				'memcached'
+			);
+
+		foreach ($default_config as $key)
+		{
+			if (isset($config[$key]))
+			{
+				$param = '_'.$key;
+
+				$this->{$param} = $config[$key];
+			}
+		}
+
+		if (isset($config['backup']))
+		{
+			if (in_array('cache_'.$config['backup'], $this->valid_drivers))
+			{
+				$this->_backup_driver = $config['backup'];
+			}
+		}
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Is the requested driver supported in this environment?
+	 *
+	 * @param 	string	The driver to test.
+	 * @return 	array
+	 */
+	public function is_supported($driver)
+	{
+		static $support = array();
+
+		if ( ! isset($support[$driver]))
+		{
+			$support[$driver] = $this->{$driver}->is_supported();
+		}
+
+		return $support[$driver];
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * __get()
+	 *
+	 * @param 	child
+	 * @return 	object
+	 */
+	public function __get($child)
+	{
+		$obj = parent::__get($child);
+
+		if ( ! $this->is_supported($child))
+		{
+			$this->_adapter = $this->_backup_driver;
+		}
+
+		return $obj;
+	}
+	
+	// ------------------------------------------------------------------------
+}
+// End Class
+
+/* End of file Cache.php */
+/* Location: ./system/libraries/Cache/Cache.php */
\ No newline at end of file
diff --git a/system/libraries/Cache/drivers/Cache_apc.php b/system/libraries/Cache/drivers/Cache_apc.php
new file mode 100644
index 0000000..9c716a9
--- /dev/null
+++ b/system/libraries/Cache/drivers/Cache_apc.php
@@ -0,0 +1,151 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.1.6 or newer
+ *
+ * @package		CodeIgniter
+ * @author		ExpressionEngine Dev Team
+ * @copyright	Copyright (c) 2006 - 2010 EllisLab, Inc.
+ * @license		http://codeigniter.com/user_guide/license.html
+ * @link		http://codeigniter.com
+ * @since		Version 2.0
+ * @filesource	
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * CodeIgniter APC Caching Class 
+ *
+ * @package		CodeIgniter
+ * @subpackage	Libraries
+ * @category	Core
+ * @author		ExpressionEngine Dev Team
+ * @link		
+ */
+
+class Cache_apc extends CI_Driver {
+
+	/**
+	 * Get 
+	 *
+	 * Look for a value in the cache.  If it exists, return the data 
+	 * if not, return FALSE
+	 *
+	 * @param 	string	
+	 * @return 	mixed		value that is stored/FALSE on failure
+	 */
+	public function get($id)
+	{
+		$data = apc_fetch($id);
+
+		return (is_array($data)) ? $data[0] : FALSE;
+	}
+
+	// ------------------------------------------------------------------------	
+	
+	/**
+	 * Cache Save
+	 *
+	 * @param 	string		Unique Key
+	 * @param 	mixed		Data to store
+	 * @param 	int			Length of time (in seconds) to cache the data
+	 *
+	 * @return 	boolean		true on success/false on failure
+	 */
+	public function save($id, $data, $ttl = 60)
+	{
+		return apc_store($id, array($data, time(), $ttl), $ttl);
+	}
+	
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Delete from Cache
+	 *
+	 * @param 	mixed		unique identifier of the item in the cache
+	 * @param 	boolean		true on success/false on failure
+	 */
+	public function delete($id)
+	{
+		return apc_delete($id);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Clean the cache
+	 *
+	 * @return 	boolean		false on failure/true on success
+	 */
+	public function clean()
+	{
+		return apc_clear_cache('user');
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Cache Info
+	 *
+	 * @param 	string		user/filehits
+	 * @return 	mixed		array on success, false on failure	
+	 */
+	 public function cache_info($type = NULL)
+	 {
+		 return apc_cache_info($type);
+	 }
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Get Cache Metadata
+	 *
+	 * @param 	mixed		key to get cache metadata on
+	 * @return 	mixed		array on success/false on failure
+	 */
+	public function get_metadata($id)
+	{
+		$stored = apc_fetch($id);
+
+		if (count($stored) !== 3)
+		{
+			return FALSE;
+		}
+
+		list($value, $time, $ttl) = $stored;
+
+		return array(
+			'expire'	=> $time + $ttl,
+			'mtime'		=> $time,
+			'data'		=> $data
+		);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * is_supported()
+	 *
+	 * Check to see if APC is available on this system, bail if it isn't.
+	 */
+	public function is_supported()
+	{
+		if ( ! extension_loaded('apc') OR ! function_exists('apc_store'))
+		{
+			log_message('error', 'The APC PHP extension must be loaded to use APC Cache.');
+			return FALSE;
+		}
+		
+		return TRUE;
+	}
+
+	// ------------------------------------------------------------------------
+
+	
+}
+// End Class
+
+/* End of file Cache_apc.php */
+/* Location: ./system/libraries/Cache/drivers/Cache_apc.php */
\ No newline at end of file
diff --git a/system/libraries/Cache/drivers/Cache_dummy.php b/system/libraries/Cache/drivers/Cache_dummy.php
new file mode 100644
index 0000000..13c1f5c
--- /dev/null
+++ b/system/libraries/Cache/drivers/Cache_dummy.php
@@ -0,0 +1,129 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 4.3.2 or newer
+ *
+ * @package		CodeIgniter
+ * @author		ExpressionEngine Dev Team
+ * @copyright	Copyright (c) 2006 - 2010 EllisLab, Inc.
+ * @license		http://codeigniter.com/user_guide/license.html
+ * @link		http://codeigniter.com
+ * @since		Version 2.0
+ * @filesource	
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * CodeIgniter Dummy Caching Class 
+ *
+ * @package		CodeIgniter
+ * @subpackage	Libraries
+ * @category	Core
+ * @author		ExpressionEngine Dev Team
+ * @link		
+ */
+
+class Cache_dummy extends CI_Driver {
+
+	/**
+	 * Get 
+	 *
+	 * Since this is the dummy class, it's always going to return FALSE.
+	 *
+	 * @param 	string	
+	 * @return 	Boolean		FALSE
+	 */
+	public function get($id)
+	{
+		return FALSE;
+	}
+
+	// ------------------------------------------------------------------------	
+	
+	/**
+	 * Cache Save
+	 *
+	 * @param 	string		Unique Key
+	 * @param 	mixed		Data to store
+	 * @param 	int			Length of time (in seconds) to cache the data
+	 *
+	 * @return 	boolean		TRUE, Simulating success
+	 */
+	public function save($id, $data, $ttl = 60)
+	{
+		return TRUE;
+	}
+	
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Delete from Cache
+	 *
+	 * @param 	mixed		unique identifier of the item in the cache
+	 * @param 	boolean		TRUE, simulating success
+	 */
+	public function delete($id)
+	{
+		return TRUE;
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Clean the cache
+	 *
+	 * @return 	boolean		TRUE, simulating success
+	 */
+	public function clean()
+	{
+		return TRUE;
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Cache Info
+	 *
+	 * @param 	string		user/filehits
+	 * @return 	boolean		FALSE
+	 */
+	 public function cache_info($type = NULL)
+	 {
+		 return FALSE;
+	 }
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Get Cache Metadata
+	 *
+	 * @param 	mixed		key to get cache metadata on
+	 * @return 	boolean		FALSE
+	 */
+	public function get_metadata($id)
+	{
+		return FALSE;
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Is this caching driver supported on the system?
+	 * Of course this one is.
+	 * 
+	 * @return TRUE;
+	 */
+	public function is_supported()
+	{
+		return TRUE;
+	}
+
+	// ------------------------------------------------------------------------
+	
+}
+// End Class
+
+/* End of file Cache_apc.php */
+/* Location: ./system/libraries/Cache/drivers/Cache_apc.php */
\ No newline at end of file
diff --git a/system/libraries/Cache/drivers/Cache_file.php b/system/libraries/Cache/drivers/Cache_file.php
new file mode 100644
index 0000000..bedbfaf
--- /dev/null
+++ b/system/libraries/Cache/drivers/Cache_file.php
@@ -0,0 +1,196 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 4.3.2 or newer
+ *
+ * @package		CodeIgniter
+ * @author		ExpressionEngine Dev Team
+ * @copyright	Copyright (c) 2006 - 2010 EllisLab, Inc.
+ * @license		http://codeigniter.com/user_guide/license.html
+ * @link		http://codeigniter.com
+ * @since		Version 2.0
+ * @filesource	
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * CodeIgniter Memcached Caching Class 
+ *
+ * @package		CodeIgniter
+ * @subpackage	Libraries
+ * @category	Core
+ * @author		ExpressionEngine Dev Team
+ * @link		
+ */
+
+class Cache_file extends CI_Driver {
+
+	protected $_cache_path;
+
+	/**
+	 * Constructor
+	 */
+	public function __construct()
+	{
+		$CI =& get_instance();
+		$CI->load->helper('file');
+		
+		$path = $CI->config->item('cache_path');
+	
+		$this->_cache_path = ($path == '') ? BASEPATH.'cache/' : $path;
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Fetch from cache
+	 *
+	 * @param 	mixed		unique key id
+	 * @return 	mixed		data on success/false on failure
+	 */
+	public function get($id)
+	{
+		if ( ! file_exists($this->_cache_path.$id))
+		{
+			return FALSE;
+		}
+		
+		$data = read_file($this->_cache_path.$id);
+		$data = unserialize($data);
+		
+		if (time() >  $data['time'] + $data['ttl'])
+		{
+			unlink($this->_cache_path.$id);
+			return FALSE;
+		}
+		
+		return $data['data'];
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Save into cache
+	 *
+	 * @param 	string		unique key
+	 * @param 	mixed		data to store
+	 * @param 	int			length of time (in seconds) the cache is valid 
+	 *						- Default is 60 seconds
+	 * @return 	boolean		true on success/false on failure
+	 */
+	public function save($id, $data, $ttl = 60)
+	{		
+		$contents = array(
+				'time'		=> time(),
+				'ttl'		=> $ttl,			
+				'data'		=> $data
+			);
+		
+		if (write_file($this->_cache_path.$id, serialize($contents)))
+		{
+			@chmod($this->_cache_path.$id, 0777);
+			return TRUE;			
+		}
+
+		return FALSE;
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Delete from Cache
+	 *
+	 * @param 	mixed		unique identifier of item in cache
+	 * @return 	boolean		true on success/false on failure
+	 */
+	public function delete($id)
+	{
+		return unlink($this->_cache_path.$id);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Clean the Cache
+	 *
+	 * @return 	boolean		false on failure/true on success
+	 */	
+	public function clean()
+	{
+		return delete_files($this->_cache_path);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Cache Info
+	 *
+	 * Not supported by file-based caching
+	 *
+	 * @param 	string	user/filehits
+	 * @return 	mixed 	FALSE
+	 */
+	public function cache_info($type = NULL)
+	{
+		return get_dir_file_info($this->_cache_path);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Get Cache Metadata
+	 *
+	 * @param 	mixed		key to get cache metadata on
+	 * @return 	mixed		FALSE on failure, array on success.
+	 */
+	public function get_metadata($id)
+	{
+		if ( ! file_exists($this->_cache_path.$id))
+		{
+			return FALSE;
+		}
+		
+		$data = read_file($this->_cache_path.$id);		
+		$data = unserialize($data);
+		
+		if (is_array($data))
+		{
+			$data = $data['data'];
+			$mtime = filemtime($this->_cache_path.$id);
+
+			if ( ! isset($data['ttl']))
+			{
+				return FALSE;
+			}
+
+			return array(
+				'expire' 	=> $mtime + $data['ttl'],
+				'mtime'		=> $mtime
+			);
+		}
+		
+		return FALSE;
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Is supported
+	 *
+	 * In the file driver, check to see that the cache directory is indeed writable
+	 * 
+	 * @return boolean
+	 */
+	public function is_supported()
+	{
+		return is_really_writable($this->_cache_path);
+	}
+
+	// ------------------------------------------------------------------------
+}
+// End Class
+
+/* End of file Cache_file.php */
+/* Location: ./system/libraries/Cache/drivers/Cache_file.php */
\ No newline at end of file
diff --git a/system/libraries/Cache/drivers/Cache_memcached.php b/system/libraries/Cache/drivers/Cache_memcached.php
new file mode 100644
index 0000000..adc7fbf
--- /dev/null
+++ b/system/libraries/Cache/drivers/Cache_memcached.php
@@ -0,0 +1,209 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 4.3.2 or newer
+ *
+ * @package		CodeIgniter
+ * @author		ExpressionEngine Dev Team
+ * @copyright	Copyright (c) 2006 - 2010 EllisLab, Inc.
+ * @license		http://codeigniter.com/user_guide/license.html
+ * @link		http://codeigniter.com
+ * @since		Version 2.0
+ * @filesource	
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * CodeIgniter Memcached Caching Class 
+ *
+ * @package		CodeIgniter
+ * @subpackage	Libraries
+ * @category	Core
+ * @author		ExpressionEngine Dev Team
+ * @link		
+ */
+
+class Cache_memcached extends CI_Driver {
+
+	private $_memcached;	// Holds the memcached object
+
+	protected $_memcache_conf 	= array(
+					'default' => array(
+						'default_host'		=> '127.0.0.1',
+						'default_port'		=> 11211,
+						'default_weight'	=> 1
+					)
+				);
+
+	// ------------------------------------------------------------------------	
+
+	/**
+	 * Fetch from cache
+	 *
+	 * @param 	mixed		unique key id
+	 * @return 	mixed		data on success/false on failure
+	 */	
+	public function get($id)
+	{	
+		$data = $this->_memcached->get($id);
+		
+		return (is_array($data)) ? $data[0] : FALSE;
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Save
+	 *
+	 * @param 	string		unique identifier
+	 * @param 	mixed		data being cached
+	 * @param 	int			time to live
+	 * @return 	boolean 	true on success, false on failure
+	 */
+	public function save($id, $data, $ttl = 60)
+	{
+		return $this->_memcached->add($id, array($data, time(), $ttl), $ttl);
+	}
+
+	// ------------------------------------------------------------------------
+	
+	/**
+	 * Delete from Cache
+	 *
+	 * @param 	mixed		key to be deleted.
+	 * @return 	boolean 	true on success, false on failure
+	 */
+	public function delete($id)
+	{
+		return $this->_memcached->delete($id);
+	}
+
+	// ------------------------------------------------------------------------
+	
+	/**
+	 * Clean the Cache
+	 *
+	 * @return 	boolean		false on failure/true on success
+	 */
+	public function clean()
+	{
+		return $this->_memcached->flush();
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Cache Info
+	 *
+	 * @param 	null		type not supported in memcached
+	 * @return 	mixed 		array on success, false on failure
+	 */
+	public function cache_info($type = NULL)
+	{
+		return $this->_memcached->getStats();
+	}
+
+	// ------------------------------------------------------------------------
+	
+	/**
+	 * Get Cache Metadata
+	 *
+	 * @param 	mixed		key to get cache metadata on
+	 * @return 	mixed		FALSE on failure, array on success.
+	 */
+	public function get_metadata($id)
+	{
+		$stored = $this->_memcached->get($id);
+
+		if (count($stored) !== 3)
+		{
+			return FALSE;
+		}
+
+		list($value, $time, $ttl) = $stored;
+
+		return array(
+			'expire'	=> $time + $ttl,
+			'mtime'		=> $time,
+			'data'		=> $data
+		);
+	}
+
+	// ------------------------------------------------------------------------
+
+	/**
+	 * Setup memcached.
+	 */
+	private function _setup_memcached()
+	{
+		// Try to load memcached server info from the config file.
+		$CI =& get_instance();
+		if ($CI->config->load('memcached', TRUE, TRUE))
+		{
+			if (is_array($CI->config->config['memcached']))
+			{
+				$this->_memcache_conf = NULL;
+
+				foreach ($CI->config->config['memcached'] as $name => $conf)
+				{
+					$this->_memcache_conf[$name] = $conf;
+				}				
+			}			
+		}
+		
+		$this->_memcached = new Memcached();
+
+		foreach ($this->_memcache_conf as $name => $cache_server)
+		{
+			if ( ! array_key_exists('hostname', $cache_server))
+			{
+				$cache_server['hostname'] = $this->_default_options['default_host'];
+			}
+	
+			if ( ! array_key_exists('port', $cache_server))
+			{
+				$cache_server['port'] = $this->_default_options['default_port'];
+			}
+	
+			if ( ! array_key_exists('weight', $cache_server))
+			{
+				$cache_server['weight'] = $this->_default_options['default_weight'];
+			}
+	
+			$this->_memcached->addServer(
+					$cache_server['hostname'], $cache_server['port'], $cache_server['weight']
+			);
+		}
+	}
+
+	// ------------------------------------------------------------------------
+
+
+	/**
+	 * Is supported
+	 *
+	 * Returns FALSE if memcached is not supported on the system.
+	 * If it is, we setup the memcached object & return TRUE
+	 */
+	public function is_supported()
+	{
+		if ( ! extension_loaded('memcached'))
+		{
+			log_message('error', 'The Memcached Extension must be loaded to use Memcached Cache.');
+			
+			return FALSE;
+		}
+		
+		$this->_setup_memcached();
+		return TRUE;
+	}
+
+	// ------------------------------------------------------------------------
+
+}
+// End Class
+
+/* End of file Cache_memcached.php */
+/* Location: ./system/libraries/Cache/drivers/Cache_memcached.php */
\ No newline at end of file