A free and open-source book on ZF3 for beginners


3.4. PHP Class Autoloading

A web application consists of many PHP classes, and each class typically resides in a separate file. This introduces the need of including the files.

For example, let's assume we have the file named Application.php which contains the definition for the \Zend\Mvc\Application class from the previous section. Before you can create an instance of the Application class somewhere in your code, you have to include the contents of Application.php file (you can do this with the help of require_once statement, passing it the full path to the file):

<?php
require_once "/path/to/zendframework/zend-mvc/src/Application.php";

use Zend\Mvc\Application;

$application = new Application();

As your application grows in size, it may be difficult to include each needed file. Zend Framework 3 itself consists of hundreds of files, and it can be very difficult to load the entire library and all its dependencies this way. Moreover, when executing the resulting code, PHP interpreter will take CPU time to process each included file, even if you don't create an instance of its class.

To fix this problem, in PHP, the class autoloading feature has been introduced. The PHP function spl_autoload_register() allows you to register an autoloader function. For complex websites, you even can create several autoloader functions, which are chained in a stack.

During script execution, if PHP interpreter encounters a class name which has not been defined yet, it calls all the registered autoloader functions in turn, until either the autoloader function includes the class or "not found" error is raised. This allows for "lazy" loading, when PHP interpreter processes the class definition only at the moment of class invocation, when it is really needed.

3.4.1. Class Map Autoloader

To give you an idea of how an autoloader function looks like, below we provide a simplified implementation of an autoloader function:

<?php
// Autoloader function.
function autoloadFunc($className) 
{
    // Class map static array.
    static $classMap = [
        '\\Zend\\Mvc\\Application' => '/path/to/zendframework/zend-mvc/src/Zend/Mvc/Application.php',
        '\\Application\\Module' => '/path/to/app/dir/module/Application/Module.php',
        //...
    ];

    // Check if such a class name presents in the class map.
    if(isset(static::$classMap[$className])) {
        $fileName = static::$classMap[$className];
  
        // Check if file exists and is readable.
        if (is_readable($fileName)) {
            // Include the file.
            require $fileName;
        }
    }
}

// Register our autoloader function.
spl_autoload_register("autoloadFunc");

In the above example, we define the autoloadFunc() autoloader function, which we will further refer to as the class map autoloader.

The class map autoloader uses the class map for mapping between class name and absolute path to PHP file containing that class. The class map is just a usual PHP array containing keys and values. To determine the file path by class name, the class map autoloader just needs to fetch the value from the class map array. It is obvious, that the class map autoloader works very fast. However, the disadvantage of it is that you have to maintain the class map and update it each time you add a new class to your program.

3.4.2. PSR-4 Standard

Because each library's vendor uses its own code naming and file organization conventions, you will have to register a different custom autoloader function per each dependent library, which is rather annoying (and actually this is an unneeded work). To resolve this problem, the PSR-4 standard was introduced.

PSR stands for PHP Standards Recommendation.

The PSR-4 standard defines the recommended code structure that an application or library must follow to guarantee autoloader interoperability. In two words, the standard says that:

For example, for the Zend\Mvc\Application class, you will have the following directory structure:

/path/to/zendframework/zend-mvc/src
  /Zend
    /Mvc
       Application.php

The disadvantage of this is that you need to put your code in multiple nested directories (Zend and Mvc).

To fix this, the PSR-4 allows you to define that a contiguous series of one or more leading namespace and sub-namespace names corresponds to a "base directory". For example, if you have the \Zend\Mvc\Application fully qualified class name, and if you define that the series \Zend\Mvc corresponds to the "/path/to/zendframework/zend-mvc/src" directory, you can organise your files as follows:

/path/to/zendframework/zend-mvc/src
    Application.php

For the code conforming to the PSR-4 standard, we can write and register an autoloader, which we will refer to as the "standard" autoloader:

<?php

// "Standard" autoloader function.
function standardAutoloadFunc($className) 
{
    // Replace the namespace prefix with base directory.
    $prefix = '\\Zend\\Mvc';
    $baseDir = '/path/to/zendframework/zend-mvc/src/';
    if (substr($className, 0, strlen($prefix)) == $prefix) {
        $className = substr($className, strlen($prefix)+1);
        $className = $baseDir . $className;
    }

    // Replace namespace separators in class name with directory separators.
    $className = str_replace('\\', DIRECTORY_SEPARATOR, $className);
  
    // Add the .php extension.
    $fileName = $className . ".php";
  
    // Check if file exists and is readable.
    if (is_readable($fileName)) {
        // Include the file.
        require $fileName;
    } 
}

// Register the autoloader function.
spl_autoload_register("standardAutoloadFunc");

The standard autoloader works as follows. Assuming that the class namespace can be mapped to the directory structure one-by-one, the function calculates the path to PHP file by transforming back-slashes (namespace separators) to forward slashes (path separators) and concatenating the resulting path with the absolute path to the directory where the library is located. Then the function checks if such a PHP file really exists, and if so, includes it with the require statement.

It is obvious, that the standard autoloader works slower than the class map autoloader. However, its advantage is that you don't need to maintain any class map, which is very convenient when you develop new code and add new classes to your application.

Zend Framework 3 conforms to PSR-4 standard, making it possible to use standard autoloading mechanism across all its components. It is also compatible with other PSR-4 conforming libraries like Doctrine or Symfony.

3.4.3. Composer-provided Autoloader

Composer can generate autoloader functions (both class map autoloaders and PSR-4 standard autoloaders) for the code you install with it. Zend Framework 3 uses the autoloader implementation provided by Composer. When you install a package with Composer, it automatically creates the file APP_DIR/vendor/autoload.php, which uses the spl_autoload_register() PHP function to register an autoloader. This way all PHP classes located in APP_DIR/vendor directory are correctly autoloaded.

To autoload PHP classes located in your own modules (like Application module), you'll have to specify the autoload key in your composer.json file:

"autoload": {
    "psr-4": {
        "Application\\": "module/Application/src/"
    }
},

Then the only thing need to be done is to include that file in your website entry script index.php:

// Composer autoloading
include __DIR__ . '/../vendor/autoload.php';

The autoload.php file is generated each time you install a package with Composer. Besides that, to make Composer generate the autoload.php file, you may need to execute the dump-autoload command:

php composer.phar dump-autoload

3.4.4. PSR-4 and Module's Source Directory Structure

In Zend Skeleton Application, you can see how the PSR-4 standard is applied in practice. For the default module of your website, the Application module, PHP classes which are registered with the standard autoloader are stored under the APP_DIR/module/Application/src directory ("src" abbreviation means "source").

We will refer to the src directory as module's source directory.

For example, lets look at the IndexController.php file of Application module (figure 3.2).

Figure 3.2. Skeleton application's directory structure conforms to PSR-4 standard Figure 3.2. Skeleton application's directory structure conforms to PSR-4 standard

As you can see, it contains the IndexController class 1 belonging to Application\Controller namespace. To be able to follow the PSR-4 standard and use the standard autoloader with this PHP class, we have to put it under the Controller directory under the module's source directory.

1) IndexController class is the default controller for the skeleton website. We will talk about controllers later in chapter Model-View-Controller.


Top