magento 1 企业版的一些页面使用了 fpc ,如果我们调用一个产生随机字符串的方法,那么在 fpc 开着的情况下,我们每次会得到相同的字符串,而不是随机的字符串。那么我们怎样才能每次都得到随机的字符串呢? 我们以 Magento 1.14.1.0 为例,创建一个模块 VendorName_TestModule 做实验。 File: app\etc\modules\VendorName_TestModule.xml
1 2 3 4 5 6 7 8 9
| <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <?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
1 2 3 4 5 6 7 8
| <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?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
1 2 3 4
| This is from block <br/> <?php echo $this->getRandString() ?> <br/>
|
缓存原因,每次刷新页面得到的都是相同字符串。 
每次刷新获得随机字符串
File: app\code\local\VendorName\TestModule\etc\cache.xml
1 2 3 4 5 6 7 8 9 10 11
| <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <?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
1 2 3 4 5
| 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?