magento 1 企业版的一些页面使用了 fpc ,如果我们调用一个产生随机字符串的方法,那么在 fpc 开着的情况下,我们每次会得到相同的字符串,而不是随机的字符串。那么我们怎样才能每次都得到随机的字符串呢?
我们以 Magento 1.14.1.0 为例,创建一个模块 VendorName_TestModule 做实验。
File: app\etc\modules\VendorName_TestModule.xml
<?xml version="1.0"?>
<config>
<modules>
<VendorName_TestModule>
<active>true</active>
<codePool>local</codePool>
</VendorName_TestModule>
</modules>
</config>
File: app\code\local\VendorName\TestModule\etc\config.xml
<?xml version="1.0"?>
<config>
<modules>
<VendorName_TestModule>
<version>0.1.0</version>
</VendorName_TestModule>
</modules>
<global>
<blocks>
<testmodule>
<class>VendorName_TestModule_Block</class>
</testmodule>
</blocks>
<helpers>
<testmodule>
<class>VendorName_TestModule_Helper</class>
</testmodule>
</helpers>
</global>
<frontend>
<layout>
<updates>
<testmodule>
<file>testmodule.xml</file>
</testmodule>
</updates>
</layout>
</frontend>
</config>
File:app\design\frontend\rwd\default\layout\testmodule.xml
<?xml version="1.0"?>
<layout version="0.1.0">
<default>
<reference name="content">
<block type="testmodule/fpctest" name="fpctest" template="testmodule/fpctest.phtml" after="-"/>
</reference>
</default>
</layout>
File: app\code\local\VendorName\TestModule\Block\Fpctest.php
<?php
class VendorName_TestModule_Block_Fpctest extends Mage_Core_Block_Template
{
protected function _construct()
{
$this->setTemplate('testmodule/fpctest.phtml');
}
public function getRandString($length = 4)
{
$chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
$str = '';
for ($i = 0; $i < $length; ++$i) {
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}
}
File: app\design\frontend\rwd\default\template\testmodule\fpctest.phtml
This is from block
<br/>
<?php echo $this->getRandString() ?>
<br/>
缓存原因,每次刷新页面得到的都是相同字符串。
每次刷新获得随机字符串
File: app\code\local\VendorName\TestModule\etc\cache.xml
<?xml version="1.0"?>
<config>
<placeholders>
<testmodule>
<block>testmodule/fpctest</block>
<placeholder>TESTMODULE_CACHE</placeholder>
<container>VendorName_TestModule_Model_Container_Fpctest</container>
<cache_life>5</cache_life>
</testmodule>
</placeholders>
</config>
File: app\code\local\VendorName\TestModule\Model\Container\Fpctest.php
<?php
class VendorName_TestModule_Model_Container_Fpctest extends Enterprise_PageCache_Model_Container_Abstract{
protected function _renderBlock()
{
$block = $this->_getPlaceHolderBlock();
return $block->toHtml();
}
protected function _getCacheId()
{
return 'TESTMODULE_CACHE' . md5($this->_placeholder->getAttribute('cache_id')).'_'.$this->_getIdentifier();
}
protected function _getIdentifier()
{
return $this->_getCookieValue(Enterprise_PageCache_Model_Cookie::COOKIE_CUSTOMER, '');
}
public function applyWithoutApp(&$content)
{
// by default will attempt to load block from cache.
return false; // always dynamic
}
}
记得刷新缓存
fpc 页面有 4 种状态
- Page in cache, no dynamic blocks
- Page in cache, dynamic blocks cached
- Page in cache, dynamic blocks not cached
- Page not in cache
当我们刷新缓存后,再请求页面,我们的页面处于状态 4 ,执行过程就是正常的过程。但是 magento 会根据 cache.xml ,用 <!--{MY_PLACEHOLDER_NAME_hash}-->rendered block content<!--/{MY_PLACEHOLDER_NAME_hash}-->
替代 block 的内容缓存起来, block 也被缓存起来。在最终页面发送之前,再把上面的 placeholder 替换成缓存的 block 内容。缓存的 block 内容会有过期时间。
所以我们第二次刷新的时候,页面处于状态 2 ,那么这个时候会请求 container 的 applyWithoutApp($content)。按照道理应该返回 block 的内容(如果 block 没有过期的话)。但是我们返回的是 false ,这时候就是状态 3 了,请求转向 pagecache/request/process
最后来到了 container 的 applyWithApp() ,而 applyWithApp() 则会调用 $this->_renderBlock()。所以我们得到了随机的字符串。
下面讨论下过期时间
File: app\code\local\VendorName\TestModule\Model\Container\Fpctest.php
<?php
class VendorName_TestModule_Model_Container_Fpctest extends Enterprise_PageCache_Model_Container_Abstract{
protected function _renderBlock()
{
$block = $this->_getPlaceHolderBlock();
return $block->toHtml();
}
protected function _getCacheId()
{
return 'TESTMODULE_CACHE' . md5($this->_placeholder->getAttribute('cache_id')).'_'.$this->_getIdentifier();
}
protected function _getIdentifier()
{
return $this->_getCookieValue(Enterprise_PageCache_Model_Cookie::COOKIE_CUSTOMER, '');
}
protected function _saveCache($data, $id, $tags = array(), $lifetime = 5)
{
parent::_saveCache($data, $id, $tags, $lifetime);
}
}
刷新缓存,我们会发现随机字符串在 5 s 内保持一样,5 s 后由于缓存过期,拿到新的值。如果 $lifetime = 0 那么就会一直过期。
充分利用缓存, placeholder 带参
在 fpc 上打孔,不能直接使用 Mage::registry() 和 Mage::app()->getFrontController()->getAction()->getFullActionName() 这种,因为在状态 3 的时候,页面是转向 pagecache/request/process
,它并不是一个通常的请求。如果你这么用了,那么就会发现,结果很怪异,访问 A 页面正常,访问下 B 页面也正常,再回到 A 页面,哎~ ,怎么把 B 的结果带过来了。。
下面我们让 block 缓存起来,通过 placeholder 带参来解决上面提到的问题。
File: app\code\local\VendorName\TestModule\Block\Fpctest.php
<?php
class VendorName_TestModule_Block_Fpctest extends Mage_Core_Block_Template
{
// !important
public function getCacheKeyInfo() {
$info = parent::getCacheKeyInfo();
$info['page_type'] = $this->getPagetype();
return $info;
}
protected function _construct()
{
$this->setTemplate('testmodule/fpctest.phtml');
}
public function getRandString($length = 4)
{
$chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
$str = '';
for ($i = 0; $i < $length; ++$i) {
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}
public function getPagetype()
{
if ($this->hasData('page_type')) {
return $this->getData('page_type');
} else {
return Mage::app()->getFrontController()->getAction()->getFullActionName();
}
}
}
File: app\code\local\VendorName\TestModule\Model\Container\Fpctest.php
<?php
class VendorName_TestModule_Model_Container_Fpctest extends Enterprise_PageCache_Model_Container_Abstract{
protected function _renderBlock()
{
$block = $this->_getPlaceHolderBlock();
$pageType = $this->_placeholder->getAttribute('page_type');
$block->setPageType($pageType);
return $block->toHtml();
}
protected function _getCacheId()
{
return 'TESTMODULE_CACHE' . md5($this->_placeholder->getAttribute('cache_id')).'_'.$this->_getIdentifier();
}
protected function _getIdentifier()
{
return $this->_getCookieValue(Enterprise_PageCache_Model_Cookie::COOKIE_CUSTOMER, '');
}
protected function _saveCache($data, $id, $tags = array(), $lifetime = 5)
{
parent::_saveCache($data, $id, $tags, $lifetime);
}
}
File: app\design\frontend\rwd\default\template\testmodule\fpctest.phtml
This is from block
<br/>
<?php echo $this->getRandString() ?>
<br/>
<?php echo $this->getPagetype();?>
参考链接
How do I include a dynamic block in the product page with full page caching turned on?
Overcoming Magento’s full-page cache through hole punching
what do cache.xml in etc folder of any module?