Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

Updated for Magento 2! No Frills Magento Layout is the only Magento
front end book you'll ever need. Get your copy
today!

This article is part of a longer series exploring the Magento global configuration object. While this article contains useful stand-alone information, you’ll want to read part 1 of the series to fully understand what’s going on.

When our last article finished up, we had successfully instantiated the global config object (Mage_Core_Model_Config), and loaded the base configuration files from app/etc/*.xml. In this article we’ll be covering the loading of Magento’s module configuration.

A key piece of the Varien/Magento business strategy involved relying on third-party developers to fill holes in their startup-developed system. It’s no surprise the Magento engineers chose to implement a formal system of code modules so developers across the world could avoid stepping on each other’s toes. While it’s still possible to create conflicting modules in Magento’s system, it’s done a good enough job and enabled a world wide community of commercial and open-source extensions

Magento’s configuration loading is a vital step to bootstrapping the module system. The Mage_Core_Model_Config object identifies which modules are active in a system, and loads the necessary information into the global config that will allow these modules to function properly.

This is the code that will tell the config object it’s time to load the XML information for each module. The surrounding if block ensures the full configuration loading only happens if Magento can’t load the configuration information from cache. This bit of code is why the phrase “clear your cache” is on every Magento developer’s lips.

Merging (or extending, see the first article in our series) each module’s config.xml tree into the main config object is actually a two step process.

First, we need to identify which modules are declared in the system, and which code pool they reside in. Once those modules have been identified we can start step two; loading their config.xml files from disk.

It’s worth repeating the two types of configuration files in play here. Files in

app/etc/modules/*.xml

are for declaring Magento modules. A file here tells Magento to look for a module.

Files in

app/code/[code pool]/Packagename/Modulename/etc/config.xml

are the files that contain the actual configuration information for a module.

Declared Modules

Jumping to the _loadDeclaredModules method, our first step is to grab a list of all the “declared” modules.

To identify which modules are declared, Magento will glob up the files in

app/etc/modules/*.xml

and merge/extend them into the global config. This will let Magento know which modules are installed into the system, which are active, and which code pool the module files are loaded in. By loading this information into the global configuration tree, Magento ensures the information will be available to any developer who wants it without having to reload everything.

This loading and extending is handled by the call to _loadDeclaredModules.

The _getDeclaredModuleFiles method is responsible for fetching a list of these files. Unlike loading the app/etc/*.xml files (per the last article), this is far from a simple file glob. Let’s take a look

after which it needs to reorder the files in the $moduleFiles array before returning a final list. Here’s the plain english explanation of the sorting

Mage_All.xml is always first

Any files that start with Mage_ come next

Any other files (with a non-Mage namespace) are last

This is necessary to ensure the configuration for any core Magento modules load before any third party modules. While Magento offers the ability to configure the loading order via a <depends/> tag, by jury-rigging the order ahead of time Magento ensures third-party modules without such a tag are loaded last.

Back up in _loadDeclaredModules, our $moduleFiles variable now contains an array of values, something like this

This code creates a new Mage_Core_Model_Config_Base object to hold a merged XML configuration tree. Then, each of the above XML files are loaded into another Mage_Core_Model_Config_Base object, and this object is merged into the first with extend. This is a similar pattern to the one we saw when loading the base config, except we’re merging files into a new first object instead of merging them into the Magento global config tree (in $this->_xml). Before we can add this node to $this->_xml, we need to ensure the modules are in the correct order, (per the previously mentioned <depends/> tag)

Before we continue, it’s interesting to note another way this pattern is different from loading our base configuration. Rather than cloning the _prototype object to hold a loaded configuration, the hard-coded Mage_Core_Model_Config_Base object is used. It’s unclear if this is a deliberate deviation, two developers working at a similar system level who had a difference of opinion on things, or a half done refactoring job. We mention it mainly to put to rest any confusion you might have about the weird discrepancy.

doesn’t pass in extend‘s second parameter. This means identical nodes in the second file will replace nodes in the first. The implication here is you could, theoretically, use your own module declaration file to replace the values of a core module’s declaration. While the system was design to allow this, it’s probably not what you want to do, so be careful with your own declaration files. For a full explanation of extends, see part one of our series.

Depends Sorting

At this point, we have a merged tree in $unsortedConfig that contains all the declared modules for our system, along with their code pools, active state, etc.

However, we’ve done nothing to merge this into our main configuration tree in $this->_xml. Before we do that, we need to reorder this tree such that modules which depend on other modules (configured with a <depends/> tag) appear later in the list of modules. Later on, this will control the order in which modules are loaded into the system, allowing one module to “depend” on another.

For example, if you look at the declaration file for the Mage_Bundle module, you can see it’s been made to depend on the Mage_Catalog module.

We’re going to skip looking into the specifics of _sortModuleDepends, but it’s worth investigating if you’re interested in that sort of thing. Once sorting is complete, the $moduleDepends array will look something like this

With a new XML “shell” tree built up, we loop over the sorted $moduleDepends data array, and use the information therein to append (again, not merge) information from the unsorted config into a new <modules> node in the new sorted config

The end results of which will be an updated global XML tree in $this->_xml property.

Loading the Modules

It wasn’t always pretty, but we now have a new <modules/> node in our global configuration tree. This is a list of modules, but we haven’t yet loaded each of our module’s config.xml files. That’s handled by the following two lines in the loadModules method

If you’re familiar with Magento, you may know the loadModulesConfiguration method can be used to merge together any XML configuration from all the installed modules. When called with a single parameter, Magento will return a merged configuration object. For example, my Simple Page module uses this method to fetch a list of routes from all simplepage.xml in the system.

However, if a configuration object is passed in as the second parameter, then Magento will use that object as the “caller” object for extends. As you can see above, the Mage_Core_Model_Config object passes itself in (as $this), which means the merged files will be added directly to the global config.

The first line of the above pair is another interesting bit of code to note. In versions prior to 1.6, this call looked a little different.

Rather than passing in an array of configuration file names, a single file named config.xml was passed in. Magento 1.6, however, passes in two file names. We’re getting a little ahead of ourselves, but these are the files Magento will attempt to load from each and every installed module’s etc folder. If we expand $resourceConfig‘s parameters, the call looks more like this

Magento 1.6 CE, in addition to merging in config.xml files, will also look for a config.mysql4.xml file. It’s unclear if this was an early attempt at breaking out the new database resource configuration into their own files, or if it’s a portent of things to come.

Regardless of all that, let’s take a look the loadModulesConfiguration method itself. To start with, Magento checks if “local” modules have been disabled.

As previously mentioned, this is the object that Magento will be merging each config.xml into. In our case this will be the Mage_Core_Model_Config object itself. Similarly, Magento sets up a “merge model” object

This is the object that will be used to initially load the individual config.xml files prior to merging. If that didn’t make sense don’t worry, all will become clear in a moment.

Before moving on, it’s (once again!) worth noting that we’re back to using the prototype cloning to create our merge objects. When loading the declared modules, Magento simply instantiated a Mage_Core_Model_Config_Base object. The core team moves in mysterious ways.

Merging into the Global Config

With our objects instantiated, we’re ready to go. Using the config object’s getNode method, we grab the (previously inserted) top level <modules/> node and all its children

This is why it was so vital our module information nodes be in a particular order. PHP’s children method will return a list of nodes in document order. In other words the order of the nodes in the document dictates the order each module’s config.xml will be loaded

The if conditional that surrounds the body of the foreach loop has a pretty obvious meaning. If the module isn’t active, then we skip this particular go through the loop. It’s worth looking at the implementation of the is method

Here we can see the call to $module->is('active') will return false if

There’s no <active/>; node

The <active/> node has a string value of ‘false‘

The <active/> node has a string value of ‘off‘

While there’s nothing particularly earth shattering in this code, it does serve as an example of why a developer might use the custom class feature of simple XML objects. Magento has given all their SimpleXML config objects this additional is method.

Back in the loop, if Magento determined local modules are disabled, it will continue on to the next iteration of the loop

Pausing again as an aside, this is a curious mixing of coding styles. On one hand, the loop is skipped for inactive modules by placing its contents in an if block. On the other hand, the way the loop is skipped for disabled local modules uses a continue. It’s likely that the local module disabling came later, and the second developer was unwilling to mess with the outer if clause.

Regardless, if we’re still here Magento will ensure our $fileName parameter is an array, even if we only requested the merging of a single file.

If $configFile doesn’t exist or is unreadable, loadFile will return false and the extend will be skipped.

Magento does this for each and every declared module. Notice that the extend method’s $override parameter is made explicitly true here. That means if there’s conflicts with specific nodes between modules, the value in the config.xml loaded later will win out. This is one of the many ways a module with a poorly configured config.xml can wreck a system, so be vigilant in making sure your nodes don’t conflict.

Cleaning Up

At this point, we have all our module’s config.xml nodes loaded into the system. We can start using getModel to instantiate objects, query the config for template files, or any of the other countless things that Magento needs its module configuration for. If you’ve ever wondered why the magento app model and configuration model are instantiated by regular PHP code

instead of using the getModel factory, this is why. Magento needs its module configuration in the global config before it can use its getModel factory pattern, and these are the classes that get that information into the configuration. This sort of chicken/egg problem is common in systems programming, and while it’s always easy to see a better way after the system’s been in use for a while, it’s not always easy to see that better way at the get go.

We’re not quite done with the loadModules method yet. After we get our configuration loaded, there’s two more things that happen. First, there’s this bit of code

Here Magento is remerging the app/etc/local.xml file we loaded back in loadBase (see part one of the series). Magento reloads this file in case any of the loaded modules inadvertently or maliciously replace some local.xml nodes via extend. Without this in place, a module author could replace/ruin important information in local.xml (database connection information, etc.).

From what I can see, this feature hasn’t been used by core code since at least Magento 1.3.4 CE, and it’s unclear if it ever was. My instincts say this was an early system that would provide an explicit path for third party developers to overwrite existing configuration nodes with their own values. For whatever reasons, this was abandoned/never-adopted, but it remains a curious artifact. If anyone knows the history and/or current usage of this feature, use the comments below or get in touch.

Wrap Up

So, that’s our module configuration loaded into the Magento global config. As you can see, the code is less than elegant in places, but it gets the job done. You can also start to see why Magento relies so heavily on caching. Without configuration caching in place a Magento store would start bottlenecking at disk access in a heartbeat. as each configuration file was loaded for each individual http request.

If you think we’re done with the loading of the global config — you’re wrong!

Magento’s system configuration variables were an early topic here, but I never covered how these values are persisted in Magento, or how they’re read back out of the database. It turns out Magento’s global configuration tree plays a huge role in this vital Magento sub-system, which will be the topic of our next article.