<?php /** * 緩存類,實現數據,輸出緩存 * @author ZhouHr 2012-11-09 http://www.ketann.com * @copyright version 0.1 */ class Cache { private static $_instance; protected $_cacheId = null; const CLEANING_MODE_ALL = 'all'; const CLEANING_MODE_OLD = 'old'; protected $_options = array( 'cache_dir' => null, //數據緩存目錄 'life_time' => 7200, //緩存時間 'page_dir' => null, //文本緩存目錄 'cache_prefix' => 'cache_' //緩存前綴 ); private function __construct(){} //創建__clone方法防止對象被復制克隆 private function __clone(){} /** * 取緩存對象,如果存在直接返回,如果不存在實例化本身 * @return object cache */ public static function getInstance(){ if(! self::$_instance){ self::$_instance = new self(); } return self::$_instance; } /** * 設置緩存參數集 * @param array $options 要設置的緩存參數集 */ public function setOptions($options = array()){ while (list($name, $value) = each($options)) { $this->setOption($name, $value); } } /** * 取得當前緩存參數,如果$name為空返回全部參數,否則返回該參數值 * @param string $name 要返回的參數名稱 * @return string or array $option; */ public function getOption($name = null){ if(null === $name) return $this->_options; if (!is_string($name)) { throwException("不正確的參數名稱 : $name"); } if (array_key_exists($name, $this->_options)){ return $this->_options[$name]; } } /** * 設置緩存參數 * @param array $options 要設置的緩存參數 */ protected function setOption($name, $value){ if (!is_string($name)) { throwException("不正確的參數名稱 : $name"); } $name = strtolower($name); if (array_key_exists($name, $this->getOption())){ $this->_options[$name] = $value; } if ($this->_options['cache_dir'] === null) { $this->setOption('cache_dir', $this->getTmpDir() . DIRECTORY_SEPARATOR); } if ($this->_options['page_dir'] === null) { $this->setOption('page_dir', $this->getTmpDir() . DIRECTORY_SEPARATOR); } } /** * 讀取數據緩存,如果不存在或過期,返回false * @param string $id 緩存ID * @return false or data */ public function load($id){ $this->_cacheId = $id; $file = $this->getOption('cache_dir') . $this->getOption('cache_prefix') . $this->_cacheId; if (@filemtime($file) >= time()){ return unserialize(file_get_contents($file)); } else { @unlink($file); return false; } } /** * 保存數據緩存,並設置緩存過期時間 * @param array or string $data 要緩存的數據 * @param int $lifeTime 緩存過期時間 */ public function save($data, $lifeTime = null){ if(null !== $lifeTime) $this->setOption('life_time', $lifeTime); $file = $this->getOption('cache_dir') . $this->getOption('cache_prefix') . $this->_cacheId; $data = serialize($data); @file_put_contents($file, $data); @chmod($file, 0777); @touch($file, time() + $this->getOption('life_time'])); } /** * 讀取輸出緩存,如果不存在或緩存過期將重新開啟輸出緩存 * @param string $id 緩存ID */ public function start($id){ $this->_cacheId = $id; $file = $this->getOption('page_dir') . $this->getOption('cache_prefix') . $this->_cacheId; if (@filemtime($file) >= time()){ return file_get_contents($file); } else { @unlink($file); ob_start(); return false; } } /** * 刪除指定ID緩存 * @param string $id 緩存ID */ public function remove($id){ $this->_cacheId = $id; //刪除附合條件的數據緩存 $file = $this->getOption('cache_dir') . $this->getOption('cache_prefix') . $this->_cacheId; @unlink($file); //刪除附合條件的輸出緩存 $file = $this->getOption('page_dir') . $this->getOption('cache_prefix') . $this->_cacheId; @unlink($file); } /** * 保存輸出緩存,並設置緩存過期時間 * @param int $lifeTime 緩存過期時間 */ public function end($lifeTime = null){ if(null !== $lifeTime) $this->setOption('life_time', $lifeTime); $file = $this->getOption('page_dir') . $this->getOption('cache_prefix') . $this->_cacheId; $data = ob_get_contents(); ob_end_clean(); @file_put_contents($file, $data); @chmod($file, 0777); @touch($file, time() + $this->getOption('life_time'])); } /** * 根據參數清除相應緩存 * @param string $mode 緩存類型,包括(CLEANING_MODE_ALL:所有緩存, CLEANING_MODE_OLD: 過期緩存) */ public function clear($mode = CLEANING_MODE_OLD){ $dirs = array('cache_dir', 'page_dir'); foreach($dirs as $value){ if(null != $this->getOption($value)){ $files = scandir($this->getOption($value)); switch ($mode) { case CLEANING_MODE_ALL: default: foreach ($files as $val){ @unlink($this->getOption($value) . $val); } break; case CLEANING_MODE_OLD: default: foreach ($files as $val){ if (filemtime($this->getOption($value) . $val) < time()){ @unlink($this->getOption($value) . $val); } } break; } } } } /** * 取臨時文件夾為緩存文件夾 * @return $dir 臨時文件夾路徑 */ public function getTmpDir(){ $tmpdir = array(); foreach (array($_ENV, $_SERVER) as $tab) { foreach (array('TMPDIR', 'TEMP', 'TMP', 'windir', 'SystemRoot') as $key) { if (isset($tab[$key])) { if (($key == 'windir') or ($key == 'SystemRoot')) { $dir = realpath($tab[$key] . '\\temp'); } else { $dir = realpath($tab[$key]); } if ($this->_isGoodTmpDir($dir)) { return $dir; } } } } $upload = ini_get('upload_tmp_dir'); if ($upload) { $dir = realpath($upload); if ($this->_isGoodTmpDir($dir)) { return $dir; } } if (function_exists('sys_get_temp_dir')) { $dir = sys_get_temp_dir(); if ($this->_isGoodTmpDir($dir)) { return $dir; } } //通過嘗試創建一個臨時文件來檢測 $tempFile = tempnam(md5(uniqid(rand(), TRUE)), ''); if ($tempFile) { $dir = realpath(dirname($tempFile)); unlink($tempFile); if ($this->_isGoodTmpDir($dir)) { return $dir; } } if ($this->_isGoodTmpDir('/tmp')) { return '/tmp'; } if ($this->_isGoodTmpDir('\\temp')) { return '\\temp'; } throw new Exception('無法確定臨時目錄,請手動指定cache_dir', E_USER_ERROR); } /** * 驗證給定的臨時目錄是可讀和可寫的 * * @param string $dir 臨時文件夾路徑 * @return boolean true or false 臨時文件夾路徑是否可讀寫 */ protected function _isGoodTmpDir($dir){ if (is_readable($dir)) { if (is_writable($dir)) { return true; } } return false; } }//endclass