Magento Enterprise:フルページキャッシュとは何で、なぜ必要なのか

Magento Enterprise-全ペジキッチャッシュ



Magento Enterprise:フルページキャッシュとは何か、なぜ必要なのか。





Magentoに精通している人にとって、このeコマースエンジンがハードウェアに非常に厳しいことは周知の事実です。 しかし、このオンラインストアの開発者はこの問題を解決しようとし、多くの異なる種類の「アクセラレータ」を思い付きました。 長すぎると、Magentoはエンドユーザーにページをレンダリングします。 このような「アクセラレータ」には、キャッシュ、インデックス、コンパイル、JS / CSSの1つの圧縮ファイルへの結合などがあります。



Magento Enterpriseの主な「トリック」の1つは、フルページキャッシュ(以下FPC)です。 この「トリック」は、Magento Enterpriseパッケージの一部であるEnterprise_PageCacheモジュールによって実装されます。



この記事では、執筆時点のMagento Enterpriseの最新バージョン1.13.1について説明しています。



FPCを使用すると、事実上サーバーをロードすることなく、サーバーに数ミリ秒でページを提供できます。 製品ページがサーバーでレンダリングされる時間を測定しました(作業中のプロジェクトの1つで)。結果は次のとおりです。



Magento Enterprise-FPC





なぜそんなに大きな違いがあるのですか? それを理解しましょう。



FPCキャッシュの内容と時期



名前が示すように、フルページキャッシュはページ全体をキャッシュします。 ただし、すべてのページがキャッシュされるわけではありません。 少なくとも意味がないので。 デフォルトでは、製品ページ、カテゴリページ、CMSページ、および404エラーのあるページのみがキャッシュされます(ページが見つかりません)。 これを確認するには、モジュール構成(config.xml)を調べます。



<frontend> <cache> <requests> <_no_route>enterprise_pagecache/processor_noroute</_no_route> <cms>enterprise_pagecache/processor_default</cms> <catalog> <category> <view>enterprise_pagecache/processor_category</view> </category> </catalog> <catalog> <product> <view>enterprise_pagecache/processor_product</view> </product> </catalog> </requests> </cache> </frontend>
      
      







フロントエンド/キャッシュ/リクエストセクションでは、モジュールコントローラーのfrontNameがここで指定されます(その値は、パスに沿ってモジュール構成に保存されます:frontend / routers / route_name / args / frontName)、コントローラーとアクション(コントローラーとアクションはオプションのパラメーターです)。 そして、送信される値はリクエスト(リクエスト)プロセッサです。



モジュールのコントローラー/コントローラー/アクションを同じ方法でキャッシュするように強制できます。Enterprise_PageCacheモジュールと同じ方法で、目的のセクションにデータを追加するだけです。 例:

 <frontend> <cache> <requests> <productinfo> <index>enterprise_pagecache/processor_default</index> </productinfo> </requests> </cache> </frontend>
      
      







FPCがページをキャッシュしない条件はいくつかあります。 たとえば、HTTPSページ、no_cache GETパラメーターを持つページはキャッシュされません。 Enterprise_PageCache_Model_ProcessorクラスのcanProcessRequestおよびisAllowedメソッド:



 /** * Do basic validation for request to be cached * * @param Zend_Controller_Request_Http $request * @return bool */ public function canProcessRequest(Zend_Controller_Request_Http $request) { $res = $this->isAllowed(); $res = $res && Mage::app()->useCache('full_page'); if ($request->getParam('no_cache')) { $res = false; } if ($res) { $maxDepth = Mage::getStoreConfig(self::XML_PATH_ALLOWED_DEPTH); $queryParams = $request->getQuery(); unset($queryParams[Enterprise_PageCache_Model_Cache::REQUEST_MESSAGE_GET_PARAM]); $res = count($queryParams)<=$maxDepth; } if ($res) { $multicurrency = Mage::getStoreConfig(self::XML_PATH_CACHE_MULTICURRENCY); if (!$multicurrency && !empty($_COOKIE['currency'])) { $res = false; } } return $res; }
      
      





 /** * Check if processor is allowed for current HTTP request. * Disable processing HTTPS requests and requests with "NO_CACHE" cookie * * @return bool */ public function isAllowed() { if (!$this->_requestId) { return false; } if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') { return false; } if (isset($_COOKIE['NO_CACHE'])) { return false; } if (isset($_GET['no_cache'])) { return false; } if (isset($_GET[Mage_Core_Model_Session_Abstract::SESSION_ID_QUERY_PARAM])) { return false; } if (!Mage::app()->useCache('full_page')) { return false; } return true; }
      
      







FPCはどのように機能しますか?



ページがFPCキャッシュでキャッシュされている場合、FPCは標準ブロックキャッシュを無効にします。 (イベント)controller_action_predispatchイベントが発生したときに起動するEnterprise_PageCache_Model_ObserverクラスのprocessPreDispatchメソッドを参照してください。



 /** * Check when cache should be disabled * * @param Varien_Event_Observer $observer * @return Enterprise_PageCache_Model_Observer */ public function processPreDispatch(Varien_Event_Observer $observer) { if (!$this->isCacheEnabled()) { return $this; } $action = $observer->getEvent()->getControllerAction(); /* @var $request Mage_Core_Controller_Request_Http */ $request = $action->getRequest(); $noCache = $this->_getCookie()->get(Enterprise_PageCache_Model_Processor::NO_CACHE_COOKIE); if ($noCache) { Mage::getSingleton('catalog/session')->setParamsMemorizeDisabled(false); $this->_getCookie()->renew(Enterprise_PageCache_Model_Processor::NO_CACHE_COOKIE); } elseif ($action) { Mage::getSingleton('catalog/session')->setParamsMemorizeDisabled(true); } /** * Check if request will be cached */ if ($this->_processor->canProcessRequest($request)) { Mage::app()->getCacheInstance()->banUse(Mage_Core_Block_Abstract::CACHE_GROUP); } $this->_getCookie()->updateCustomerCookies(); return $this; }
      
      







HTTPリクエストごとに、FPCはキャッシュ識別子を生成します。キャッシュ識別子は、ページをキャッシュに保存するために使用されます。 キャッシュ識別子は、カスタムメーターが承認されているかどうか、どのセグメントに属しているか、属するユーザーグループなど、いくつかのパラメーターに依存します。この識別子の生成方法は、Enterprise_PageCache_Model_Processorクラスの_createRequestIdsメソッドで確認できます。



 /** * Populate request ids * @return Enterprise_PageCache_Model_Processor */ protected function _createRequestIds() { $uri = $this->_getFullPageUrl(); //Removing get params $pieces = explode('?', $uri); $uri = array_shift($pieces); /** * Define COOKIE state */ if ($uri) { if (isset($_COOKIE['store'])) { $uri = $uri.'_'.$_COOKIE['store']; } if (isset($_COOKIE['currency'])) { $uri = $uri.'_'.$_COOKIE['currency']; } if (isset($_COOKIE[Enterprise_PageCache_Model_Cookie::COOKIE_CUSTOMER_GROUP])) { $uri .= '_' . $_COOKIE[Enterprise_PageCache_Model_Cookie::COOKIE_CUSTOMER_GROUP]; } if (isset($_COOKIE[Enterprise_PageCache_Model_Cookie::COOKIE_CUSTOMER_LOGGED_IN])) { $uri .= '_' . $_COOKIE[Enterprise_PageCache_Model_Cookie::COOKIE_CUSTOMER_LOGGED_IN]; } if (isset($_COOKIE[Enterprise_PageCache_Model_Cookie::CUSTOMER_SEGMENT_IDS])) { $uri .= '_' . $_COOKIE[Enterprise_PageCache_Model_Cookie::CUSTOMER_SEGMENT_IDS]; } if (isset($_COOKIE[Enterprise_PageCache_Model_Cookie::IS_USER_ALLOWED_SAVE_COOKIE])) { $uri .= '_' . $_COOKIE[Enterprise_PageCache_Model_Cookie::IS_USER_ALLOWED_SAVE_COOKIE]; } $designPackage = $this->_getDesignPackage(); if ($designPackage) { $uri .= '_' . $designPackage; } } $this->_requestId = $uri; $this->_requestCacheId = $this->prepareCacheId($this->_requestId); return $this; }
      
      







FPCはプレースホルダーを使用します。 これらは、モジュールのetcフォルダーにあるcache.xml設定ファイルを使用してMagentoに追加されます。 そして、プレースホルダーはプレースホルダーのコンテナーによって処理されます。 コンテナは、FPCレンダリング中にプレースホルダーが処理するクラスです(FPCキャッシュを使用してブロックをレンダリングするプロセスは、ブロックの通常のレンダリングとは異なります)。 cache.xmlファイルの例:



 <?xml version="1.0" encoding="UTF-8"?> <config> <placeholders> <turnkeye_popup> <block>turnkeye_popup/popup</block> <placeholder>POPUP</placeholder> <container>Turnkeye_Popup_Model_PageCache_Container_Popup</container> <cache_lifetime>86400</cache_lifetime> </turnkeye_popup> </placeholders> </config>
      
      







プレースホルダーの構成について簡単に説明します。





レンダリング中、FPCが有効になっている場合、各ブロックのコンテンツはプレースホルダーでラップされてキャッシュされます。core_block_abstract_to_html_afterイベントが発生すると、Enterprise_PageCache_Model_ObserverクラスのrenderBlockPlaceholderメソッドが実行されます。



 /** * Render placeholder tags around the block if needed * * @param Varien_Event_Observer $observer * @return Enterprise_PageCache_Model_Observer */ public function renderBlockPlaceholder(Varien_Event_Observer $observer) { if (!$this->_isEnabled) { return $this; } $block = $observer->getEvent()->getBlock(); $transport = $observer->getEvent()->getTransport(); $placeholder = $this->_config->getBlockPlaceholder($block); if ($transport && $placeholder && !$block->getSkipRenderTag()) { $blockHtml = $transport->getHtml(); $request = Mage::app()->getFrontController()->getRequest(); /** @var $processor Enterprise_PageCache_Model_Processor_Default */ $processor = $this->_processor->getRequestProcessor($request); if ($processor && $processor->allowCache($request)) { $container = $placeholder->getContainerClass(); if ($container && !Mage::getIsDeveloperMode()) { $container = new $container($placeholder); $container->setProcessor(Mage::getSingleton('enterprise_pagecache/processor')); $container->setPlaceholderBlock($block); $container->saveCache($blockHtml); } } $blockHtml = $placeholder->getStartTag() . $blockHtml . $placeholder->getEndTag(); $transport->setHtml($blockHtml); } return $this; }
      
      







ここで、$ Bプレースホルダーは、getBlockPlaceholderメソッドによってEnterprise_PageCache_Model_Configクラスを使用して取得されます。



 /** * Create placeholder object based on block information * * @param Mage_Core_Block_Abstract $block * @return Enterprise_PageCache_Model_Container_Placeholder */ public function getBlockPlaceholder($block) { $this->_initPlaceholders(); $type = $block->getType(); if (isset($this->_placeholders[$type])) { $placeholderData = false; foreach ($this->_placeholders[$type] as $placeholderInfo) { if (!empty($placeholderInfo['name'])) { if ($placeholderInfo['name'] == $block->getNameInLayout()) { $placeholderData = $placeholderInfo; } } else { $placeholderData = $placeholderInfo; } } if (!$placeholderData) { return false; } $placeholder = $placeholderData['code'] . ' container="' . $placeholderData['container'] . '"' . ' block="' . get_class($block) . '"'; $placeholder.= ' cache_id="' . $block->getCacheKey() . '"'; if (!empty($placeholderData['cache_lifetime'])) { $placeholder .= ' cache_lifetime="' . $placeholderData['cache_lifetime'] . '"'; } foreach ($block->getCacheKeyInfo() as $k => $v) { if (is_string($k) && !empty($k)) { $placeholder .= ' ' . $k . '="' . $v . '"'; } } $placeholder = Mage::getModel('enterprise_pagecache/container_placeholder', $placeholder); return $placeholder; } return false; }
      
      







Enterprise_PageCache_Model_Container_Placeholderクラスのコンストラクター:



 /** * Class constructor. * Initialize placeholder name and attributes based on definition * * @param string $definition */ public function __construct($definition) { if ($definition && array_key_exists($definition, self::$_definitionMap)) { $definition = self::$_definitionMap[$definition]; } $this->_definition = $definition; $definition = explode(' ', $definition); $this->_name = $definition[0]; $count = count($definition); if ($count>1) { for ($i=1; $i_attributes[$info[0]] = isset($info[1]) ? trim($info[1], '"\'') : null; } } }
      
      







将来、コンテナクラスでは、getAttributeメソッドを使用してブロック属性を取得できるようになります。



 /** * Get attribute by specific code * @param $code string * @return string */ public function getAttribute($code) { return isset($this->_attributes[$code]) ? $this->_attributes[$code] : null; }
      
      







コンテンツブロックでプレースホルダーをラップする方法:



 /** * Retrieve placeholder definition hash * * @return string */ protected function _getDefinitionHash() { $definition = $this->getDefinition(); $result = array_search($definition, self::$_definitionMap); if ($result === false) { $result = $this->getName() . '_' . md5($definition); self::$_definitionMap[$result] = $definition; } return $result; } /** * Get placeholder start tag for block html generation * * @return string */ public function getStartTag() { return '<!--{' . $this->_getDefinitionHash() . '}-->'; } /** * Get placeholder end tag for block html generation * * @return string */ public function getEndTag() { return '<!--/{' . $this->_getDefinitionHash() . '}-->'; }
      
      







ブロックコンテンツをラップするプレースホルダーの例:



 < !--{TOPMENU_21c7f778e21d072d331836703b6295f5}--> < div class="nav-container"> < ul id="nav"> < li class="level0 nav-1 first last level-top"> < a class="level-top" href="http://example.com/test-category.html"> < span>Test category</span> </a> </li> </ul> </div> < !--/{TOPMENU_21c7f778e21d072d331836703b6295f5}-->
      
      







要求への応答を送信する前に、controller_front_send_response_beforeイベントが発生し、Enterprise_PageCache_Model_ObserverクラスのcacheResponseメソッドが実行され、必要に応じてページがキャッシュに保存されます。 CacheResponseメソッドコード:



 /** * Save page body to cache storage * * @param Varien_Event_Observer $observer * @return Enterprise_PageCache_Model_Observer */ public function cacheResponse(Varien_Event_Observer $observer) { if (!$this->isCacheEnabled()) { return $this; } $frontController = $observer->getEvent()->getFront(); $request = $frontController->getRequest(); $response = $frontController->getResponse(); $this->_saveDesignException(); $this->_processor->processRequestResponse($request, $response); return $this; }
      
      







ここで、processRequestResponseメソッドは、必要に応じてページ全体と必要なデータをキャッシュに保存します。



 /** * Process response body by specific request * * @param Zend_Controller_Request_Http $request * @param Zend_Controller_Response_Http $response * @return Enterprise_PageCache_Model_Processor */ public function processRequestResponse(Zend_Controller_Request_Http $request, Zend_Controller_Response_Http $response ) { // we should add original path info tag as another way we can't drop some entities from cron job $this->addRequestTag(Enterprise_PageCache_Helper_Url::prepareRequestPathTag($request->getOriginalPathInfo())); $cacheInstance = Enterprise_PageCache_Model_Cache::getCacheInstance(); /** * Basic validation for request processing */ if ($this->canProcessRequest($request)) { $processor = $this->getRequestProcessor($request); if ($processor && $processor->allowCache($request)) { $this->setMetadata('cache_subprocessor', get_class($processor)); $cacheId = $this->prepareCacheId($processor->getPageIdInApp($this)); $content = $processor->prepareContent($response); /** * Replace all occurrences of session_id with unique marker */ Enterprise_PageCache_Helper_Url::replaceSid($content); Enterprise_PageCache_Helper_Form_Key::replaceFormKey($content); if (function_exists('gzcompress')) { $content = gzcompress($content); } $contentSize = strlen($content); $currentStorageSize = (int) $cacheInstance->load(self::CACHE_SIZE_KEY); if (Mage::getStoreConfig(Enterprise_PageCache_Model_Processor::XML_PATH_CACHE_DEBUG)) { $response->setBody(implode(', ', $this->getRequestTags()) . $response->getBody()); } $maxSizeInBytes = Mage::getStoreConfig(self::XML_PATH_CACHE_MAX_SIZE) * 1024 * 1024; if ($currentStorageSize >= $maxSizeInBytes) { Mage::app()->getCacheInstance()->invalidateType('full_page'); return $this; } $cacheInstance->save($content, $cacheId, $this->getRequestTags()); $cacheInstance->save( $currentStorageSize + $contentSize, self::CACHE_SIZE_KEY, $this->getRequestTags() ); /* * Save design change in cache */ $designChange = Mage::getSingleton('core/design'); if ($designChange->getData()) { $cacheInstance->save( serialize($designChange->getData()), $this->getRequestCacheId() . self::DESIGN_CHANGE_CACHE_SUFFIX, $this->getRequestTags() ); } // save response headers $this->setMetadata('response_headers', $response->getHeaders()); // save original routing info $this->setMetadata('routing_aliases', Mage::app()->getRequest()->getAliases()); $this->setMetadata('routing_requested_route', Mage::app()->getRequest()->getRequestedRouteName()); $this->setMetadata('routing_requested_controller', Mage::app()->getRequest()->getRequestedControllerName()); $this->setMetadata('routing_requested_action', Mage::app()->getRequest()->getRequestedActionName()); $this->setMetadata('sid_cookie_name', Mage::getSingleton('core/session')->getSessionName()); Mage::dispatchEvent('pagecache_processor_metadata_before_save', array('processor' => $this)); $this->_saveMetadata(); } if (isset($_GET[Mage_Core_Model_Session_Abstract::SESSION_ID_QUERY_PARAM])) { Mage::getSingleton('enterprise_pagecache/cookie')->updateCustomerCookies(); Mage::getModel('enterprise_pagecache/observer')->updateCustomerProductIndex(); } } return $this; }
      
      







ここで、非常に重要な点は、ページキャッシュを保存する前に、プレースホルダーを持つブロックのコンテンツがブロックの説明に置き換えられることです:$ content = $ processor-> prepareContent($ response);。 PrepareContentメソッド:



 /** * Prepare response body before caching * * @param Zend_Controller_Response_Http $response * @return string */ public function prepareContent(Zend_Controller_Response_Http $response) { return $this->replaceContentToPlaceholderReplacer($response->getBody()); }
      
      







ReplaceContentToPlaceholderReplacerメソッド:



 /** * Replace block content to placeholder replacer * * @param string $content * @return string */ public function replaceContentToPlaceholderReplacer($content) { $placeholders = array(); preg_match_all( Enterprise_PageCache_Model_Container_Placeholder::HTML_NAME_PATTERN, $content, $placeholders, PREG_PATTERN_ORDER ); $placeholders = array_unique($placeholders[1]); try { foreach ($placeholders as $definition) { $this->_placeholder = Mage::getModel('enterprise_pagecache/container_placeholder', $definition); $content = preg_replace_callback($this->_placeholder->getPattern(), array($this, '_getPlaceholderReplacer'), $content); } $this->_placeholder = null; } catch (Exception $e) { $this->_placeholder = null; throw $e; } return $content; }
      
      







その結果、ページキャッシュには、ブロックコンテンツではなくブロックに関する情報が含まれます。 この情報は、将来のブロックのレンダリングに役立ちます。 以下は、FPCがレンダリング中にブロックのコンテンツで置き換えるような情報の例です。



 < !--{POPUP container="Turnkeye_Popup_Model_PageCache_Container_Popup" block="Turnkeye_Popup_Block_Popup" cache_id="c3b32091a1ebd3b276a8fd70496a8e6da20865d0" cache_lifetime="86400" template="turnkeye/popup/popup.phtml" handles="a:2:{i:0;s:15:"cms_index_index";i:1;s:8:"cms_page";}" customer_segment_ids="a:1:{i:0;i:0;}" popup_ids="a:1:{i:0;s:1:"1";}" excluded_popup_ids="a:1:{i:0;i:1;}"}-->
      
      







次回このページが読み込まれると、Magentoはこのページをキャッシュから読み込みます(Enterprise_PageCache_Model_ProcessorクラスのextractContentメソッドを参照)。 キャッシュが空の場合、通常のレンダリングはMagentoの初期化(FPCが存在しないかのように)で行われ、ページキャッシュに保存されます。 ページがキャッシュ内にある場合、FPCはこのキャッシュからコンテンツを処理します。



Enterprise_PageCache_Model_Processorクラスの_processContentおよび_processContainersメソッド:



 /** * Determine and process all defined containers. * Direct request to pagecache/request/process action if necessary for additional processing * * @param string $content * @return string|false */ protected function _processContent($content) { $containers = $this->_processContainers($content); $isProcessed = empty($containers); // renew session cookie $sessionInfo = Enterprise_PageCache_Model_Cache::getCacheInstance()->load($this->getSessionInfoCacheId()); if ($sessionInfo) { $sessionInfo = unserialize($sessionInfo); foreach ($sessionInfo as $cookieName => $cookieInfo) { if (isset($_COOKIE[$cookieName]) && isset($cookieInfo['lifetime']) && isset($cookieInfo['path']) && isset($cookieInfo['domain']) && isset($cookieInfo['secure']) && isset($cookieInfo['httponly']) ) { $lifeTime = (0 == $cookieInfo['lifetime']) ? 0 : time() + $cookieInfo['lifetime']; setcookie($cookieName, $_COOKIE[$cookieName], $lifeTime, $cookieInfo['path'], $cookieInfo['domain'], $cookieInfo['secure'], $cookieInfo['httponly'] ); } } } else { $isProcessed = false; } if (isset($_COOKIE[Enterprise_PageCache_Model_Cookie::COOKIE_FORM_KEY])) { $formKey = $_COOKIE[Enterprise_PageCache_Model_Cookie::COOKIE_FORM_KEY]; } else { $formKey = Enterprise_PageCache_Helper_Data::getRandomString(16); Enterprise_PageCache_Model_Cookie::setFormKeyCookieValue($formKey); } Enterprise_PageCache_Helper_Form_Key::restoreFormKey($content, $formKey); /** * restore session_id in content whether content is completely processed or not */ $sidCookieName = $this->getMetadata('sid_cookie_name'); $sidCookieValue = $sidCookieName && isset($_COOKIE[$sidCookieName]) ? $_COOKIE[$sidCookieName] : ''; // XSS vulnerability protection provided by htmlspcialchars call - escape & " ' < > chars Enterprise_PageCache_Helper_Url::restoreSid($content, htmlspecialchars($sidCookieValue, ENT_QUOTES)); if ($isProcessed) { return $content; } else { Mage::register('cached_page_content', $content); Mage::register('cached_page_containers', $containers); Mage::app()->getRequest() ->setModuleName('pagecache') ->setControllerName('request') ->setActionName('process') ->isStraight(true); // restore original routing info $routingInfo = array( 'aliases' => $this->getMetadata('routing_aliases'), 'requested_route' => $this->getMetadata('routing_requested_route'), 'requested_controller' => $this->getMetadata('routing_requested_controller'), 'requested_action' => $this->getMetadata('routing_requested_action') ); Mage::app()->getRequest()->setRoutingInfo($routingInfo); return false; } }
      
      







 /** * Process Containers * * @param $content * @return array */ protected function _processContainers(&$content) { $placeholders = array(); preg_match_all( Enterprise_PageCache_Model_Container_Placeholder::HTML_NAME_PATTERN, $content, $placeholders, PREG_PATTERN_ORDER ); $placeholders = array_unique($placeholders[1]); $containers = array(); foreach ($placeholders as $definition) { $placeholder = new Enterprise_PageCache_Model_Container_Placeholder($definition); $container = $placeholder->getContainerClass(); if (!$container) { continue; } $container = new $container($placeholder); $container->setProcessor($this); if (!$container->applyWithoutApp($content)) { $containers[] = $container; } else { preg_match($placeholder->getPattern(), $content, $matches); if (array_key_exists(1,$matches)) { $containers = array_merge($this->_processContainers($matches[1]), $containers); $content = preg_replace($placeholder->getPattern(), str_replace('$', '\\$', $matches[1]), $content); } } } return $containers; }
      
      







これらのメソッドのコードからわかるように、プレースホルダーを持つブロックのみが処理されます。 ブロックのプレースホルダーがない場合、その内容は変更されません。 したがって、動的に変化するブロックを表示する場合は、それらのプレースホルダーを作成する必要があります。



プレースホルダーコンテナーは、まずapplyWithoutAppメソッドによって処理されます。 このメソッドは、キャッシュされたページのブロックのコンテンツを必要なコンテンツに置き換えようとします。 applyWithoutAppメソッドの例、Enterprise_PageCache_Model_Container_Abstractクラスのすべてのコンテナーの抽象:



 /** * Generate placeholder content before application was initialized and apply to page content if possible * * @param string $content * @return bool */ public function applyWithoutApp(&$content) { $cacheId = $this->_getCacheId(); if ($cacheId === false) { $this->_applyToContent($content, ''); return true; } $block = $this->_loadCache($cacheId); if ($block === false) { return false; } $block = Enterprise_PageCache_Helper_Url::replaceUenc($block); $this->_applyToContent($content, $block); return true; }
      
      







コードから、ブロックを動的に変更する場合、ブロックのロジックに基づいてコンテナのメソッド_getCacheIdを決定する必要があることは明らかです。 少なくとも1つのコンテナーが処理されていない場合(applyWithoutAppがfalseを返した場合)、レンダリングする必要があり(Enterprise_PageCache_Model_Processorクラスの_processContentメソッドを参照)、Magentoが初期化されます(Mage :: app()が実行されます)。 この場合、要求はプロセスアクションを使用してEnterprise_PageCache_RequestControllerコントローラーによって処理されます。 プロセスのアクションコード:



 /** * Request processing action */ public function processAction() { $processor = Mage::getSingleton('enterprise_pagecache/processor'); $content = Mage::registry('cached_page_content'); $containers = Mage::registry('cached_page_containers'); $cacheInstance = Enterprise_PageCache_Model_Cache::getCacheInstance(); foreach ($containers as $container) { $container->applyInApp($content); } $this->getResponse()->appendBody($content); // save session cookie lifetime info $cacheId = $processor->getSessionInfoCacheId(); $sessionInfo = $cacheInstance->load($cacheId); if ($sessionInfo) { $sessionInfo = unserialize($sessionInfo); } else { $sessionInfo = array(); } $session = Mage::getSingleton('core/session'); $cookieName = $session->getSessionName(); $cookieInfo = array( 'lifetime' => $session->getCookie()->getLifetime(), 'path' => $session->getCookie()->getPath(), 'domain' => $session->getCookie()->getDomain(), 'secure' => $session->getCookie()->isSecure(), 'httponly' => $session->getCookie()->getHttponly(), ); if (!isset($sessionInfo[$cookieName]) || $sessionInfo[$cookieName] != $cookieInfo) { $sessionInfo[$cookieName] = $cookieInfo; // customer cookies have to be refreshed as well as the session cookie $sessionInfo[Enterprise_PageCache_Model_Cookie::COOKIE_CUSTOMER] = $cookieInfo; $sessionInfo[Enterprise_PageCache_Model_Cookie::COOKIE_CUSTOMER_GROUP] = $cookieInfo; $sessionInfo[Enterprise_PageCache_Model_Cookie::COOKIE_CUSTOMER_LOGGED_IN] = $cookieInfo; $sessionInfo[Enterprise_PageCache_Model_Cookie::CUSTOMER_SEGMENT_IDS] = $cookieInfo; $sessionInfo[Enterprise_PageCache_Model_Cookie::COOKIE_MESSAGE] = $cookieInfo; $sessionInfo = serialize($sessionInfo); $cacheInstance->save($sessionInfo, $cacheId, array(Enterprise_PageCache_Model_Processor::CACHE_TAG)); } }
      
      







未処理のコンテナごとに、applyInAppメソッドが起動されます。これにより、ブロックがレンダリングされ、コンテンツ内のプレースホルダーのコンテンツが置き換えられます。 Enterprise_PageCache_Model_Container_AbstractクラスのコンテナーのapplyInAppメソッド抽象のコード:



 /** * Generate and apply container content in controller after application is initialized * * @param string $content * @return bool */ public function applyInApp(&$content) { $blockContent = $this->_renderBlock(); if ($blockContent === false) { return false; } if (Mage::getStoreConfig(Enterprise_PageCache_Model_Processor::XML_PATH_CACHE_DEBUG)) { $debugBlock = new Enterprise_PageCache_Block_Debug(); $debugBlock->setDynamicBlockContent($blockContent); $debugBlock->setTags($this->_getPlaceHolderBlock()->getCacheTags()); $debugBlock->setType($this->_placeholder->getName()); $this->_applyToContent($content, $debugBlock->toHtml()); } else { $this->_applyToContent($content, $blockContent); } $subprocessor = $this->_processor->getSubprocessor(); if ($subprocessor) { $contentWithoutNestedBlocks = $subprocessor->replaceContentToPlaceholderReplacer($blockContent); $this->saveCache($contentWithoutNestedBlocks); } return true; }
      
      







ここで見るように、コンテンツはコンテナクラスの_renderBlockメソッドによって取得されます。原則として、このメソッドはコンテナごとに一意であり、いくつかの特別なロジックが含まれています。コンテナの抽象クラスEnterprise_PageCache_Model_Container_Abstractでは、このメソッドはfalseを返します。ブロックのHTMLコンテンツを返すのは彼です。



ただし、コンテナのレンダリングには問題があります。コンテナブロックの場合、親はレンダリングされない可能性があり、コントローラーの実行は異なります。これは、ブロック内にデータが存在しない可能性があることを意味し、このケースを提供する必要があります(たとえば、次のように製品を取得します:$ product = Mage :: registry( 'current_product')、または親ブロックにプロパティを設定します: $ this-> getChild( 'block_alias')-> setProduct($ _ product))。



しかし、覚えているように、FPCはレンダリングに必要なすべての情報を保存します。したがって、保存するブロックのgetCacheKeyInfoメソッドで情報を渡した場合、FPCレンダリング中に設定できます。これは、コンテナの_renderBlockメソッドで行う必要があります。_getPlaceHolderBlockメソッドを使用して、ブロックインスタンスを取得できます。



 /** * Get Placeholder Block * * @return Mage_Core_Block_Abstract */ protected function _getPlaceHolderBlock() { if (null === $this->_placeholderBlock) { $blockName = $this->_placeholder->getAttribute('block'); $this->_placeholderBlock = new $blockName; $this->_placeholderBlock->setTemplate($this->_placeholder->getAttribute('template')); $this->_placeholderBlock->setLayout(Mage::app()->getLayout()); $this->_placeholderBlock->setSkipRenderTag(true); } return $this->_placeholderBlock; }
      
      







ご覧のとおり、テンプレートがインストールされたブロッククラスのインスタンスを取得します。_renderBlockメソッドの例:



 /** * Render block content from placeholder * * @return string|false */ protected function _renderBlock() { /** * @var $block Turnkeye_Popup_Block_Popup */ $block = $this->_getPlaceHolderBlock(); $placeholder = $this->_placeholder; $serializedParameters = array('handles', 'popup_ids'); foreach ($serializedParameters as $parameter) { $value = unserialize($placeholder->getAttribute($parameter)); $block->setDataUsingMethod($parameter, $value); } return $block->toHtml(); }
      
      







ここでは、コードからわかるように、handlesおよびpopup_idsパラメーターをブロックに渡しました。同じブロックで、次のように取得する必要があります。



 public function getPopupIds() { if (!$this->hasData('popup_ids')) { $popupIds = ... ... $this->setData('popup_ids', $popupIds); } return $this->getData('popup_ids'); }
      
      







この記事がFPC機能の秘密のベールを解き、FPCメカニズムがどのように機能するかを徹底的に学んだことを願っています。



キャッシュは非常に効率的で優れた機能を発揮し、サーバーの負荷を減らし、トラフィックの多い電子ストアの高速動作を保証します。



FPCに関する次の記事では、FPCを有効にして追加の動的機能を正しく動作させるために、Magento Enterpriseで独自の動的ブロックを設定する方法について説明します。



All Articles