array(), 'to'=>array()) * * @var array */ protected $_subst = array(); public function __construct() { $subst = Mage::getConfig()->getPathVars(); foreach ($subst as $k=>$v) { $this->_subst['from'][] = '{{'.$k.'}}'; $this->_subst['to'][] = $v; } } public function getElementClass() { if (!$this->_elementClass) { $this->_elementClass = Mage::getConfig()->getModelClassName('core/layout_element'); } return $this->_elementClass; } public function resetUpdates() { $this->_updates = array(); return $this; } public function addUpdate($update) { $this->_updates[] = $update; return $this; } public function asArray() { return $this->_updates; } public function asString() { return implode('', $this->_updates); } public function resetHandles() { $this->_handles = array(); return $this; } public function addHandle($handle) { if (is_array($handle)) { foreach ($handle as $h) { $this->_handles[$h] = 1; } } else { $this->_handles[$handle] = 1; } return $this; } public function removeHandle($handle) { unset($this->_handles[$handle]); return $this; } public function getHandles() { return array_keys($this->_handles); } /** * Get cache id * * @return string */ public function getCacheId() { if (!$this->_cacheId) { $this->_cacheId = 'LAYOUT_'.Mage::app()->getStore()->getId().md5(join('__', $this->getHandles())); } return $this->_cacheId; } /** * Set cache id * * @param string $cacheId * @return Mage_Core_Model_Layout_Update */ public function setCacheId($cacheId) { $this->_cacheId = $cacheId; return $this; } public function loadCache() { if (!Mage::app()->useCache('layout')) { return false; } if (!$result = Mage::app()->loadCache($this->getCacheId())) { return false; } $this->addUpdate($result); return true; } public function saveCache() { if (!Mage::app()->useCache('layout')) { return false; } $str = $this->asString(); $tags = $this->getHandles(); $tags[] = self::LAYOUT_GENERAL_CACHE_TAG; return Mage::app()->saveCache($str, $this->getCacheId(), $tags, null); } /** * Load layout updates by handles * * @param array|string $handles * @return Mage_Core_Model_Layout_Update */ public function load($handles=array()) { if (is_string($handles)) { $handles = array($handles); } elseif (!is_array($handles)) { throw Mage::exception('Mage_Core', Mage::helper('core')->__('Invalid layout update handle')); } foreach ($handles as $handle) { $this->addHandle($handle); } if ($this->loadCache()) { return $this; } foreach ($this->getHandles() as $handle) { $this->merge($handle); } $this->saveCache(); return $this; } public function asSimplexml() { $updates = trim($this->asString()); $updates = '<'.'?xml version="1.0"?'.'>'.$updates.''; return simplexml_load_string($updates, $this->getElementClass()); } /** * Merge layout update by handle * * @param string $handle * @return Mage_Core_Model_Layout_Update */ public function merge($handle) { $packageUpdatesStatus = $this->fetchPackageLayoutUpdates($handle); if (Mage::app()->isInstalled()) { $this->fetchDbLayoutUpdates($handle); } // if (!$this->fetchPackageLayoutUpdates($handle) // && !$this->fetchDbLayoutUpdates($handle)) { // #$this->removeHandle($handle); // } return $this; } public function fetchFileLayoutUpdates() { $storeId = Mage::app()->getStore()->getId(); $elementClass = $this->getElementClass(); $design = Mage::getSingleton('core/design_package'); $cacheKey = 'LAYOUT_' . $design->getArea() . '_STORE' . $storeId . '_' . $design->getPackageName() . '_' . $design->getTheme('layout'); $cacheTags = array(self::LAYOUT_GENERAL_CACHE_TAG); if (Mage::app()->useCache('layout') && ($layoutStr = Mage::app()->loadCache($cacheKey))) { $this->_packageLayout = simplexml_load_string($layoutStr, $elementClass); } if (empty($layoutStr)) { $this->_packageLayout = $this->getFileLayoutUpdatesXml( $design->getArea(), $design->getPackageName(), $design->getTheme('layout'), $storeId ); if (Mage::app()->useCache('layout')) { Mage::app()->saveCache($this->_packageLayout->asXml(), $cacheKey, $cacheTags, null); } } // $elementClass = $this->getElementClass(); // // $design = Mage::getSingleton('core/design_package'); // $area = $design->getArea(); // $storeId = Mage::app()->getStore()->getId(); // $cacheKey = 'LAYOUT_'.$area.'_STORE'.$storeId.'_'.$design->getPackageName().'_'.$design->getTheme('layout'); //#echo "TEST:".$cacheKey; // $cacheTags = array('layout'); // // if (Mage::app()->useCache('layout') && ($layoutStr = Mage::app()->loadCache($cacheKey))) { // $this->_packageLayout = simplexml_load_string($layoutStr, $elementClass); // } // // if (empty($layoutStr)) { // $updatesRoot = Mage::app()->getConfig()->getNode($area.'/layout/updates'); // Mage::dispatchEvent('core_layout_update_updates_get_after', array('updates' => $updatesRoot)); // $updateFiles = array(); // foreach ($updatesRoot->children() as $updateNode) { // if ($updateNode->file) { // $module = $updateNode->getAttribute('module'); // if ($module && Mage::getStoreConfigFlag('advanced/modules_disable_output/' . $module)) { // continue; // } // $updateFiles[] = (string)$updateNode->file; // } // } // // // custom local layout updates file - load always last // $updateFiles[] = 'local.xml'; // // $layoutStr = ''; // #$layoutXml = new $elementClass(''); // foreach ($updateFiles as $file) { // $filename = $design->getLayoutFilename($file); // if (!is_readable($filename)) { // continue; // } // $fileStr = file_get_contents($filename); // $fileStr = str_replace($this->_subst['from'], $this->_subst['to'], $fileStr); // $fileXml = simplexml_load_string($fileStr, $elementClass); // if (!$fileXml instanceof SimpleXMLElement) { // continue; // } // $layoutStr .= $fileXml->innerXml(); // // #$layoutXml->appendChild($fileXml); // } // $layoutXml = simplexml_load_string(''.$layoutStr.'', $elementClass); // // $this->_packageLayout = $layoutXml; // // if (Mage::app()->useCache('layout')) { // Mage::app()->saveCache($this->_packageLayout->asXml(), $cacheKey, $cacheTags, null); // } // } return $this; } public function fetchPackageLayoutUpdates($handle) { $_profilerKey = 'layout/package_update: '.$handle; Varien_Profiler::start($_profilerKey); if (empty($this->_packageLayout)) { $this->fetchFileLayoutUpdates(); } foreach ($this->_packageLayout->$handle as $updateXml) { #echo ''; $this->fetchRecursiveUpdates($updateXml); $this->addUpdate($updateXml->innerXml()); } Varien_Profiler::stop($_profilerKey); return true; } public function fetchDbLayoutUpdates($handle) { $_profilerKey = 'layout/db_update: '.$handle; Varien_Profiler::start($_profilerKey); $updateStr = $this->_getUpdateString($handle); if (!$updateStr) { return false; } $updateStr = '' . $updateStr . ''; $updateStr = str_replace($this->_subst['from'], $this->_subst['to'], $updateStr); $updateXml = simplexml_load_string($updateStr, $this->getElementClass()); $this->fetchRecursiveUpdates($updateXml); $this->addUpdate($updateXml->innerXml()); Varien_Profiler::stop($_profilerKey); return true; } /** * Get update string * * @param string $handle * @return mixed */ protected function _getUpdateString($handle) { return Mage::getResourceModel('core/layout')->fetchUpdatesByHandle($handle); } public function fetchRecursiveUpdates($updateXml) { foreach ($updateXml->children() as $child) { if (strtolower($child->getName())=='update' && isset($child['handle'])) { $this->merge((string)$child['handle']); // Adding merged layout handle to the list of applied hanles $this->addHandle((string)$child['handle']); } } return $this; } /** * Collect and merge layout updates from file * * @param string $area * @param string $package * @param string $theme * @param integer|null $storeId * @return Mage_Core_Model_Layout_Element */ public function getFileLayoutUpdatesXml($area, $package, $theme, $storeId = null) { if (null === $storeId) { $storeId = Mage::app()->getStore()->getId(); } /* @var $design Mage_Core_Model_Design_Package */ $design = Mage::getSingleton('core/design_package'); $layoutXml = null; $elementClass = $this->getElementClass(); $updatesRoot = Mage::app()->getConfig()->getNode($area.'/layout/updates'); Mage::dispatchEvent('core_layout_update_updates_get_after', array('updates' => $updatesRoot)); $updates = $updatesRoot->asArray(); $themeUpdates = Mage::getSingleton('core/design_config')->getNode("$area/$package/$theme/layout/updates"); if ($themeUpdates && is_array($themeUpdates->asArray())) { //array_values() to ensure that theme-specific layouts don't override, but add to module layouts $updates = array_merge($updates, array_values($themeUpdates->asArray())); } $updateFiles = array(); foreach ($updates as $updateNode) { if (!empty($updateNode['file'])) { $module = isset($updateNode['@']['module']) ? $updateNode['@']['module'] : false; if ($module && Mage::getStoreConfigFlag('advanced/modules_disable_output/' . $module, $storeId)) { continue; } $updateFiles[] = $updateNode['file']; } } // custom local layout updates file - load always last $updateFiles[] = 'local.xml'; $layoutStr = ''; foreach ($updateFiles as $file) { $filename = $design->getLayoutFilename($file, array( '_area' => $area, '_package' => $package, '_theme' => $theme )); if (!is_readable($filename)) { continue; } $fileStr = file_get_contents($filename); $fileStr = str_replace($this->_subst['from'], $this->_subst['to'], $fileStr); $fileXml = simplexml_load_string($fileStr, $elementClass); if (!$fileXml instanceof SimpleXMLElement) { continue; } $layoutStr .= $fileXml->innerXml(); } $layoutXml = simplexml_load_string(''.$layoutStr.'', $elementClass); return $layoutXml; } }