<?php

/**
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @package fusionLib
 * @copyright Copyright (c) 2012 fusionLib. All rights reserved.
 * @link http://fusionlib.com
 */

/**
 * flCacheSqlite Class
 *
 * Implementation of Sqlite based cache engine. The cache file/database is stored in the folder
 * {FLSYSPATH}/cache/cachedata.{code}.sqlite. Due to the possibly guessable location this should not
 * be used on servers where the supplied .htaccess does not protect the cache folder from being read.
 *
 * @package fusionLib
 */
class flCacheSqlite extends flCache {

	/**
	 * The Sqlite object
	 *
	 * @var flDb
	 */
	private $db;

	/**
	 * Construct the Sqlite cache
	 *
	 * @param int $lifetime The default lifetime for objects in seconds.
	 */
	function __construct($lifetime = 3600) {
		parent::__construct($lifetime);

		// Get the database object and initialise it if required
		$this->db = flDb::connect('sqlite3:///system/cache/cachedata.' . md5(__FILE__) . '.sqlite');
		if(!in_array('cache', $this->db->fetchTables())) {
			// Create the tables
			$this->db->query('CREATE TABLE cache ( id TEXT(32) NOT NULL, expireat INTEGER NOT NULL, content TEXT(5242880) NOT NULL, PRIMARY KEY (id ASC) )');
			$this->db->query('CREATE INDEX "main"."expireat" ON "cache" ("expireat" ASC)');
		}
	}

	/**
	 * Store an element in the cache.
	 *
	 * @param string $id The id to store the element as.
	 * @param mixed $data The data to store.
	 * @param int $lifetime The lifetime of the object in seconds, null to use the default or 0 to never expire.
	 * @return bool true if the data is cached or false if it could not be cached.
	 */
	function store($id, $data, $lifetime = null) {
		// Calculate expire time
		$expire = $lifetime === null ? $this->lifetime : $lifetime;
		if($expire)
			$expire = time() + $expire;

		// Cache the data
		$result = $this->db->replace('cache', array('id' => md5($id), 'expireat' => $expire, 'content' => serialize($data)));

		// Randomly choose to clean cache
		static $autoClean = true;
		if($autoClean && mt_rand(1, 100) == 1) {
			$autoClean = false;
			$this->db->delete('cache', 'expireat AND expireat<?', time());
		}

		return $result;
	}

	/**
	 * Get the element from the cache.
	 *
	 * @param string $id The id of the element to get.
	 * @return mixed false if the element is not cached or has expired; else the cached data.
	 */
	function get($id) {
		$result = $this->db->fetchOne('SELECT content FROM cache WHERE id=? AND (expireat==0 OR expireat>=?)', md5($id), time());
		return $result ? unserialize($result) : false;
	}

	/**
	 * Remove the given element from the cache.
	 *
	 * @param string $id The id of the element to remove.
	 */
	function remove($id) {
		$this->db->delete('cache', 'id=?', md5($id));
	}

	/**
	 * Flush the cache.
	 */
	function flush() {
		$this->db->query('DELETE FROM cache');
	}
}
