Magento Autoloading

Magento makes use of PHP 5’s autoloading feature to include classes. One of the more irritating aspects of a large application is including class files at the top of each script every time you create a new class. Autoloading eliminates this problem. This article represents how Magento uses its Varien_Autoload class.

Firstly, Magento sets its include paths to be in four different directories. This can be seen within the Mage.php class.

/**
 * Set include path
 */
$paths = array();
$paths[] = BP . DS . 'app' . DS . 'code' . DS . 'local';
$paths[] = BP . DS . 'app' . DS . 'code' . DS . 'community';
$paths[] = BP . DS . 'app' . DS . 'code' . DS . 'core';
$paths[] = BP . DS . 'lib';

$appPath = implode(PS, $paths);
set_include_path($appPath . PS . Mage::registry('original_include_path'));

So we have /app/code/local, /app/code/community, /app/code/core and /lib directories.

Note that the constants BP and DS are defined near the top of the Mage.php file. BP is the base path (i.e. your Magento root directory) and DS is the directory separator.

It’s also worth noting the order of which the paths are included. If you’re familiar with class overrides in Magento, you’ll know that common practice is to use the local code pool. The reason for this is because of the code above.

Included files are looked for firstly in the local directory, followed by community and so on.

A bit further down the Mage.php file, we come across an include to the Varien_Autoload class.

include_once "Varien/Autoload.php";

And just below this inclusion, we come a register() method of Varien_Autoload.

Varien_Autoload::register();

If we head to this class, the register() method contains the in built spl_autoload_register() function as we can see below.

/**
 * Register SPL autoload function
 */
static public function register()
{
    spl_autoload_register(array(self::instance(), 'autoload'));
}

So the first parameter of the spl_autoload_register() function is a value returned from the static instance() method, which is a new instance of the Varien_Autoload class.

/**
 * Singleton pattern implementation
 *
 * @return Varien_Autoload
 */
static public function instance()
{
    if (!self::$_instance) {
        self::$_instance = new Varien_Autoload();
    }
    return self::$_instance;
}

The magic __construct() method is then called that delegates to the registerScope() method passing in the $_scope property which is set to default, found near the top of the Varien_Autoload class.

/**
 * Class constructor
 */
public function __construct()
{
    register_shutdown_function(array($this, 'destroy'));
    $this->_isIncludePathDefined = defined('COMPILER_INCLUDE_PATH');
    if (defined('COMPILER_COLLECT_PATH')) {
        $this->_collectClasses  = true;
        $this->_collectPath     = COMPILER_COLLECT_PATH;
    }
    self::registerScope(self::$_scope);
}

The registerScope() method checks if the compiler include path constant has been defined and if it has, we include the php class files within this path.

static public function registerScope($code)
{
    self::$_scope = $code;
    if (defined('COMPILER_INCLUDE_PATH')) {
        @include COMPILER_INCLUDE_PATH . DIRECTORY_SEPARATOR . self::SCOPE_FILE_PREFIX.$code.'.php';
    }
}

So returning back to the register() method, let’s focus on the second paramater which is autoload. This parameter is the registered function.

/**
 * Load class source code
 *
 * @param string $class
 */
public function autoload($class)
{
    if ($this->_collectClasses) {
        $this->_arrLoadedClasses[self::$_scope][] = $class;
    }
    if ($this->_isIncludePathDefined) {
        $classFile =  COMPILER_INCLUDE_PATH . DIRECTORY_SEPARATOR . $class;
    } else {
        $classFile = str_replace(' ', DIRECTORY_SEPARATOR, ucwords(str_replace('_', ' ', $class)));
    }
    $classFile.= '.php';
    //echo $classFile;die();
    return include $classFile;
}

The important part here is to take note if how Magento converts the included path classes to class names. If we ignore the compiler include path check for now, we can see the line below.

$classFile = str_replace(' ', DIRECTORY_SEPARATOR, ucwords(str_replace('_', ' ', $class)));

If you uncomment the echo $classFile;die(); line within this method, you can see that it outputs Varien/Profiler. The Varien_Profiler class is the first class to get autoloaded using this method.

So the string conversions that take are as follows.

The $class variable that gets passed in the autoload() method contains in this example, Varien_Profiler. This gets converted to Varien Profiler via the inner str_replace() function.

ucwords(str_replace('_', ' ', $class))

Varien Profiler then becomes Varien/Profiler via the outer str_replace() function.

str_replace(' ', DIRECTORY_SEPARATOR, ucwords(str_replace('_', ' ', $class)));

At the end of the autoload() method, we then append a .php extensions onto the end of the class file and the class file gets included and returned.

$classFile.= '.php';
//echo $classFile;die();
return include $classFile;

If we couple this with one of our included paths that we saw in Mage.php, we can see that a full path of lib/Varien/Autoload.php is established.

If you enable and run compilation within System -> Tools -> Compilation, by checking the includes/src folder from the Magento root directory, you will get an idea of the class files that are loaded in the autoloading process.

Note: This article is based on Magento Community/Open Source version 1.9.