今天花了點時間看了下CI框架源碼緩存的實現,寫出來梳理下思路. 1:在CI框架中加載視圖文件使用的是$this->load->view();方法,因此從load類庫着手,在ci的system文件夾中能夠看到Loader.php,這個類庫是在Controller.php中被加載的。Loader類中有個方法: function view($view, $vars = array(), $return = FALSE)//加載視圖 { return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return)); } 調用了自身的一個私有方法_ci_load(),這個方法其中關鍵部分在: ob_start();//開啓緩存 // If the PHP installation does not support short tags we'll // do a little string replacement, changing the short tags // to standard PHP echo statements. if ((bool) @ini_get('short_open_tag') === FALSE AND config_item('rewrite_short_tags') == TRUE) { echo eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace('< php echo file_get_contents_ci_pathbr /> } else { //將視圖包含進來 include($_ci_path); // include() vs include_once() allows for multiple views with the same name } if (ob_get_level() < $this-<_ci_ob_level + 1) { ob_end_flush(); } else { $_ci_CI-<output-<append_output(ob_get_contents());//獲取緩存,調用了output類中的append_output方法將緩存的內容放到了output類的全局變量final_output中,供後面使用。 @ob_end_clean(); } 2:CI框架中設置緩存的方法是$this-<output-<cache(n)//n是分鐘數 打開system/core/Output.php在裏面有個cache方法: function cache($time) { $this-<cache_expiration = ( ! is_numeric($time)) ? 0 : $time; //output類中變量cache_expiration賦上緩存時間 return $this; } 3:打開system/core/Codeigniter.php這個核心文件。能夠看到以下代碼: $OUT =& load_class('Output', 'core');//實例化output類 // 調用鉤子 cache_override hook if ($EXT->_call_hook('cache_override') === FALSE)//若是沒有設置這個緩存鉤子就使用默認的_display_cache方法 { if ($OUT->_display_cache($CFG, $URI) == TRUE)//將config,uri類的對象傳入 { exit;//若是調用緩存成功就會直接顯示頁面中斷程序,不會加載實例化下面的類,進行一些請求,這就是緩存的好處; } } 4:找到Output.php類中的私有方法_display_cache($CFG, $URI): function _display_cache(&$CFG, &$URI) { //是否在配置文件中定義了緩存路徑,若是沒有是用系統默認的cache文件夾做爲緩存目錄 $cache_path = ($CFG->item('cache_path') == '') ? APPPATH.'cache/' : $CFG->item('cache_path'); // 構造文件路徑。文件名是 URI 的 md5 值 $uri = $CFG->item('base_url'). $CFG->item('index_page'). $URI->uri_string;//這是請求的頁面的控制器/方法/參數那一串字符 $filepath = $cache_path.md5($uri); // 判斷文件是否存在 if ( ! @file_exists($filepath)) { return FALSE;//到了這裏就中斷了,而是按照正常的向服務器請求頁面內容,下面的return false同理 } if ( ! $fp = @fopen($filepath, FOPEN_READ)) { return FALSE; } flock($fp, LOCK_SH);//讀取文件前給文件加個共享鎖 $cache = ''; if (filesize($filepath) > 0) { $cache = fread($fp, filesize($filepath)); } flock($fp, LOCK_UN);//釋放鎖 fclose($fp); // 匹配內嵌時間戳 if ( ! preg_match("/(\d+TS--->)/", $cache, $match)) { return FALSE; } // Has the file expired? If so we'll delete it. // 文件過時了,就刪掉 if (time() >= trim(str_replace('TS--->', '', $match['1']))) { if (is_really_writable($cache_path)) { @unlink($filepath); log_message('debug', "Cache file has expired. File deleted"); return FALSE } // Display the cache // 顯示緩存,到了這裏說明有緩存文件而且緩存文件沒過時,而後執行_display方法 $this->_display(str_replace($match['0'], '', $cache)); log_message('debug', "Cache file is current. Sending it to browser."); return TRUE; } 5:找到Output方法中的_display($output='')方法,這個 方法有兩處調用了,1個是在上述的_display_cache中,將緩存文件中的內容取出賦於$output變量而後傳入_display($output='')中,這時候只會執行_display中的: //$CI 對象不存在,咱們就知道咱們是在處理緩存文件,因此簡單的輸出和退出 if ( ! isset($CI)) { echo $output;//直接將緩存輸出,返回ture中斷codeigniter繼續執行 log_message('debug', "Final output sent to browser"); log_message('debug', "Total execution time: ".$elapsed); return TRUE; } 第二處調用是,當if ($OUT->_display_cache($CFG, $URI) == TRUE)這個判斷不成立codeigniter向下執行, 前後實例化了一些系統核心類,以及url中請求的控制器方法等.最後執行一個鉤子: // 調用 display_override hook if ($EXT->_call_hook('display_override') === FALSE) { $OUT->_display(); } 這時候執行這個方法是無緩存的狀況下. 這時候$output爲空因此執行了: // 設置輸出數據 if ($output == '') { $output =& $this->final_output;//這就是在Loader中設置的輸出緩存. } 接下來若是執行了$this->output->cache()方法設置了$this->cache_expiration 參數且沒有緩存文件時: // 啓用 cache 時,$CI 沒有 _output 函數時,調用 $this->_write_cache,寫緩存文件 if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output')) { $this->_write_cache($output); } _write_cache($output)方法以下: function _write_cache($output) { $CI =& get_instance(); $path = $CI->config->item('cache_path'); $cache_path = ($path == '') ? APPPATH.'cache/' : $path; // $cache_path 是目錄而且可寫 if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path)) { log_message('error', "Unable to write cache file: ".$cache_path); return; } $uri = $CI->config->item('base_url'). $CI->config->item('index_page'). $CI->uri->uri_string(); $cache_path .= md5($uri); if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE)) { log_message('error', "Unable to write cache file: ".$cache_path); return; } // 加個時間戳,指示過時時間 $expire = time() + ($this->cache_expiration * 60); if (flock($fp, LOCK_EX))//寫入前先加個獨佔鎖 { fwrite($fp, $expire.'TS--->'.$output); flock($fp, LOCK_UN);//寫完解鎖 } else { log_message('error', "Unable to secure a file lock for file at: ".$cache_path); return; } fclose($fp); @chmod($cache_path, FILE_WRITE_MODE); log_message('debug', "Cache file written: ".$cache_path); } 寫完緩存後會進行一系列處理好比設置header等 最後輸出$output: if (method_exists($CI, '_output')) { $CI->_output($output); } else { echo $output; // Send it to the browser! } 總結:CI的緩存是在要輸出的頁面設置ob_start(),使用ob_get_contents()獲取緩存內容,而後經過判斷設置中 是否設置緩存.若是設置了則將緩存將頁面的url地址進行MD5哈希做爲緩存文件名建立之,而後將(當前時間+設置的緩存時間)+一個特殊符號+內容寫到 緩存文件中,下次訪問時候將訪問的url進行MD5查找這個緩存文件,若是沒有則再建立.有則取出其中的內容,分離出過時時間和內容,判斷時間是否過時, 若是過時則丟棄內容,繼續進行請求,若是沒過時直接取出內容輸出到頁面,中斷執行。CI將這一套緩存機制用面向對象的方法寫到了框架中,使用起來很方便。 CI默認的這種緩存方法是緩存整個頁面。但有時候只要緩存頁面中不變的元素header和footer比較好,CI中還有鉤子的機制,能夠本身設置緩存的 方法替換其中的_display_cache()方法。具體的能夠看手冊