Ransford Okpoti's Blog

26 May, 2011

How to use PHP’s __autoload

Filed under: PHP — ranskills @ 4:05 pm

Classes used in this posting may refer to classes or interfaces where appropriate.

Ever wondered if there could be a more appropriate way to include dependent files into other PHP files other than using multiple include or require language constructs? Well, wonder no more, the __autoload magic function in PHP5 comes to the rescue. In simple words, this method/function gets invoked anytime the execution reaches a line containing a class or an interface for which the interpreter does not know of it’s existence either because it has not been defined in the same file or it’s been defined in a separate file ,but has not been included in the current executing script/file.

The __autoload function allows for the lazy loading of classes, the classes only gets loaded when they are needed.

Lets look at some examples. In the code below, the __autoload function will never be called because there is only one class which has been defined within the file itself, so the interpreter knows of its definition.

<?php
function __autoload($class){
    echo 'Loading Interface/Class with name '.$class;
}

class Address{
}
?>

With this second example, the PHP interpreter does not known the definition of the interface IAddress, so it calls the __autoload function passing it the name IAddress, but since the function does not do anything yet except output the string Loading Interface/Class with name IAddress , and raises the error:

Interface ‘IAddress’ not found in path/to/script

<?php
function __autoload($class){
    echo 'Loading Interface/Class with name '.$class;
}

class Address implements IAddress{

}
?>

If the definition of the interface IAddress was in a different file say IAddress.php and in the same directory as our current script, we would have had the following code below.

<?php
include_once 'IAddress.php';
class Address implements IAddress{

}
?>

Lets look at another script which requires several classes defined in the classes directory.

<?php
include_once 'classes/Address.php';
include_once 'classes/Person.php';
include_once 'classes/ShoppingCart.php';

$addr = new Address();
?>

From the above code, it should hit you that if your script needs several classes/interfaces, that have rightly been, defined in their own separate files, then you’ll have to manually include all of them, so you could end up with 15 or 20 or more lines of include_once/require_once in your code. This definitely doesn’t sound exciting, you could argue that you could add the classes directory to your include path using the set_include_path function, but it still doesn’t take away the fact that you’ll have to include the class files using any of the file inclusion statements.

Now, lets solve the simple problem above before moving to a more complex scenario.

To quickly solve this, all we do is to remove all the include/require lines from the above code and add the __autoload function.

<?php
function autoLoader($class_name){
    include_once('classes/'.$class.'.php');
}

spl_autoload_register('autoLoader');

$addr = new Address();
?>

[notice type=attention]
For this to work, the class name should lead us to easily predict the name of the file to be included i.e., the class name must be part of the file name. So, a class named Address stored in
address.php,
address.class.php,
address.inc.php or
class.address.php
is more helpful than storing it in some_unrelated_name.php .
[/notice]
This works perfectly now, but it won’t work in all scenarios because

1. All classes may not be in a single directory called classes, unless you decide to do it so.

2. All classes may not be named in this format: class_name.php eg. Address.php, Person.php, etc. What happens if there are mixed class naming formats like Address.class.php, class.Address.php, Address.inc.php or any other format that the developer(s) of the class used.

While we are looking at situations that might break our seemingly wonderful solution to the class file inclusion nightmare, lets look at a class name like Zend_Log, Log_display or Archive_Tar, you can quickly take a look at the class names in the PEAR package/directory in relation to the directory they are defined in. The naming convention adopted was to solve the problem of class name clashes. The naming convention is: [directory_]+class
where the + implies that there must be at least one directory before the class name.
Here, the naming gives information about the directory that contains the actual class eg. here are some class names and their implied locations

Zend_Log -> Zend/Log.php

Calendar_Month_Weekdays -> Calendar/Month/Weekdays.php

Log_display -> Log/display.php

To solve this, we define our own autoloader function to try and load the required classes/interfaces. The code is shown below. Lets name the script autoloader.php

<?php
/**
 *
 * @param string $className Class or Interface name automatically
 *              passed to this function by the PHP Interpreter
 */
function autoLoader($className){
    //Directories added here must be relative to the script going to use this file
    $directories = array(
      '',
      'classes/'
    );

    //Add your file naming formats here
    $fileNameFormats = array(
      '%s.php',
      '%s.class.php',
      'class.%s.php',
      '%s.inc.php'
    );

    // this is to take care of the PEAR style of naming classes
    $path = str_ireplace('_', '/', $className);
    if(@include_once $path.'.php'){
        return;
    }
    
    foreach($directories as $directory){
        foreach($fileNameFormats as $fileNameFormat){
            $path = $directory.sprintf($fileNameFormat, $className);
            if(file_exists($path)){
                include_once $path;
                return;
            }
        }
    }
}

spl_autoload_register('autoLoader');
?>

Now, all we have to do is to include autoloader.php into any of the scripts what will be making use of other classes/interfaces.

<?php
include_once 'autoloader.php';

$addr = new Address();
$shoppingCart = new ShoppingCart();
$person = new Person();

$person->setAddress($addr);
$person->setShoppingCart($shoppingCart);

?>

[notice type=download]
Download autoloader.php
[/notice]

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: