yii中緩存(cache)詳解 - 彼岸あ年華ツ


緩存是用於提升網站性能的一種即簡單又有效的途徑。通過存儲相對靜態的數據至緩存以備所需,我們可以省去生成 
這些數據的時間。在 Yii 中使用緩存主要包括配置和訪問緩存組件 。

內部方法

一、緩存配置:

1、單一緩存組件配置:

Yii緩存可以在不同的級別使用。在最低級別,可用來緩存單個數據(數據緩存)。往上一級,我們緩存一個由視圖腳本生成的頁面片斷(片段緩存)。在最高級別,可存儲整個頁面以便需要的時候直接從緩存讀取。本文說明頁面緩存的配置及實現效果;

實現分為2步;

1. 在config文件加入緩存組件.

‘cache’ => array (

‘class’ => ‘system.caching.CFileCache’,

‘directoryLevel’ => 2,

),

class標識需要使用的緩存媒介,用途比較廣的類型基本都有支持:

CMemCache: 使用 PHP memcache 擴展.

CApcCache: 使用 PHP APC 擴展.

CDbCache: 使用一張數據庫表來存儲緩存數據。

CFileCache: 使用文件來存儲緩存數據。 特別適用於大塊數據(例如頁面)。

當然,yii也可以支持Redis,需要裝一個插件:

http://www.yiibase.com/download/view/32.html

本文實例使用的是文件緩存,對於文件緩存,緩存到的位置為protected/runtime/;directoryLevel設置緩存文件的目錄深度;如果緩存頁面特別多,這個值需要設置大點,否則每個目錄下的頁面會很多;

對於除class其他的選項的配置,可以查看各class類中的一些屬性

2. 在要做緩存的控制器里定義過濾器。

public function filters() {

return array (

array (

‘COutputCache + post, list’,

‘duration’ => 3600,

‘varyByParam’ => array(‘id’,'page’),

‘dependency’ => array(

‘class’=>’CDbCacheDependency’,

‘sql’=>’SELECT MAX(id) FROM me115_book’,

)

);

}

COutputCache 是用於處理緩存的類,如果只填’COutputCache’,則控制器里所有action都會通過緩存過濾,定義’COutputCache + post, list’,表示只對以下方法進行緩存:actionPost, actionList

duration 是緩存的時間,單位是秒,

varyByParam 是指定一系列GET參數名稱列表, 使用相應的值去確定緩存內容的版本,即同一個action用於區分是不同頁面的的參數,此處我以id和page來區分不同頁面。

除varyByParam以外,還可以采用其他的條件來區分頁面:

varyByExpression:指定緩存內容通過自定義的PHP表達式的結果而變化

varyByRoute:指定緩存內容基於請求的路由不同而變化 (controller 和 action)

varyBySession:指定是否緩存內容. 因用戶session不同而變化

dependency’指定緩存失效依賴關系:可指定文件或數據庫;本文采用的是數據庫依賴CDbCacheDependency;

本例指定的是數據庫,通過數據表的某個值的變化來確定緩存是否失效。例如,如果在表中新增了一條me115_book記錄,即使緩存才過了2分鍾(<3600),仍然判斷為失效,從而查詢數據庫,生成整個頁面,再次緩存;

檢查:

查看當前頁面是否緩存,可以dump輸出一個當前服務器時間,從而檢查當前頁面是否已緩存;

優化效果:

優化站點為一個博客站點(me115.com),除了DNS解析轉接外,未進行任何優化,優化前的數據為:

微考教程網-PHP開源框架Yii 緩存配置實例

微考教程網-PHP開源框架Yii 緩存配置實例

首字節時間為842ms;

采用頁面緩存之后的效果:

微考教程網-PHP開源框架Yii 緩存配置實例

微考教程網-PHP開源框架Yii 緩存配置實例

首字節時間為376ms;html生成的時間大大縮短,后台時間減少了一倍。

當然,通過本圖可以看到整個站點的用時還是比較長,主要是在頁面組件(css/js/圖片)上的下載耗費了不少時間,后續將針對這方面進行前端優化;

2、多緩存組件配置:

詳看:http://hudeyong926.iteye.com/blog/1313713

//不管是多緩存配置還是單緩存配置,都需要設置一個默認的cache組件,因為CDbConnection中的 schemaCacheID ,queryCacheID,CCacheHttpSession 中的cacheID ,CUrlManager 中的cacheID, CStatePersister 中的cacheID都是默認的 ’cache'

'cache' => array(

'class' => 'CFileCache',
),

'ApcCache'=>array(

'class'=>'CApcCache',
),
'Dbcache'=>array(
'class'=>'CDbCache',
),
'FileCache'=>array(
'class'=>'CFileCache',
'cachePath'=> '/Webroot/trackstar/protected/runtime/cache/test',
),
'MemCache'=>array(
'class'=>'CMemCache',
'servers'=>array(
array(
'host'=>'server1',
'port'=>11211,
'weight'=>60,
),
array(
'host'=>'server2',
'port'=>11211,
'weight'=>40,
),
),
),   

session cache在開啟apc cache時可以用,它將seesion存到apc中比存到文件中要快
    'cache' => array( 'class' => 'CApcCache', ), 'session' => array( 'class' => 'CCacheHttpSession', //CCacheHttpSession在system.web包里 ),

yii如果要使用session.save_handler=memcache 如果需要擴展自定義的session管理方法 ,僅僅需要繼承CHttpSession, 重寫openSession,readSession,writeSession, destroySession,gcSession 這五個方法即可 
3、數據庫緩存配置: 
//main.php配置文件中 數據庫緩存配置,具體參照CDbConnection

'schemaCachingDuration' => 3600, //緩存時間
'schemaCachingExclude' => array(), //不需要緩存的表名數組
'schemaCacheID' => 'cache', //使用的緩存組件 此外,CDbConnection中還有關於數據庫查詢的緩存配置,不過這里只是一個全局的配置 //查詢緩存配置 'queryCachingDuration' => 3600,//緩存時間 'queryCachingDependency' => null, //緩存依賴 'queryCachingCount' => 2, //第一次使用這條sql語句后同樣的多少條sql語句需要緩存 'queryCacheID' => 'cache', //使用的緩存組件 需要配置數據庫查詢緩存,可以使用使用以下方法:

一、CDbConnection中的cache方法 
public CDbConnection cache(integer $duration, CCacheDependency $dependency=NULL,integer $queryCount=1) 
$duration integer 查詢結果保持在緩存中有效的秒數。如果它是0,緩存將被禁用。

$dependency CCacheDependency    當查詢結果保存到緩存時,使用的依賴。
$queryCount integer 在調用此方法后,需要緩存的SQL查詢的數目。默認為 1,意味着下一條SQL查詢將被緩存。
{return}    CDbConnection   返回連接實例本身。

public function cache($duration, $dependency=null, $queryCount=1)
{
$this->queryCachingDuration=$duration;
$this->queryCachingDependency=$dependency;
$this->queryCachingCount=$queryCount;
return $this;
}

例子:
$sql = 'SELECT * FROM tbl_post LIMIT 20';  
$dependency = new CDbCacheDependency('SELECT MAX(update_time) FROM tbl_post'); $rows = Yii::app()->db->cache(1000, $dependency)->createCommand($sql)->queryAll();
二、CActiveRecord中的cache方法:
public CActiveRecord cache(integer $duration, CCacheDependency $dependency=NULL, integer $queryCount=1) $duration integer 查詢結果可能保持在緩存中有效的秒數。如果這是0,緩存將被禁用。 $dependency CCacheDependency 保存查詢結果到緩存時使用的依賴關系。 $queryCount integer 在調用此方法后,需要緩存的SQL查詢的數目。 默認值為 1,這意味着下一個SQL查詢將被緩存。 {return} CActiveRecord AR實例本身。

public function cache($duration, $dependency=null, $queryCount=1)
{
 
//實際上他調用的是CDbConnection中的cache方法
$this->getDbConnection()->cache($duration, $dependency, $queryCount);
return $this;
}

例子:

$dependency = new CDbCacheDependency('SELECT MAX(update_time) FROM tbl_post'); $posts = Post::model()->cache(1000, $dependency)->findAll();
關於$queryCount:

啟用查詢緩存

要使用查詢緩存,首先要確保CDbConnection::queryCacheID指向一個可用的緩存組件ID(默認是cache)。

用DAO查詢緩存

要使用查詢緩存,我們在數據庫查詢的時候調用CDbConnection::cache()這個方法。例如:

  1. $sql = 'SELECT * FROM tbl_post LIMIT 20';
  2. $dependency = new CDbCacheDependency('SELECT MAX(update_time) FROM tbl_post');
  3. $rows = Yii::app()->db->cache(1000, $dependency)->createCommand($sql)->queryAll();

執行以上語句的時候,Yii會首先檢查一下緩存是否包含一個該語句的查詢結果。檢查步驟是以下的三個條件:

●如果緩存包含了SQL語句中的入口索引 
●如果入口還沒過期(少於保存后的1000秒) 
●如果依賴關系沒有變化(update_time的最大值是跟查詢結果保存到緩存時一致)

如果以上3個條件都滿足了,緩存的結果就會直接返回給請求。否則,SQL語句就會被傳遞到數據庫系統去執行,得到的結果會保存到緩存,返回給請求。

用AR查詢緩存

我們也可以用AR來查詢緩存。我們用一個類似的方法,調用CActiveRecord::cache():

  1. $dependency = new CDbCacheDependency('SELECT MAX(update_time) FROM tbl post');
  2. $posts = Post::model()->cache(1000, $dependency)->findAll();
  3. // relational AR query
  4. $posts = Post::model()->cache(1000, $dependency)->with('author')->findAll();

上面的cache()方法,實際上是CDbConnection::cache()的快捷方式。在內部,當執行AR的查詢語句是,Yii會嘗試我們之前講述過的查詢緩存。

緩存的多種查詢

默認情況下,我們每次調用cache()(不管是CDbConnection 還是 CActiveRecord),都會標記下次要緩存的SQL,其他任何的SQL查詢都不會被緩存,除非我們再次調用cache(),例如:

  1. $sql = 'SELECT * FROM tbl post LIMIT 20';
  2. $dependency = new CDbCacheDependency('SELECT MAX(update_time) FROM tbl post');
  3. $rows = Yii::app()->db->cache(1000, $dependency)->createCommand($sql)->queryAll();
  4. // query caching will NOT be used
  5. $rows = Yii::app()->db->createCommand($sql)->queryAll();

通過傳遞另一個參數$queryCount到cache()的方法中,我們可以強制多次查詢緩存。下面的例子中,通過調用call(),我們指定這個換成必須用於接下來的2次

  1. // ...
  2. $rows = Yii::app()->db->cache(1000, $dependency, 2)->createCommand($sql)->queryAll();
  3. // query caching WILL be used
  4. $rows = Yii::app()->db->createCommand($sql)->queryAll();

如你所知,當我們執行關聯AR查詢時,可能是執行多句的SQL。例如,Post與Coment之間的關系是HAS_MANY,以下的SQL語句是數據庫中真正執行的。

it first selects the posts limited by 20; 
it then selects the comments for the previously selected posts.

  1. $posts = Post::model()->with('comments')->findAll(array(
  2. 'limit'=>20,
  3. ));

如果我們用下面的語句查詢緩存,只有第一句會被緩存:

  1. $posts = Post::model()->cache(1000, $dependency)->with('comments')->findAll(array(
  2.     'limit'=>20,
  3. ));

如果要緩存兩個,我們要提供額外的參數來指明接下來我們要緩存幾句:

  1. $posts = Post::model()->cache(1000, $dependency, 2)->with('comments')->findAll(array(
  2.     'limit'=>20,
  3. ));

限制

如果查詢結果中包含資源句柄,查詢訪問就不能用了。例如,我們在某些數據庫系統中使用BLOB作為字段類型,查詢結果會返回一個資源句柄給這個字段。

有些緩存器會有大小限制。例如mencache限制每個入口大小為1M,所以,當一個查詢結果大於該大小時,會緩存失敗。

二、緩存的運用

提示 : 因為所有這些緩存組件都從同一個基礎類 CCache 擴展而來,不需要修改使用緩存的代碼即可在不同的緩存組 
件之間切換。

緩存可以在不同的級別使用。在最低級別,我們使用緩存來存儲單個數據,比如一個變量,我們把它叫做 數據緩存 。 
往上一級,我們緩存一個由視圖腳本生成的頁面片斷。在最高級別,我們存儲整個頁面以便需要的時候直接從緩存讀 
取。 
接下來我們將闡述如何在這些級別上使用緩存。 
注意 : 按定義來講 , 緩存是一個不穩定的存儲媒介 , 它不保證緩存一定存在 —— 不管該緩存是否過期 。 所以 , 不要使用 
緩存進行持久存儲(比如,不要使用緩存來存儲 SESSION 數據 ) 。

一、數據緩存

數據緩存也就是在緩存中存儲一些 PHP 變量 , 過一會再取出來 。 緩存基礎類 CCache 提供了兩個最常用的方法 : set() 
和 get() 。 
要在緩存中存儲變量 $value ,我們選擇一個唯一 ID 並調用 set() 來存儲它: 
Yii::app()->cache->set($id, $value); 
被緩存的數據會一直保留在緩存中 , 直到因一些緩存策略而被刪除 ( 比如緩存空間滿了 , 刪除最舊的數據 ) 。 要改變這 
一行為,我們還可以在調用 set() 時加一個過期參數,這樣數據過一段時間就會自動從緩存中清除。 
// 在緩存中保留該值最多 30 秒 
Yii::app()->cache->set($id, $value, 30); 
當我們稍后需要訪問該變量時 ( 不管是不是同一 Web 請求 ) , 我們調用 get() ( 傳入 ID ) 來從緩存中獲取它 。 如果返 
回值為 false ,說明該緩存不可用,需要我們重新生成它。

$value=Yii::app()->cache->get($id);
if($value===false) { // 因為在緩存中沒找到,重新生成 $value // 再緩存一下以備下次使用 // Yii::app()->cache->set($id,$value); }

為一個要緩存的變量選擇 ID 時,確保該 ID 在應用中是唯一的。不必保證 ID 在跨應用的情況下保證唯一,因為緩 
存組件有足夠的智能來區分不同應用的緩存 ID 。 
要從緩存中刪除一個緩存值 , 調用 delete() ; 要清空所有緩存 , 調用 flush() 。 調用 flush() 時要非常小心 , 因為它會把 
其它應用的緩存也清空。 
提示 : 因為 CCache 實現了 ArrayAccess 接口,可以像數組一樣使用緩存組件。例如:

$cache=Yii::app()->cache; $cache['var1']=$value1; // 相當於 : $cache->set('var1',$value1); $value2=$cache['var2']; // 相當於 : $value2=$cache->get('var2');

緩存依賴 
除了過期設置,緩存數據還會因某些依賴條件發生改變而失效。如果我們緩存了某文件的內容,而該文件后來又被更 
新了,我們應該讓緩存中的拷貝失效,從文件中讀取最新內容(而不是從緩存 ) 。 
我們把一個依賴關系表現為一個 CCacheDependency 或它的子類的實例,調用 set() 的時候把依賴實例和要緩存的數 
據一起傳入。 
// 緩存將在 30 秒后過期 
// 也可能因依賴的文件有更新而更快失效 
Yii::app()->cache->set($id, $value, 30, new CFileCacheDependency(‘FileName’)); 
如果我們現在調用 get() 從緩存中獲取 $value ,緩存組件將檢查依賴條件。如果有變,我們會得到 false 值 —— 數據 
需要重新生成。 
下面是可用的緩存依賴的簡要說明: 
CFileCacheDependency: 該依賴因文件的最近修改時間發生改變而改變。 
CDirectoryCacheDependency: 該依賴因目錄(或其子目錄)下的任何文件發生改變而改變。 
CDbCacheDependency: 該依賴因指定的 SQL 語句的查詢結果發生改變而改變。 
CGlobalStateCacheDependency: 該依賴因指定的全局狀態值發生改變而改變。全局狀態是應用中跨請求、跨 SESSION 
的持久變量,它由 CApplication::setGlobalState() 來定義。 
CChainedCacheDependency: 該依賴因依賴鏈中的任何一環發生改變而改變。

二、片段緩存   (Fragment Caching)

片段緩存指緩存網頁某片段。例如,如果一個頁面在表中顯示每年的銷售摘要,我們可以存儲此表在緩存中,減少每 
次請求需要重新產生的時間。 
要使用片段緩存,在控制器視圖腳本中調用 CController::beginCache() 和 CController::endCache() 。這兩種方法開始和 
結束包括的頁面內容將被緩存。類似 data caching ,我們需要一個編號,識別被緩存的片段。 
… 別的 HTML 內容 …

<?php if($this->beginCache($id)) { ?> ... 被緩存的內容 ... <?php $this->endCache(); } ?> ... 別的 HTML 內容 ...

在上面的,如果 beginCache() 返回 false ,緩存的內容將此地方自動插入 ; 否則,在 if 語句內的內容將被執行並 在 
endCache() 觸發時緩存。

例如:

<?php if($this->beginCache('tagCloud', array('duration'=>60))) { ?> <?php $this->widget('TagCloud'); ?> <?php $this->endCache(); } ?> <?php if($this->beginCache('RecentPosts', array('duration'=>60))) { ?> <?php $this->widget('RecentPosts'); ?> <?php $this->endCache(); } ?> <?php if($this->beginCache('RecentComments', array('duration'=>60))) { ?> <?php $this->widget('RecentComments'); ?> <?php $this->endCache(); } ?
1. 緩存選項 (Caching Options)

當調用 beginCache() ,可以提供一個數組由緩存選項組成的作為第二個參數,以自定義片段緩存。事實上為了方便, 
beginCache() 和 endCache() 方法是 COutputCache widget 的包裝。因此 COutputCache 的所有屬性都可以在緩存選項中 
初始化。

2. 有效期( Duration )

也許是最常見的選項是 duration , 指定了內容在緩存中多久有效 。 和 CCache::set() 過期參數有點類似 。 下面的代碼緩存 
內容片段最多一小時: 
… 其他 HTML 內容 …

<?php if($this->beginCache($id, array('duration'=>3600))) { ?> ... 被緩存的內容 ... <?php $this->endCache(); } ?>

… 其他 HTML 內容 … 
如果我們不設定期限,它將默認為 60 ,這意味着 60 秒后緩存內容將無效。

3. 依賴 (Dependency)

像 data caching ,內容片段被緩存也可以有依賴。例如,文章的內容被顯示取決於文章是否被修改。 
要指定一個依賴 , 我們建立了 dependency 選項 , 可以是一個實現 ICacheDependency 的對象或可用於生成依賴對象的配 
置數組。下面的代碼指定片段內容取決 lastModified 列的值是否變化: 
… 其他 HTML 內容 …

<?php if($this->beginCache($id, array('dependency'=>array( 'class'=>'system.caching.dependencies.CDbCacheDependency', 'sql'=>'SELECT MAX(lastModified) FROM Post')))) { ?> ... 被緩存的內容 ... <?php $this->endCache(); } ?> ... 其他 HTML 內容 ...
4. 變化 (Variation)

緩存的內容可根據一些參數變化。例如,每個人的檔案都不一樣。緩存的檔案內容將根據每個人 ID 變化。這意味着 , 
當調用 beginCache() 時將用不同的 ID 。 
COutputCache 內置了這一特征,程序員不需要編寫根據 ID 變動內容的模式。以下是摘要。 
varyByRoute: 設置此選項為 true ,緩存的內容將根據 route 變化。因此,每個控制器和行動的組合將有一個單獨的緩 
存內容。 
varyBySession: 設置此選項為 true , 緩存的內容將根據 session ID 變化 。 因此 , 每個用戶會話可能會看到由緩存提供的 
不同內容。 
varyByParam: 設置此選項的數組里的名字 , 緩存的內容將根據 GET 參數的值變動 。 例如 , 如果一個頁面顯示文章的內 
容根據 id 的 GET 參數,我們可以指定 varyByParam 為 array(‘id’) ,以使我們能夠緩存每篇文章內容。如果沒有這樣的 
變化,我們只能能夠緩存某一文章。

這里有問題要討論: 
我可以正確使用以下數據調用

if($this->beginCache('good_list_manager',array('duration'=>3600,'varyByParam'=>array('id')))){?> { // ... display the content to be cached here $this->endCache(); }

但是以下代碼不能使用,因為其參數為‘Goods[name]’YII無法正確識別!

if($this->beginCache('good_list_manager',array('duration'=>3600,'varyByParam'=>array("Goods[name]"))) { // ... display the content to be cached here $this->endCache(); }

解決方法:自己建立根據 ID 變動內容的模式。

if($this->beginCache('good_list_manager_'.$_GET[Goods][name].'_'.$_GET[Goods][bn].'_'.$_GET[Goods][cat_id].'_'.$_GET[Goods][brand_id].'_'.$_GET[Goods][marketable].'_'.$_GET[Goods][cps_status].'_'.$_GET[Goods][is_check],array('duration'=>3600))){?> {  // ... display the content to be cached here $this->endCache(); }
5. 請求類型(Request Types)

有時候,我們希望片段緩存只對某些類型的請求啟用。例如,對於某張網頁上顯示表單,我們只想要緩存 initially 
requested 表單 ( 通過 GET 請求 ) 。任何隨后顯示(通過 POST 請求)的表單將不被緩存,因為表單可能包含用戶輸入。 
要做到這一點,我們可以指定 requestTypes 選項:

... 其他 HTML 內容 ...
<?php if($this->beginCache($id, array('requestTypes'=>array('GET')))) { ?> ... 被緩存的內容 ... <?php $this->endCache(); } ?> ... 其他 HTML 內容 ...
6. 嵌套緩存 (Nested Caching)

片段緩存可以嵌套。就是說一個緩存片段附在一個更大的片段緩存里。例如,意見緩存在內部片段緩存,而且它們一 
起在外部緩存中在文章內容里緩存。

... 其他 HTML 內容 ...
<?php if($this->beginCache($id1)) { ?> ... 外部被緩存內容 ... <?php if($this->beginCache($id2)) { ?> ... 內部被緩存內容 ... <?php $this->endCache(); } ?> ... 外部被緩存內容 ... <?php $this->endCache(); } ?> ... 其他 HTML 內容 ...

嵌套緩存可以設定不同的緩存選項。例如, 在上面的例子中內部緩存和外部緩存可以設置時間長短不同的持續值 。 當 
數據存儲在外部緩存無效 , 內部緩存仍然可以提供有效的內部片段 。 然而 , 反之就不行了 。 如果外部緩存包含有效的 
數據, 它會永遠保持緩存副本,即使內容中的內部緩存已經過期。

三、頁面緩存

頁面緩存指的是緩存整個頁面的內容。頁面緩存可以發生在不同的地方。例如,通過選擇適當的頁面頭,客戶端的瀏 
覽器可能會緩存網頁瀏覽有限時間。 Web 應用程序本身也可以在緩存中存儲網頁內容。 在本節中,我們側重於后一 
種辦法。 
頁面緩存可以被看作是 片段緩存 (/doc/guide/caching.fragment) 一個特殊情況 。 由於網頁內容是往往通過應用布局來生 
成,如果我們只是簡單的在布局中調用 beginCache() 和 endCache() ,將無法正常工作。這是因為布局 在 
CController::render() 方法里的加載是在頁面內容產生之后。 
緩存整個頁面,我們應該跳過產生網頁內容的動作執行。我們可以使用 COutputCache 作為動作 過濾器 
( /doc/guide/basics.controller#filter )來完成這一任務。下面的代碼演示如何配置緩存過濾器:

public function filters() { return array( array( 'system.web.widgets.COutputCache', 'duration'=>100, 'varyByParam'=>array('id'), ), ); }

上述過濾器配置會使過濾器適用於控制器中的所有行動。我們可能會限制它在一個或幾個行動通過使用插件操作器。 
更多的細節中可以看過濾器( /doc/guide/basics.controller#filter ) 。 
提示 : 我們可以使用 COutputCache 作為一個過濾器 , 因為它從 CFilterWidget 繼承過來 , 這意味着它是一個工具 (widget) 
和一個過濾器。事實上, widge 的工作方式和過濾器非常相似:工具 widget ( 過濾器 filter) 是在 action 動作里的內容執 
行前執行,在執行后結束。

四、動態內容(Dynamic Content)

當使用 fragment caching 或 page caching , 我們常常遇到的這樣的情況整個部分的輸出除了個別地方都是靜態的 。 例如 , 
幫助頁可能會顯示靜態的幫助信息,而用戶名稱顯示的是當前用戶的。 
解決這個問題,我們可以根據用戶名匹配緩存內容,但是這將是我們寶貴空間一個巨大的浪費,因為緩存除了用戶名 
其他大部分內容是相同的。我們還可以把網頁切成幾個片段並分別緩存,但這種情況會使頁面和代碼變得非常復雜。 
更好的方法是使用由 CController 提供的動態內容 dynamic content 功能 。 
動態內容是指片段輸出即使是在片段緩存包括的內容中也不會被緩存。即使是包括的內容是從緩存中取出,為了使動 
態內容在所有時間是動態的,每次都得重新生成。出於這個原因,我們要求動態內容通過一些方法或函數生成。 
調用 CController::renderDynamic() 在你想的地方插入動態內容。

... 別的 HTML 內容 ...
<?php if($this->beginCache($id)) { ?> ... 被緩存的片段內容 ... <?php $this->renderDynamic($callback); ?> ... 被緩存的片段內容 ... <?php $this->endCache(); } ?> ... 別的 HTML 內容 ...

renderDynamic復雜調用形式:

$this->renderDynamic(‘widget’,'application.extensions.uinfo’,array(‘uid’=>’hahahah!’), true); 
就相當於 
$this->widget(‘application.extensions.uinfo’,array(‘uid’=>’hahahah!’), true);

在上面的, $callback 指的是有效的 PHP 回調。它可以是指向當前控制器類的方法或者全局函數的字符串名。它也可 
以是一個數組名指向一個類的方法。其他任何的參數,將傳遞到 renderDynamic() 方法中。回調將返回動態內容而不是 
僅僅顯示它。 
三、緩存注意事項: 
請小心使用緩存,因為它增加了應用程序的性能,但降低了交互性。 (Please be careful to use the cache, because it increases the performance of the application, but decreases the interactivity.)

四、關於cacheKey的一些命名:

(1) CCacheHttpSession 中cacheKey的命名:

/**

         * Prefix to the keys for storing cached data

         */

        const CACHE_KEY_PREFIX='Yii.CCacheHttpSession.';

/**

         * Generates a unique key used for storing session data in cache.

         * @param string $id session variable name

         * @return string a safe cache key associated with the session variable name

         */

        protected function calculateKey($id)

        {

            return self::CACHE_KEY_PREFIX.$id;

        } 

使用:

/**

         * Session read handler.

         * Do not call this method directly.

         * @param string $id session ID

         * @return string the session data

         */

        public function readSession($id)

        {

                $data=$this->_cache->get($this->calculateKey($id));

                return $data===false?'':$data;

        }

(2) COutputCache 中cacheKey的命名:

const CACHE_KEY_PREFIX='Yii.COutputCache.';

/**

         * Caclulates the base cache key.

         * The calculated key will be further variated in {@link getCacheKey}.

         * Derived classes may override this method if more variations are needed.

         * @return string basic cache key without variations

         */

        protected function getBaseCacheKey()

        {

                return self::CACHE_KEY_PREFIX.$this->getId().'.';

        }

        /**

         * Calculates the cache key.

         * The key is calculated based on {@link getBaseCacheKey} and other factors, including

         * {@link varyByRoute}, {@link varyByParam} and {@link varyBySession}.

         * @return string cache key

         */

        protected function getCacheKey()

        {

                if($this->_key!==null)

                        return $this->_key;

                else

                {

                        $key=$this->getBaseCacheKey().'.';

                        if($this->varyByRoute)

                        {

                                $controller=$this->getController();

                                $key.=$controller->getUniqueId().'/';

                                if(($action=$controller->getAction())!==null)

                                        $key.=$action->getId();

                        }

                        $key.='.';

                        if($this->varyBySession)

                                $key.=Yii::app()->getSession()->getSessionID();

                        $key.='.';

                        if(is_array($this->varyByParam) && isset($this->varyByParam[0]))

                        {

                                $params=array();

                                foreach($this->varyByParam as $name)

                                {

                                        if(isset($_GET[$name]))

                                                $params[$name]=$_GET[$name];

                                        else

                                                $params[$name]='';

                                }

                                $key.=serialize($params);

                        }

                        $key.='.';

                        if($this->varyByExpression!==null)

                                $key.=$this->evaluateExpression($this->varyByExpression);

                        $key.='.';

                        return $this->_key=$key;

                }

        }

(3) CStatePersister 中cacheKey的命名:

//從持久存儲加載狀態數據。

public function load()
{
$stateFile=$this->stateFile;
if($this->cacheID!==false && ($cache=Yii::app()->getComponent($this->cacheID))!==null)
{
$cacheKey='Yii.CStatePersister.'.$stateFile; //路徑名加文件名
if(($value=$cache->get($cacheKey))!==false)
return unserialize($value);
else if(($content=@file_get_contents($stateFile))!==false)
{
$cache->set($cacheKey,$content,0,new CFileCacheDependency($stateFile));
return unserialize($content);
}
else
return null;
}
else if(($content=@file_get_contents($stateFile))!==false)
return unserialize($content);
else
return null;
}
 
//保存應用程序狀態到持久存儲。

public function save($state)
{
file_put_contents($this->stateFile,serialize($state),LOCK_EX);
}

五、關於各個組件中用到的cache部分:

(1)CUrlManager 中

const CACHE_KEY='Yii.CUrlManager.rules'; 
public $cacheID='cache';

/**

         * Processes the URL rules.

         */

        protected function processRules()

        {

                if(empty($this->rules) || $this->getUrlFormat()===self::GET_FORMAT)

                        return;

                if($this->cacheID!==false && ($cache=Yii::app()->getComponent($this->cacheID))!==null)

                {

                        $hash=md5(serialize($this->rules)); //這里寫的很巧妙,用md5生成一個固定的字符串來保存rules,相當於一個緩存依賴中的 generateDependentData() 方法,通過它來判斷是規則變了,用md5比較起來好比較

//如果緩存存在,並且緩存依賴檢查通過($data[1]===$hash),則返回緩存中存儲的rules($data[0])

                        if(($data=$cache->get(self::CACHE_KEY))!==false && isset($data[1]) && $data[1]===$hash)

                        {

                                $this->_rules=$data[0];

                                return;

                        }

                }

                foreach($this->rules as $pattern=>$route)

                        $this->_rules[]=$this->createUrlRule($route,$pattern);

                if(isset($cache))

                        $cache->set(self::CACHE_KEY,array($this->_rules,$hash)); //加$hash是為了下次訪問時判斷是否該rules變化過沒,相當於CCacheDependency中的 evaluateDependency()來判斷是否變化

        }

五、關於CDummyCache : 
CDummyCache是一個占位緩存組件。 他里面的方法和cache中的方法一樣,不過是把設置緩存的代碼給取消了,只返回操作成功后的結果,無實際操作代碼。 
CDummyCache不緩存任何數據。它使得我們總是可以配置一個緩存應用組件而不必檢查Yii::app()->cache是否為null。將CDummyCache替換為其他緩存組件,可以快速的在無緩存模式和緩存模式之間切換。 
測試階段可以用他關閉緩存,設置如下: 
//緩存配置

'cache' => array(
'class' => 'CDummyCache',
),

六、關於COutputCache: 
想弄清CoutputCache,不得不知CController中的相關方法: 
CController用到的關於cache的內容有:

private $_cachingStack; //存儲着關於各個CoutputCache的一個棧

/**

* Renders dynamic content returned by the specified callback.
* This method is used together with {@link COutputCache}. Dynamic contents
* will always show as their latest state even if the content surrounding them is being cached.
* This is especially useful when caching pages that are mostly static but contain some small
* dynamic regions, such as username or current time.
* We can use this method to render these dynamic regions to ensure they are always up-to-date.
*(輸出一個由特殊回調函數返回的動態內容,這個方法被CoutputCache調用,動態內容始終保持最新的數據,盡管他所在的內容
被緩存了。)
* The first parameter to this method should be a valid PHP callback, while the rest parameters
* will be passed to the callback. (這個函數的第一個參數是一個回調函數,其他的參數會作為參數傳遞給這個回調函數)
*
* Note, the callback and its parameter values will be serialized and saved in cache.
* Make sure they are serializable. (注意:回調函數和傳遞給回調函數的參數將要被序列化存儲到cache中,所以,
請確保他們是可序列化的)
*
* @param callback $callback a PHP callback which returns the needed dynamic content.
* When the callback is specified as a string, it will be first assumed to be a method of the current
* controller class. If the method does not exist, it is assumed to be a global PHP function. (如果這個回調函數是字符串,他會假設這個函數是當前控制器里的一個方法,如果這個方法不存在,就假設這個這個方法是個全局的 函數)
* Note, the callback should return the dynamic content instead of echoing it. (這個回調函數應該返回動態的內容,而不是輸出他)
*/
public function renderDynamic($callback)
{
$n=count($this->_dynamicOutput);
echo "<###dynamic-$n###>";
$params=func_get_args();
array_shift($params);
$this->renderDynamicInternal($callback,$params);
}
/**
* This method is internally used. (通過回調函數調用把處理結果放到_dynamicOutput數組中)
* @param callback $callback a PHP callback which returns the needed dynamic content.
* @param array $params parameters passed to the PHP callback
* @see renderDynamic
*/
public function renderDynamicInternal($callback,$params)
{
$this->recordCachingAction('','renderDynamicInternal',array($callback,$params));
if(is_string($callback) && method_exists($this,$callback))
$callback=array($this,$callback);
$this->_dynamicOutput[]=call_user_func_array($callback,$params);
}

/**

* Records a method call when an output cache is in effect.
* When the content is served from the output cache, the recorded
* method will be re-invoked.
* @param string $context a property name of the controller. It refers to an object
* whose method is being called. If empty it means the controller itself.(需要記錄的方法 所在的對象,如果為空,則默認為當前的控制器這個對象)
* @param string $method the method name (需要記錄的方法)
* @param array $params parameters passed to the method (傳遞給這個方法的參數)
* @see COutputCache
*/
public function recordCachingAction($context,$method,$params)
{
if($this->_cachingStack) // record only when there is an active output cache
{
foreach($this->_cachingStack as $cache)
$cache->recordAction($context,$method,$params);
}
}

/**

* Postprocesses the dynamic output. (處理$output中插入的動態內容,處理過的內容是占位符被替換了真是的內容)
* This method is internally used. Do not call this method directly.
* @param string $output output to be processed
* @return string the processed output
*/
public function processDynamicOutput($output)
{
if($this->_dynamicOutput)
{
        //調用replaceDynamicOutput來處理每個匹配
$output=preg_replace_callback('/<###dynamic-(\d+)###>/',array($this,'replaceDynamicOutput'),$output);
}
return $output;
}

/**

* Replaces the dynamic content placeholders with actual content.
* This is a callback function used internally. (替換匹配到的每個動態內容占位符,由processDynamicOutput 調用)
* @param array $matches matches
* @return string the replacement
* @see processOutput
*/
protected function replaceDynamicOutput($matches)
{
$content=$matches[0];  //所有的內容
if(isset($this->_dynamicOutput[$matches[1]]))
{
$content=$this->_dynamicOutput[$matches[1]];
$this->_dynamicOutput[$matches[1]]=null;
}
return $content;
}
/**
* @param boolean $createIfNull whether to create a stack if it does not exist yet. Defaults to true. (創建一個存儲COutputCache對象的棧)
* @return CStack stack of {@link COutputCache} objects
*/
public function getCachingStack($createIfNull=true)
{
if(!$this->_cachingStack)
$this->_cachingStack=new CStack;
return $this->_cachingStack;
}

/**

* Returns whether the caching stack is empty. (判斷存儲CoutputCache對象的棧是否為空)
* @return boolean whether the caching stack is empty. If not empty, it means currently there are
* some output cache in effect. Note, the return result of this method may change when it is
* called in different output regions, depending on the partition of output caches.
*/
public function isCachingStackEmpty()
{
return $this->_cachingStack===null || !$this->_cachingStack->getCount();
}

/**

* Postprocesses the output generated by {@link render()}.
* This method is invoked at the end of {@link render()} and {@link renderText()}.
* If there are registered client scripts, this method will insert them into the output
* at appropriate places. If there are dynamic contents, they will also be inserted.
* This method may also save the persistent page states in hidden fields of
* stateful forms in the page.
* @param string $output the output generated by the current action
* @return string the output that has been processed.
*/
public function processOutput($output)
{
Yii::app()->getClientScript()->render($output); //插入靜態的js,css文件等
// if using page caching, we should delay dynamic output replacement
if($this->_dynamicOutput!==null && $this->isCachingStackEmpty())
{
$output=$this->processDynamicOutput($output);
$this->_dynamicOutput=null;
}
if($this->_pageStates===null)
$this->_pageStates=$this->loadPageStates();
if(!empty($this->_pageStates))
$this->savePageStates($this->_pageStates,$output);
return $output;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM