We suppose you continue following our Magento certification-dedicated posts in our blog. This time we will tell you how Magento loads and manipulates configuration information.
Basically, Magento configuration is spread among dozens of .xml files. So this is a reasonable question – how does Magento operate all these files and find proper settings for each particular extension?
Let’s refresh some key points of the Magento structure
• Magento is a modular system, in which functionality is located in separate modules.
• There are 3 code pools in Magento – local, community and core.
• The structure of each module includes app/code/[codePool]/Namespace/Modulename/etc/config.xml (this file contains all basic module settings) and app/etc/modules/Namespace_Modulename.xml (this file contains information about code pool and extension activation flag).
• Global settings for Magento installation, including database connection data and admin panel address, are stored in app/etc/config.xml и app/etc/local.xml.
Refer to our previously posted certification-related articles: Magento Codepools and Magento Module Structure
If we trace the code performance starting from index.php, we’ll get the following outcome:
1 2 3 4 5 6 |
Index.php Mage::run() self::$_config = new Mage_Core_Model_Config($options); (in CE 1.6.* and previous versions) self::_setConfigModel($options); (in СE 1.7.*) self::$_app = new Mage_Core_Model_App(); self::$_app->run(…); |
or
1 2 3 |
Mage::app() self::$_app = new Mage_Core_Model_App(); self::$_app-> init (…); |
Later on we have a similar order of the class methods loading in both versions. In case of the Mage::run() all wrappers for processing configuration loading are located in Mage_Core_Model_App and refer to the Mage_Core_Model_Config methods. Calling Mage::app(), we immediately call Mage_Core_Model_Config::init(), which contains the process of configuration loading.
Magento Webdesign
Take your online store to the next level with BelVG Magento Webdesign
Visit the pageFinally, we come to Mage_Core_Model_Config, inherited from Mage_Core_Model_Config_Base and Varien_Simplexml_Config. At this stage the call method doesn’t matter, so let’s refer to Mage_Core_Model_Config:: init():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?php /** * Initialization of core configuration * * @return Mage_Core_Model_Config */ public function init($options=array()) { // lets skip cache init and non-standard options stuff $this->loadBase(); $cacheLoad = $this->loadModulesCache(); if ($cacheLoad) { return $this; } $this->loadModules(); $this->loadDb(); $this->saveCache(); return $this; } |
Let’s analyze this method line by line.
$this->loadBase();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/** * Load base system configuration (config.xml and local.xml files) * * @return Mage_Core_Model_Config */ public function loadBase() { $etcDir = $this->getOptions()->getEtcDir(); $files = glob($etcDir.DS.'*.xml'); $this->loadFile(current($files)); while ($file = next($files)) { $merge = clone $this->_prototype; $merge->loadFile($file); $this->extend($merge); } if (in_array($etcDir.DS.'local.xml', $files)) { $this->_isLocalConfigLoaded = true; } return $this; } |
From the very beginning we define the absolute path to app/etc directory. Then we get a list of all .xml files from this catalog, read their content and merge into a single simpleXmlElement object. If local.xml file has been loaded (which generally means that Magento has already been installed), set flag $this->_isLocalConfigLoaded = true;. It will be used later to initialize store and load module setup scripts.
Partner With Us
Let's discuss how to grow your business. Get a Free Quote.As far as this method doesn’t limit names and the amount of loaded files, we can add our custom .xml file into app/etc, if necessary. It can be helpful for specifying database connection data on a dev-server without the use of local.xml file, containing production-server information only.
1 2 3 4 5 |
<?php $cacheLoad = $this->loadModulesCache(); if ($cacheLoad) { return $this; } |
This piece of code speaks for itself. If the configuration cache is enabled and contains the required information, we load the entire config. We also replace the config that has already been loaded from app/etc with the one, loaded from the cache (Mage_Core_Model_Config:: loadCache()) and return back to Mage_Core_Model_App.
There is a reasonable question you might ask at this point: if the whole configuration can be loaded from cache, why haven’t developers made this verification the first step, before scanning app/etc directory?
The thing is that Magento cache can be stored not only as files in var/cache, but in apc, memcached and xcache as well. This particular step with files loading from app/etc allows to define the type and configuration of the cache storage used.
The next step is loading of the most extensive configuration part – modules configuration.
$this->loadModules();
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 |
/** * Load modules configuration * * @return Mage_Core_Model_Config */ 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; } |
$this->_loadDeclaredModules();
_getDeclaredModuleFiles(): At the beginning we scan app/etc/modules directory and collect the list of paths to all .xml files, indicating all modules in the system. We form an associative array with “base”, “mage” and “custom” keys. Only Mage_All.xml path goes to the “base” section. Modules from the Magento basic pack (located in app/code/core/Mage – code pool “core”) go to the «mage» section. «Custom» section collects the rest of modules. At the end we merge everything into a single array. Due to initial splitting into keys, data is stored in the following sequence: Mage_All.xml, modules with Mage namespace, all other modules.
If you haven’t figured out why Magento developers paid so much attention to Mage_All.xml file, it’s a high time for you to look inside of it. Mage_All.xml contains all information required for loading modules that are crucial for the proper system operation.
Next, information from the list of collected .xml files is loaded into Mage_Core_Model_Config_Base $unsortedConfig. Then we get the$moduleDepends array, based on <depends>, <active> flags and module name.
$this->_sortModuleDepends($moduleDepends): is necessary for checking dependencies of all existing modules. After the verification a new array is formed, where modules are arranged regarding their dependencies on one another.
At the end of _loadDeclaredModules() we create simpleXmlElement object again and merge it with the one that has been created earlier from app/etc/*.xml.
Let’s get back to loadModules() method.
1 2 3 |
<?php $resourceConfig = sprintf('config.%s.xml', $this->_getResourceConnectionModel('core')); $this->loadModulesConfiguration(array('config.xml',$resourceConfig), $this); |
$resourceConfig contains config.mysql4.xml line. Thus, our next step will be loading configuration from config.mysql4.xml and config.mysql4.xml files. It is still not clear, what config.mysql4.xml was planned to be used for, but I really hope that further releases will exclude reference to this file or eventually it will prove to be needed.
First this method checks whether modules could be loaded from local code pool (pay attention at <disable_local_modules>false</disable_local_modules> in app/etc/local.xml). If only modules from community and core are permitted, Magento will change include_path() correspondingly.
1 2 3 4 5 6 7 8 9 10 |
<?php if ($disableLocalModules && !defined('COMPILER_INCLUDE_PATH')) { set_include_path( // excluded '/app/code/local' BP . DS . 'app' . DS . 'code' . DS . 'community' . PS . BP . DS . 'app' . DS . 'code' . DS . 'core' . PS . BP . DS . 'lib' . PS . Mage::registry('original_include_path') ); } |
After this simple verification Magento ensures that we already have some config – simpleXmlElement (if we don’t – it creates it) and delete from the loaded list of modules ones that have keys <active>false</active> and <codePool>local</codePool>.
Then for each of remained modules config.xml and config.mysql4.xml files are loaded. Next, as you’ve most likely figured out by yourselves, the loaded .xml is added to the existing one. If the last loaded file already contains the older xpath, system will use the last value.
The $this->applyExtends(); call originally was a way to change (override) data in config with the help of a custom extension. But this functionality is not in use at the moment.
If someone has managed to read everything down to these lines, please, pay attention to the following piece of code:
1 2 3 4 5 6 7 8 9 |
<?php /** * 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); } |
As we see, developers took care of no module could modify system data from app/etc/local.xml (the default database connection, outer cache and proxy-servers settings).
Finally, we’ve approached $this->loadDb();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php /** * Load config data from DB * * @return Mage_Core_Model_Config */ public function loadDb() { if ($this->_isLocalConfigLoaded && Mage::isInstalled()) { Varien_Profiler::start('config/load-db'); $dbConf = $this->getResourceModel(); $dbConf->loadToXml($this); Varien_Profiler::stop('config/load-db'); } return $this; } |
This method uses resource Mage_Core_Model_Resource_Config model, related to core_config_data table. At this step we load data from core_config_data into our configuration:
1. We add data about websites (see core_website table)
2. We add data about stores for the existing websites (see core_store table)
3. We add data from core_config_data according to the scope
a. Create <default> block first
b. Then create <websites> block
c. And, finally – <stores>
d. Each iteration replaces data from more general area of the scope with more specific one (Do you remember these amazing «use default», «use website» and «configuration scope» in the backend?)
4. Self-configuration (if Magento meets data related to no longer existing website, these data will be deleted).
Magento Development
Take your online store to the next level with BelVG Magento Development
Visit the page
Thanks! It was for 1.5 years for me just “magento magic”, and now your article +xdebug makes magic go away!
Thank you for the valuable help to the next Generation of magento developers. like me :)
@Dmitry thank you, fixed
please, correct mistake in ‘app/code/[codePool]/Namespace/Modulename/config.xml’, *.xml files are stored in etc/ folder
Nice post! Before hire Magento developer companies are checking whether the developer whom they are hiring is certified in Magento or not, affordable or not, skilled or not, then only it would be a worth deal for them..
Hi.., Alessandro Ronchi
I just check your problem It seems that if you change your file name custom.xml to my.xml then it should be work CAUSE if there are bunch of the xml files are in app/etc/*.xml then the finally merged file in the loadBase() will be result of all files(merged) in alphabetical order. I hope this make sense to you.
Thanks
Yes cache was enabled at backend, thanks for pointing this.
@Dgent
I do not know why you had problems with xDebug. Here is of my IDE with breakpoint in _loadDeclaredModules() method.
This method operates with “depends” and “active” nodes. But if Magento config cache is enabled final xml is taken from the cache storage and method is not executed.
Hello Pavel,
I tried to use xdebug putting break point in Mage_Core_Moel_Config:: _loadDeclaredModules(); but the function is not executed so I cannot see what actually happens inside. I believe this module should be executed each time because we may disable any module.
Thanks
Alessandro,
To my mind, adding development database configuration without changing local.xml is more theoretical than practical question. Something like rewriting store configuration would be more suitable example.
Anyways, the “local.xml reloading” does not prevent from adding new database connection.
Here is the example – a part of the production local.xml:
And here is app/etc/mydev.xml:
Now Magento will try to use “dev_dbuser” user with the “dev_password” password and connect to the “dev_magento_db” database.
Thank you for your helpful articles, anyway there is something that is not so clear to me.
You say “As far as this method – loadBase() – doesn’t limit names and the amount of loaded files, we can add our custom .xml file into app/etc, if necessary. It can be helpful for specifying database connection data on a dev-server without the use of local.xml file, containing production-server information only.”
But this doesn’t seem to work, because, as you write after “As we see, developers took care of no module could modify system data from app/etc/local.xml (the default database connection, outer cache and proxy-servers settings).”
Infact even if the loadBase() loads every xml file in the app/etc dir, the loadModules() reloads the “local.xml” making our custom xml files useless.
Do you agree?
Kind regards,
Alessandro Ronchi
Sagar,
Thanks for appreciating the article.
That’s great article! Well done, keep on publishing such articles more and more. Thanks