Magento Autoloader and Compilation Process

In this blog post we will see in detail how magento auto loaders works and how the magento compilation process helps to speed up magento class loading.

PHP Autoloader

Before we get into details of magento autoloder, lets see what does auto loading mean and for what purpose is it used.
Usually in a typical oops based php application, classes are distributed across different php files. So when ever you want to use a class you need to “require” or “include” the php file it is defined in. Problem comes up in big application when you have to require 100’s of external php files in a single page, you cannot write 100’s of require statements on each page.
To solve this problem, PHP5 introduced the concept class auto loading. For this in a OOP application, based on the class name php tries to find the path of the php file automatically and includes it. So using this we don’t have to include each external file rather php automatically does it for us. To implement this we need to create a “autoload” function which based on class name find the php file path.
Read more about it here http://php.net/manual/en/language.oop5.autoload.php
Important thing to note, functions to implement autoloading in php are “__autoload()” and “spl_autoload_register()”
Now lets see how magento does this.

Autoloading In Magento

There are two main files in magento for auto loading
1. “Varien_Autoload” class located at “lib\Varien\Autoload.php”.
2. Mage.php file located at ‘app/Mage.php’

Open the files “index.php”, “lib\Varien\Autoload.php” and “app/Mage.php” for reference.

First lets look at the Autoload.php file
There are two important functions in this

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

above we define the function to use to autoload classes

public function autoload($class)
    {
        if ($this->_collectClasses) {
            $this->_arrLoadedClasses[self::$_scope][] = $class;
        }
        // $this->_isIncludePathDefined = defined('COMPILER_INCLUDE_PATH');
        if ($this->_isIncludePathDefined) {
            $classFile =  COMPILER_INCLUDE_PATH . DIRECTORY_SEPARATOR . $class;
        } else {
            $classFile = str_replace(' ', DIRECTORY_SEPARATOR, ucwords(str_replace('_', ' ', $class)));
        }
        $classFile.= '.php';
        return include $classFile;
    }

As you can see the above function checks fir if $this->_isIncludePathDefined is true, then it directly loads from “COMPILER_INCLUDE_PATH” else it converts class names into directory and then includes the file. To convert class name into directory, it replaces all underscores with spaces, and then spaces with “/”

Basically,
Varien_Profiler converts to Varien\Profiler.php
Mage_Catalog_Block_Breadcrumbs converts to Mage\Catalog\Block\Breadcrums.php
Mage_Catalog_Block_Product_New converts to Mage\Catalog\Block\Product.New.php
Every class is convert to its directory.

In this auto load function, we convert a class name to full php file location and then include it. But one important thing is still missing. How does magento know inside which folders to search these php file.
e.g Mage\Catalog\Block\Product.New.php needs to searched inside “app/code/core/” folder, similarly Varient/Profiler.php needs to be searched inside “lib/Varien” folder. So magento needs to define somewhere where php needs to search for all these classes. This is done in Mage.php file

This is code which you find at the top in Mage.php

    $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'));
    include_once "Mage/Core/functions.php";
    include_once "Varien/Autoload.php";

    Varien_Autoload::register();

As it can be seen we first set the include path in php for “local”,”community”,”core”,”lib” and then call register() function in “Autoload” class.

TIP: It is also important to the order in which paths are set. First comes “local” folder, then “community” and then “core”. So if a class is first found in “local” it will read it from there and so on. This is also an important concept which is used in magento development

So above covers the entire concept of class auto loading in magento.

Magento Compilation Process

Magento has a compilation process located “System -> Tools -> Compilation”. This process is mainly used to increase the class loading time in magento and hence overall speed. Lets see how it works in detail.

First to enable compilation, you need to click on “Run Compilation Process” in magento admin. This will take a long amount of time to process, once done you can Enable/Disable the complication.

Lets now see what actually happens behind the scene. When you “Run Compilation Process”, magneto copies all you class files, model files, helper, blocks, lib files to “includes/src” folder. If you open your “includes/src” folder, you will see all class directly inside a single folder. You will see files with name like Mage_Catalog_Block_Breadcrum.php, Varien_Profile.php etc directly in a single folder itself. So basically magento removes all complex directory structures, overwrites, etc etc and simply copies all classes into a single directory. This reduces the time taken to find and load classes.
I am doubtful if this really makes a significant difference in speed

How Is This Implemented In Code
In the folder “/includes” there is a file called “config.php”. If you open this file there is a line

#define('COMPILER_INCLUDE_PATH', dirname(__FILE__).DIRECTORY_SEPARATOR.'src');

When compilation is Enabled, this line is un-commented and when compilation is Disabled this line is commented.
This file is directly included in “index.php” in magento

$compilerConfig = MAGENTO_ROOT . '/includes/config.php';
if (file_exists($compilerConfig)) {
    include $compilerConfig;
}

Next in “Mage.php” file you will see the code

if (defined('COMPILER_INCLUDE_PATH')) {
    $appPath = COMPILER_INCLUDE_PATH;
    set_include_path($appPath . PS . Mage::registry('original_include_path'));
    include_once COMPILER_INCLUDE_PATH . DS . "Mage_Core_functions.php";
    include_once COMPILER_INCLUDE_PATH . DS . "Varien_Autoload.php";
} else {
    /**
     * 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'));
    include_once "Mage/Core/functions.php";
    include_once "Varien/Autoload.php";
}

As you can see above, if “COMPILER_INCLUDE_PATH” is defined ‘local’, ‘community’,’core’,’lib’ all these are not included rather the ‘COMPILER_INCLUDE_PATH’ is added to php class path.
Next in the Varient/Autoload.php the “autoload” function works like

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';
        return include $classFile;
    }

Even in this if ‘COMPILER_INCLUDE_PATH’ is defined, we include class directory from ‘COMPILER_INCLUDE_PATH’ directory. Hence when compilation is enabled all classes are directly read from “includes/src” directory.

So above we see how auto loading in magento works, how compilation process in magento works and how class names are converted to directory paths in magento.