Solving Magento

Solutions for Magento E-Commerce Platform

by Oleg Ishenko

Magento Application Initialization: Loading Module Configuration

Loading module configuration is one of the most important stages of the application initialization. It takes place relatively early in the process soon after the basic configuration is loaded from the etc/config.xml and etc/local.xml files and before instantiating the front controller and the request dispatching.

Module initialization starts in the Mage_Core_Model_App::run function. in it you can find a call to the function _initModules():

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 function uses an object of type Mage_Core_Model_Config to load the model configuration. The system checks if the configuration can be fetched from the cache, and if not, proceeds with loading it from module declaration and configuration files.

This process takes place in the function Mage_Core_Model_Config::loadModules and starts with loading module declarations:

$this->_loadDeclaredModules();

The function Mage_Core_Model_Config::_loadDeclaredModules scans the directory etc/modules and loads XML files located there. These files contain module declarations. Before loading their contents, the system arranges them into a specific order. The first file in this list is Mage_All.xml. The modules declared in this file make up the essential core functionality of Magento. Next in line are the declaration files whose names begin with Mage_. The modules declared there are a part of the Magento core as well, but often depend on the modules from the Mage_All.xml file and thus must be loaded after those. The rest of the list are declarations of third party modules: community modules and your local extensions. Once the list is compiled, Magento fetches the files in the order they are listed and loads their contents into an object of type Mage_Core_Model_Config_Base – the application configuration object, which Magento queries whenever a configuration entry is requested. The loaded configuration must be ordered once more – this time according to module dependencies.

A module’s declaration can contain references to other modules on whose functionality the given module depends. Take a look at the XML declaration of the module Mage_Captcha:

<config>
    <modules>
        <Mage_Captcha>
            <active>true</active>
            <codePool>core</codePool>
            <depends>
                <Mage_Customer />
                <Mage_Adminhtml />
            </depends>
        </Mage_Captcha>
    </modules>
</config>

The node shows that this module requires Mage_Customer and Mage_Adminhtml. Magento observes such module relations when defining the order in which module declarations are loaded. Magento checks if dependency modules are active and loads them before dependent modules. This sorting takes place in function Mage_Core_Model_Config::_sortModuleDepends. This function produces an array in which modules are ordered by placing dependent module after their dependencies. This array (moduleDepends) is used to reorder the loaded module declarations by dependency:

foreach ($moduleDepends as $moduleProp) {
    $node = $unsortedConfig->getNode('modules/'.$moduleProp['module']);
    $sortedConfig->getNode('modules')->appendChild($node);
}

At this point the Magento knows which modules the installation has and what module configuration must be loaded. As you already know, the module configuration is declared in a config.xml file in the etc directory of the module. Additionally Magento will look for a config.mysql4.xml file in the same category. Such files are not present in the most recent Magento versions. I can only assume that scanning for config.mysql4.xml files is performed for the sake of compatibility with the earlier releases.

The loading of the configuration files is done in function Mage_Core_Model_Config::loadModulesConfiguration :

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)) {
                    $mergeToObject->extend($mergeModel, true);
                }
            }
        }
    }
    return $mergeToObject;
}

In the first line of this function the system checks if configuration settings forbid using the local modules. If yes, then behind the scenes Magento will modify the include path so that id does not contain the path to your local code pool. This way the autoloader will not be able to find your local classes.

In this context the variable $mergeToObject is the shop’s configuration object itself. After the loading is done it would contain the module configuration. Next an object of type Mage_Core_Model_Config is created, $mergeModel. It will be used as a temporary storage to fetch the contents of a configuration file and add it to $mergeToObject.

Magento fetches a list of declared modules into a variable $modules and iterates through it. The system checks every module if it is active and, if yes, looks for a config.xml or a config.mysql4.xml file in the module’s etc directory and loads the module configuration from these files.

With this the loading of the module configurations is done. In order to prevent overwriting of any settings defined in the etc/local.xml (they must always have prevalence) Magento loads configurations from file once again:

$mergeConfig = clone $this->_prototype;
$this->_isLocalConfigLoaded = $mergeConfig->loadFile($this->getOptions()->getEtcDir().DS.'local.xml');
if ($this->_isLocalConfigLoaded) {
    $this->extend($mergeConfig);
}

The final step in the function Mage_Core_Model_Config::loadModules() is the following call:

$this->applyExtends();

The class Mage_Core_Model_Config inherits the function applyExtends from the Varien_Simplexml_Config class. This function is used to load configuration extensions. Magento XML configuration (such as defined in module’s config.xml files) can contain nodes like this:

<allResources extends="/config/modules/resource"/>

This <allResources> node (its name can be custom, but the attribute “extends” is mandatory) extends the configuration found under path /config/modules/resource. Magento will look for the target node and extend it with the contents of the extension node (<allResources> in our example):

public function applyExtends()
{
    //$this->_xpathExtends = "//*[@extends]";
    $targets = $this->getXpath($this->_xpathExtends);
    if (!$targets) {
        return $this;
    }

    foreach ($targets as $target) {
        $sources = $this->getXpath((string)$target['extends']);
        if ($sources) {
                    foreach ($sources as $source) {
                $target->extend($source);
            }
        }
        }
    return $this;
}

With the module configuration loaded Magento returns control to the Mage_Core_Model_App::_initModules(). The next step is to apply module installation and update scripts. A module’s setup configuration is defined in the config.xml file under the node global/resources. Magento fetches these nodes from the loaded configuration and iterates through this list executing the installation and update scripts where necessary. We will discuss setup scripts in a much greater detail later.

Once the module installation and upgrades are through, Magento loads the configuration data stored in the database table core_config_data. The configuration stored in the database can be controlled from the shop back-end interface. The information entered in the Configuration section of the back-end is saved into the database.

When loading the configuration from the database Magento observes the scope of the settings. The settings values can have the scope stores, websites, or default – in the order of precedence. Magento will overwrite a default setting if a specific website value if provided, which in turn can be replaced with a specific store value.

The final step in the module initialization and configuration loading in saving the collected configuration data into the cache. Using the configuration cache significantly speeds up subsequent requests as Magento can skip costly IO calls to the file system and the database by replacing them with a single read of the entire cached configuration. For this to happen the configuration caching must be enabled – in backend under System => Cache Storage Management.

Leave a Reply

Your email address will not be published. Required fields are marked *

Theme: Esquire by Matthew Buchanan.