If you’re going to take the Magento exam, the article seems rather useful for you, as it is dedicated to one of the most topical issues. Module initialization in Magento 1 covers such questions as module definition, the process of module initialization including module declarations and config files. The step-by-step guide is assured to be indispensable for you.
Module definition
In order to create a new module in Magento 1, we need to create two config files which describe the module and its dependencies in Magento.
The first config file we’re creating is app/etc/modules/Vandor_Module.xml:
1 2 3 4 5 6 7 8 9 10 11 12 |
<?xml version="1.0" ?> <config> <modules> <Vendor_Module> <active>true</active> <codePool>local</codePool> <depends> <Mage_Catalog/> </depends> </Vendor_Module> </modules> </config> |
Where the tag <active> is a status definition of the module and the tag <codePool> describes which folder will be used by the module. The block <depends> describes module’s dependencies on other modules.
The second config file is app/code/local/Vendor/Module/etc/config.xml:
1 2 3 4 5 6 7 8 |
<?xml version="1.0"?> <config> <modules> <Vendor_Module> <version>0.0.1</version> </Vendor_Module> </modules> </config> |
Here we can define the module version and additionally declare the module’s block, controllers or other resources.
Module initialization process
If you want to understand how modules are loaded in Magento 1, we need to consider the method \Mage_Core_Model_App::_initModules.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
protected function _initModules() { if (!$this->_config->loadModulesCache()) { $this->_config->loadModules(); if ($this->_config->isLocalConfigLoaded() && !$this->_shouldSkipProcessModulesUpdates()) { Varien_Profiler::start('mage::app::init::apply_db_schema_updates'); Mage_Core_Model_Resource_Setup::applyAllUpdates(); Varien_Profiler::stop('mage::app::init::apply_db_schema_updates'); } $this->_config->loadDb(); $this->_config->saveCache(); } return $this; } |
This method checks if the modules can be loaded from cache and in case if it can’t be performed, it starts loading modules from declaration and configuration files by calling the method \Mage_Core_Model_Config::loadModules.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public function loadModules() { Varien_Profiler::start('config/load-modules'); $this->_loadDeclaredModules(); $resourceConfig = sprintf('config.%s.xml', $this->_getResourceConnectionModel('core')); $this->loadModulesConfiguration(array('config.xml',$resourceConfig), $this); /** * Prevent local.xml directives overwriting */ $mergeConfig = clone $this->_prototype; $this->_isLocalConfigLoaded = $mergeConfig->loadFile($this->getOptions()->getEtcDir().DS.'local.xml'); if ($this->_isLocalConfigLoaded) { $this->extend($mergeConfig); } $this->applyExtends(); Varien_Profiler::stop('config/load-modules'); return $this; } |
The method \Mage_Core_Model_Config::loadModules consists of two main steps:
- loading module declarations
- loading module config files
Loading module declarations
Module declarations are loaded via the method \Mage_Core_Model_Config::_loadDeclaredModules:
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
protected function _loadDeclaredModules($mergeConfig = null) { $moduleFiles = $this->_getDeclaredModuleFiles(); if (!$moduleFiles) { return ; } Varien_Profiler::start('config/load-modules-declaration'); $unsortedConfig = new Mage_Core_Model_Config_Base(); $unsortedConfig->loadString('<config/>'); $fileConfig = new Mage_Core_Model_Config_Base(); // load modules declarations foreach ($moduleFiles as $file) { $fileConfig->loadFile($file); $unsortedConfig->extend($fileConfig); } $moduleDepends = array(); foreach ($unsortedConfig->getNode('modules')->children() as $moduleName => $moduleNode) { if (!$this->_isAllowedModule($moduleName)) { continue; } $depends = array(); if ($moduleNode->depends) { foreach ($moduleNode->depends->children() as $depend) { $depends[$depend->getName()] = true; } } $moduleDepends[$moduleName] = array( 'module' => $moduleName, 'depends' => $depends, 'active' => ('true' === (string)$moduleNode->active ? true : false), ); } // check and sort module dependence $moduleDepends = $this->_sortModuleDepends($moduleDepends); // create sorted config $sortedConfig = new Mage_Core_Model_Config_Base(); $sortedConfig->loadString('<config><modules/></config>'); foreach ($unsortedConfig->getNode()->children() as $nodeName => $node) { if ($nodeName != 'modules') { $sortedConfig->getNode()->appendChild($node); } } foreach ($moduleDepends as $moduleProp) { $node = $unsortedConfig->getNode('modules/'.$moduleProp['module']); $sortedConfig->getNode('modules')->appendChild($node); } $this->extend($sortedConfig); Varien_Profiler::stop('config/load-modules-declaration'); return $this; } |
This method iterates through app/etc/modules directory and loads XML files located there. The first file to load is always Mage_All.xml which contains essential core modules of Magento 1. The files the names of which start with Mage_ are loaded just after Mage_All.xml. These modules are considered as a part of Magento core, but they depend on the modules from Mage_All.xml and should be loaded after that. The rest of the list contains a third party community and local modules.
Modules can have dependencies on other modules which are declared in .xml files.
The dependent modules need to be loaded after the dependencies and in order to perform it, the module list should be sorted out. The process takes place in \Mage_Core_Model_Config::_sortModuleDepends method.
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 35 36 37 38 39 40 41 |
protected function _sortModuleDepends($modules) { foreach ($modules as $moduleName => $moduleProps) { $depends = $moduleProps['depends']; foreach ($moduleProps['depends'] as $depend => $true) { if ($moduleProps['active'] && ((!isset($modules[$depend])) || empty($modules[$depend]['active']))) { Mage::throwException( Mage::helper('core')->__('Module "%1$s" requires module "%2$s".', $moduleName, $depend) ); } $depends = array_merge($depends, $modules[$depend]['depends']); } $modules[$moduleName]['depends'] = $depends; } $modules = array_values($modules); $size = count($modules) - 1; for ($i = $size; $i >= 0; $i--) { for ($j = $size; $i < $j; $j--) { if (isset($modules[$i]['depends'][$modules[$j]['module']])) { $value = $modules[$i]; $modules[$i] = $modules[$j]; $modules[$j] = $value; } } } $definedModules = array(); foreach ($modules as $moduleProp) { foreach ($moduleProp['depends'] as $dependModule => $true) { if (!isset($definedModules[$dependModule])) { Mage::throwException( Mage::helper('core')->__('Module "%1$s" cannot depend on "%2$s".', $moduleProp['module'], $dependModule) ); } } $definedModules[$moduleProp['module']] = true; } return $modules; } |
Loading module config files
Config files are loaded in the method \Mage_Core_Model_Config::loadModulesConfiguration:
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 35 36 37 |
public function loadModulesConfiguration($fileName, $mergeToObject = null, $mergeModel=null) { $disableLocalModules = !$this->_canUseLocalModules(); if ($mergeToObject === null) { $mergeToObject = clone $this->_prototype; $mergeToObject->loadString('<config/>'); } if ($mergeModel === null) { $mergeModel = clone $this->_prototype; } $modules = $this->getNode('modules')->children(); foreach ($modules as $modName=>$module) { if ($module->is('active')) { if ($disableLocalModules && ('local' === (string)$module->codePool)) { continue; } if (!is_array($fileName)) { $fileName = array($fileName); } foreach ($fileName as $configFile) { $configFile = $this->getModuleDir('etc', $modName).DS.$configFile; if ($mergeModel->loadFile($configFile)) { $this->_makeEventsLowerCase(Mage_Core_Model_App_Area::AREA_GLOBAL, $mergeModel); $this->_makeEventsLowerCase(Mage_Core_Model_App_Area::AREA_FRONTEND, $mergeModel); $this->_makeEventsLowerCase(Mage_Core_Model_App_Area::AREA_ADMIN, $mergeModel); $this->_makeEventsLowerCase(Mage_Core_Model_App_Area::AREA_ADMINHTML, $mergeModel); $mergeToObject->extend($mergeModel, true); } } } } return $mergeToObject; } |
This method checks if configuration settings allow using the local modules. Then it iterates through all declared modules and loads config.xml from the module directory if the module is enabled. In order to prevent settings overwriting from etc/local.xml (they should be loaded last), Magento loads configurations from the first file once again.
The last step in module initialization is saving collected data into the cache. Using cache speeds up subsequent call, as Magento can skip this entire process by reading the module configuration that has already been loaded and processed from the cache.