Blog |

drapeko

@author drapeko

Autoloading in PHP

with 18 comments

computer_mainHave you ever been faced with autoloading in PHP? Are you a newbie? Do you think you understand the nature of autoloading? This article is for you. This article will take approximately 25-35 min.
This article:

  1. Asks you: what is an autoloading? Do you really understand how __autoload() function works?
  2. Examines several autoloading approches
  3. Suggests a script that scans folders for classes and interfaces and generates array of associations between classes and their locations.

Autoloading/Dependency generator: download (4.6 KB) 07 05 2009 →, description

Examples for this article: download (4.0 KB) →, description ↓

Two problems

Let’s start from the conception and the approach PHP offers us.

You have to load a class before an attempt to use it is made, don’t you? How could you load it? You are likely to face two problems:

  1. You don’t know where the class/interface is located
  2. You cannot identify the moment when the loading is required

PHP 5 easily solves the second problem. Below you can see a quote from http://uk2.php.net/autoload

“Many developers writing object-oriented applications create one PHP source file per-class definition. One of the biggest annoyances is having to write a long list of needed includes at the beginning of each script (one for each class).

In PHP 5, this is no longer necessary. You may define an __autoload function which is automatically called in case you are trying to use a class/interface which hasn’t been defined yet. By calling this function the scripting engine is given a last chance to load the class before PHP fails with an error.”

Moreover PHP 5.1 gives a chance to define your own autoload funtion by using spl_autoload_register() from SPL library. Why do you need it? Imagine the situation when you want to use together two libraries from different vendors that use __autoload function. What might you get? Absolutely right! You have a chance to get an error for redefining an already defined function.

Check yourself

I have several questions for you. I believe you will cope with them in seconds.

We have the following structure of the folders and files (you can download all examples here):

  Project
  ..Classes
  ....Children
  ......A.php                    class A implements I1 {}
  ....B.php                      class B implements I2 {}
  ....Class_C.php                class C extends B implements I1, I3 {}
  ..Interfaces
  ....I1.php                     interface I1 {}
  ....blablabla.php              interface I2 extends I1 {}
  ....I3.php                     interface I3 {}

First question

On the right side you can see the content of the files.  What result will you get if you invoke the script below?


<?php

  /**
  * first question
  */

  function __autoload($className) {
    echo "1, "; require_once 'Project/Interfaces/I3.php';
    echo "2, "; require_once 'Project/Classes/B.php';
    echo "3, "; require_once 'Project/Classes/Children/A.php';
    echo "4, "; require_once 'Project/Interfaces/I1.php';
    echo "5, "; require_once 'Project/Interfaces/blablabla.php';
  }

  $x = new A();
?>

The answer is 1, 2, 1, 2, 3, 1, 2, 3, 4, 5, 4, 5, 3, 4, 5. Have you got it right? If so, my congratulations. If no, don’t worry, we will examine this now.

An explanation of the first example.

An autoload function is called to load class A.

  1. “1″ is printed. Interface I3 is loaded.
  2. “2″ is printed. Made attempt to load class B, but B could not be loaded without I2 interface. Another function is called to load I2 interface.
    1. “1″ and “2″ are printed
    2. “3″ is printed. Made attempt to load class A. Another function is called to load I1 interface
      1. “1″, “2″, “3″ are printed.
      2. “4″ is printed. I1 is loaded, the goal is achieved.
      3. “5″  is printed. I2 is loaded. Function finishes the job.
    3. Class C is completely loaded. “4″ and “5″ are printed, as all the necessary classes are loaded. Function finishes the job.
  3. Class B is completely loaded. “3″, “4″, “5″ are printed. Function finishes the job.

Second question

Let’s take the first example and move the third line to the first position, and renew the numeration in echo statements.


<?php
  /**
   * second question
   */
  function __autoload($className) {
    echo "1, "; require_once 'Project/Classes/Children/A.php';
    echo "2, "; require_once 'Project/Interfaces/I3.php';
    echo "3, "; require_once 'Project/Classes/B.php';
    echo "4, "; require_once 'Project/Interfaces/I1.php';
    echo "5, "; require_once 'Project/Interfaces/blablabla.php';
  }

  $x = new A();
?>

The right answer is 1, 1, 2, 3, 1, 2, 3, 4, 5, 4, 5, 2, 3, 4, 5.

An explanation of the second example.

  1. “1″ is printed. Made attempt to load class A, but A could not be loaded without I1 interface. Another function is called to load I1 interface.
    1. “1″ is printed.
    2. “2″ is printed. I3 is loaded.
    3. “3″ is printed. Made attempt to load class B, but B could not be loaded without I2. Another function is called to load I2 interface.
      1. “1″, “2″, “3″ are printed
      2. “4″ is printed. I1 is loaded.
      3. “5″ is printed. I2 is loaded. Function finishes the job.
    4. B is completely loaded. “4″ and “5″ are printed.
  2. A is completely loaded. “2″, “3″, “4″ and “5″ are printed.

Third question

Ok, be patient. :) The last example. Let’s take the second example and move the last line to the second position, and renew the numeration in echo statements.


<?php

  function __autoload($className) {
    echo "1, "; require_once 'Project/Classes/Children/A.php';
    echo "2, "; require_once 'Project/Interfaces/blablabla.php';
    echo "3, "; require_once 'Project/Interfaces/I3.php';
    echo "4, "; require_once 'Project/Classes/B.php';
    echo "5, "; require_once 'Project/Interfaces/I1.php';
  }

  $x = new A();
?>

What example have you got? Was it expected? I advise you to invoke this code yourself.

An explanation of the third example.

  1. “1″ is printed. Made attempt to load class A, but A could not be loaded without I1 interface. Another function is called to load I1 interface.
    1. “1″ is printed.
    2. “2″ are printed. Made attempt to load interface I2, but I2 could not be loaded without I1. Another function won’t be called. We got a fatal error :)

The result is very interesting indeed. We found at least one example, when we get the fatal error.The simplest conclusion is that everything depends on ordering of the statements.  Has not anybody been interested how autoload works when one class extends another one? I would like to ask the audience where could I find the comprehensive guide to autoload mechanism logic. Unfortunately my quest has not given me considerable results.

all examples can be downloaded here.

Naming standards

Nowadays there are several popular approaches to solve the first problem, the location problem. First of them is an usage of the specific naming standards.

Zend approach

The most popular approach was suggested by Zend. The  location of the file is simply encoded in the name of the class. Below you can see the example which draws an idea of this approach.


// Main.class

function __autoload($class_name) {
    $path = str_replace('_', DIRECTORY_SEPARATOR, $class_name);
    require_once $path.'.php';
}

$temp = new Main_Super_Class();

All “_” signs are replaced with the directory separator. For example, this function will look for Main_Super_Class class in Main/Super/Class.php file.

The main drawback is that you have to know the complete structure of folders before the code construction process. All the time you use names of  classes in the code you hardcode locations of these classes. Therefore if the structure of the folders is changed you will have to modify the source code.

‘Include all’ approach

If you work in the development environment or a speed just is not the thing you pursue it’s very convenient to include all class files in specific folders before the execution of the script. A class file could be identified by the fragment of the file’s name (for example, by ‘.class’ prefix before the extension).

Below you can see the example of this realization.


<?php
  $arr = array (
    'Project/Classes',
    'Project/Classes/Children',
    'Project/Interfaces'
  );

  foreach($arr as $dir) {
    $dir_list = opendir($dir);

    while ($file = readdir($dir_list)) {
      $path = $dir.DIRECTORY_SEPARATOR.$file;
      if(in_array($file, array('.', '..')) || is_dir($path))
        continue;

      if (strpos($file, ".class.php"))
        require_once $path;
    }
  }
?>

But this approach does not work. I’ve just wanted to check, how attentively you read. I’m sorry for that. :)

The reason is that PHP requires to follow the order of the including. If class A extends class B,  you must include B before A class including. Now I think it’s clear why PHP invokes autoload function all the time the class is not loaded. It solves ordering problem. You can try it yourself by invoking the above script. If you want to ask why not to incorporate the above code into autoload function, please examine very carefully the third example once more.

Associations between files ant their locations

At last we reached the paragraph for the sake of which this article was written.

Another approach is to store the location of classes somewhere in configuration file. For example,


// configuration.php
array_of_associations = array(
  'MainSuperClass' = 'C:/Main/Super/Class.php',
  'MainPoorClass' = 'C:/blablabla/gy.php'
);

I think you understand minuses of this approach. You always have to support the correctness of this array. If the number of classes is tremendous it’s very difficult to do. But it’s better than to hardcode locations in the code, is not it? Ok, it’s philosophical question. It’s up to you. :)

But I like it very much. Locations are stored in one place and they are not incorporated into the code. You can change  data without modifying the code. You can store the locations in array, xml, yaml, json etc.

The sample realization of autoload function is shown below.


<?php
  require 'autoload_generated.php';

  function __autoload($className) {
    global $autoload_list;
    require_once $autoload_list[$className];
  }

  $x = new A();
?>

But I do not like to watch over such things manually. What we can do in this situation? You probably know the answer.

Generate association array

Generator

We need a special script for generating this array. I think you will be very glad to hear that I realized it.

This light script scans folders and detects interfaces/classes, their locations and classes/interfaces that are extended/implemented using simple regular expressions. You can download it from the store.

How does it work? Let me give you an example. If you issue this command

E:projectsscriptsautoload_generator>php auto_generator.php Project true autoload_generated.php

parameters number: 4

target path: Project
recursive: true
output: file
result file: autoload_generated.php

You will get this result


<?php 

$autoload_list = array (
  'classes' => array (
    'A' => array ('path' => 'Project/Classes/Children/A.php',
      'extends' => array (), 'implements' => array ('I1')),
    'B' => array ('path' => 'Project/Classes/B.php',
      'extends' => array (), 'implements' => array ('I2')),
    'C' => array ('path' => 'Project/Classes/C.php',
      'extends' => array ('B'), 'implements' => array ('I1', 'I3')),
  ),
  'interfaces' => array (
    'I2' => array ('path' => 'Project/Interfaces/blablabla.php', 'extends' => array ('I1')),
    'I1' => array ('path' => 'Project/Interfaces/I1.php', 'extends' => array ()),
    'I3' => array ('path' => 'Project/Interfaces/I3.php', 'extends' => array ()),
  ),
);
?>

Do you like it? You can download this script here. But dont’ leave us, you will get something more. :)

What are the incoming parameters?

Position Type Default value Description
1 string hardcoded location, root directory a source dir path (folder where to search for the interfaces and classes)
2 boolean false is the search recursive or not
3 boolean/string true Where the result will be stored.
If false, outputs the result; if true, result is stored in the file, default destination is used.
If string, result is stored in this file.
4 string hardcoded name (“generated_array”) a name of the generated array

For more information regarding this script please visit the store [link].

Example of the autoload function is shown below.


<?php

require 'autoload_generated.php';

function __autoload($className) {

  global $autoload_list;

  if (array_key_exists($className, $autoload_list["classes"])) {
    require_once $autoload_list["classes"][$className]['path'];

  } elseif (array_key_exists($className, $autoload_list["interfaces"])) {
    require_once $autoload_list["interfaces"][$className]['path'];

  } else {
    throw new Exception("Error");
  }
}

$x = new A();
?>

Extended generator

It’s very easy to create extended generator on the above described basis. For example, in the code shown below I added scanning in several independent folders feature.


$v_dirs = array (
   array(
      'path' => 'D:/from_computer/rom_projects/dialogue',
      'recursive' => true
   ),array(
      'path' => 'D:/from_computer/dangaus/app',
      'recursive' => true
   )
);

$arr = array('classes' => array(), 'interfaces' => array());
foreach ($v_dirs as $dir) {

   unset($output);
   exec(
      "php auto_generator.php"
      ." "{$dir['path']}""                       // 1st arg (target location)
      .($dir['recursive'] ? " true" : " false")    // 2nd arg (recursive)
      ." false"                                     // 3rd arg (save destination)s
      ." temp_array"
      , $output
   );
}

Full code can be downloaded below.

Conclusion

We have deepened into autoload function behavior, considered several solutions of the location problem and in the end of this article I offered several scripts for automatic construction of the assocations between classes and their locations. Operation of associative array construction is a rough one. That’s why I don’t recommend using these scripts or their analogues in the autoload function with the exception of working in other than the development environment. These scripts are designed to be a pre-condition for invoking your programs.

I hope this article has been helpful for you. I understand that it’s far from perfect one. But together could make it a little bit better. If you have faced with problems or if you have any comments you are greatly welcome.

Read the bellow paragraph to download all examples and scripts mentioned in this article.

Necessary Files

Simple and extended generators: download (4.6 KB) 07 05 2009 →, description

Examples for article: download (4.0 KB) →, description ↓

Generators

autoload_generator.rar,  download (4.6 KB) →

Archive contains two files: auto_generator.php (version 1.2) (description ) and auto_ext_generator.php (version 1.1) (description )

auto_generator.php

This script is specially designed to scan folders and identify locations of the classes by using regular expressions. The result produced by the script is an associative array of classes/interfaces, their locations and extended/implemented classes/interfaces. The sample result is shown below:


<?php 

$autoload_list = array (
  'classes' => array (
    'A' => array ('path' => 'ProjectClassesChildrenA.php',
      'extends' => array (), 'implements' => array ('I1')),
    'B' => array ('path' => 'ProjectClassesB.php',
      'extends' => array (), 'implements' => array ('I2')),
    'C' => array ('path' => 'ProjectClassesC.php',
      'extends' => array ('B'), 'implements' => array ('I1', 'I3')),
  ),
  'interfaces' => array (
    'I2' => array ('path' => 'ProjectInterfacesblablabla.php', 'extends' => array ('I1')),
    'I1' => array ('path' => 'ProjectInterfacesI1.php', 'extends' => array ()),
    'I3' => array ('path' => 'ProjectInterfacesI3.php', 'extends' => array ()),
  ),
);
?>
Requirements

What do you need to use it? You have to have installed PHP server and ability to use command line tool. :) Script was written using PHP 5.2 version but probably it would work with all 5th versions. If you check on lower that 5.2 versions, please give a feedback.

Incoming parameters

What are the incoming parameters?

Position Type Default value Description
1 string hardcoded location, root directory a source dir path (folder where to search for the interfaces and classes)
2 boolean false is the search recursive or not
3 boolean/string true Where the result will be stored.
If false, outputs the result; if true, result is stored in the file, default destination is used.
If string, result is stored in this file.
4 string hardcoded name (“generated_array”) a name of the generated array
Default behavior

If you want to change the default behavior of the script, you could change values of the variables that are marked // CHANGABLE


  // CHANGEABLE. Default location of the target folder (if you don't use first parameter).
  $target = dirname(__FILE__);

  // CHANGEABLE. Are results passed to the file? (if you don't use third parameter)
  $v_save_to_file = true;

  // CHANGEABLE. Default result file. (if you don't use third parameter)
  $v_save = $target.DIRECTORY_SEPARATOR."autoload_generated.php";

  // CHANGEABLE. Is search recursive? Default value. (if you don't use 2nd parameter)
  $v_recursive = true;

  // CHANGEABLE. Name of the generated array.
  $v_array_name = 'autoload_list';
Example of usage
E:projectsscriptsautoload_generator>php auto_generator.php Project true autoload_generated.php
parameters number: 4

target path: Project
recursive: true
output: file
result file: autoload_generated.php
TODO list
  1. Found bug in 132-133 lines of 1.1 version. Some useful source code was erased while deleting the code. Bug was fixed in 1.2 version on 07 May 2009. FIXED
Comments

Please notice that the current version of the script does not transform relative paths into absolute. If you use a relative path as the target point parameter, you will get an array where locations of the classes/interfaces are also relative. To avoid errors please use absolute path as the first parameter.

auto_ext_generator.php

This script extends the functionality of auto_generator.php. It adds new feature to scan in several folders. It’s not designed to have incoming parameters.

If you have new autoloading taks, you should copy auto_ext_generator.php and change values of variables as you want.

Requirements

See auto_generator.php

Variables

  // CHANGEABLE. The list of directories to be processed.
  $v_dirs = array (
    // array('location', 'is_recursive')
    array(
    	'path' => 'D:from_computerrom_projectsdialogue',
    	'recursive' => true
    ),
    array(
    	'path' => 'D:from_computerdangausapp',
    	'recursive' => true
    ),
    array(
    	'path' => 'D:from_computerrom_projectsdialogue',
    	'recursive' => true
    ),
  );

  // CHANGEABLE. The result file.
  $v_save = 'E:projectsscriptsautoload_generatorautoload_generated.php';

  // CHANGEABLE. The name of the generated array;
  $v_array_name = 'autoload_list';
Comments

Please notice that this script depends on auto_generator.php. It won’t work without it.

Description of examples

Archive consists of

  1. Three questions that are examined in the article:
    1. question1.php
    2. question2.php
    3. question3.php
  2. autoload_generated.php – file generated by autoload generator script and is used by autoload_example.php
  3. autoload_example.php – example of autoload realization
  4. Project directory – data for tests
      Project
      ..Classes
      ....Children
      ......A.php                    class A implements I1 {}
      ....B.php                      class B implements I2 {}
      ....Class_C.php                class C extends B implements I1, I3 {}
      ..Interfaces
      ....I1.php                     interface I1 {}
      ....blablabla.php              interface I2 extends I1 {}
      ....I3.php                     interface I3 {}




If you enjoy reading this article please leave your thoughts in the comment area. Moreover you may subscribe to this blog’s RSS feed. This article is probably not the last one. I suggest to glance over the advertisements at the bottom of the page.

  • Share/Save/Bookmark

Written by rdrapeko

March 28th, 2009 at 6:29 am

18 Responses to 'Autoloading in PHP'

Subscribe to comments with RSS or TrackBack to 'Autoloading in PHP'.

  1. NikspasE say: I think, that you are not right. I can defend the position. Write to me in PM.

    _____________
    ciallis
    buynow
    5

    [Reply]

    Jenlkimb

    4 May 10 at 4:16 pm

  2. Hi,

    this framework is already finish?
    I need this!!!

    [Reply]

    drapeko Reply:

    @me,

    Hey man,

    The third refactoring is almost finished. Give me a couple of days :)

    [Reply]

    me Reply:

    @drapeko,
    FINE… thanks

    [Reply]

    drapeko Reply:

    @me,

    Hi,

    basically I need some more time to finish the documentation, however the stable version is finished and available here: http://code.google.com/p/intelligencer

    Feedback is really appreciated

    [Reply]

    me

    2 Apr 10 at 12:51 pm

  3. why not store only class/interface name and its location?
    Such as
    $autoload_list = array (
    ‘A’ => ‘Project/Classes/Children/A.php’,
    ‘B’ => ‘Project/Classes/B.php’,
    ‘C’ => ‘Project/Classes/C.php’,
    ‘I2′ => ‘Project/Interfaces/blablabla.php’,
    ‘I1′ => ‘Project/Interfaces/I1.php’,
    ‘I3′ => ‘Project/Interfaces/I3.php’,
    );
    Is there any problems with include order?

    [Reply]

    vtk

    3 Mar 10 at 8:36 am

  4. Thanks for the script.
    Mind the extra keywords that might appear beside a class name, such as “extends” or “implements”. Running the script as it is now, they produce spurious entries in the array.
    Regards

    Gonzalo

    [Reply]

    drapeko Reply:

    @Gonzalo Díaz, What do you exactly mean? Are not they correct?

    [Reply]

    Gonzalo Díaz

    2 Nov 09 at 12:55 am

  5. Hey.

    You’ve got a misprint in explanation to the first example.

    @3 is printed. Made attempt to load class C

    should be

    3 is printed. Made attempt to load class A.

    What the fuck is going on with this textarea?! Why I can’t paste here any text???

    [Reply]

    rdrapeko Reply:

    Hello Vadim,

    Thanks for your response, you are absolutely right. I have corrected, thanks :)

    [Reply]

    Vadim Reply:

    @rdrapeko,

    Nevertheless it’s a great job.

    [Reply]

    rdrapeko Reply:

    @Vadim, thanks.. great job is coming soon :) i have been creating tiny and powerful autoloading framework based on this idea.. it separates core source code from class imports and automates the process of loading.. the first version is 75% completed.. if you are interested, please come back in several weeks :)

    [Reply]

    me Reply:

    @rdrapeko,
    this framework is finish?

    [Reply]

    Vadim

    21 Jun 09 at 11:00 pm

  6. Dude you’ve got a bug :)

    on line 132 & 133 of autoload_generator.php you’re replacing all string with ” but most of my PHP code gets replaced as well :)

    [Reply]

    rdrapeko Reply:

    Hi dnavre,

    I was offline for several days. Thanks for your response. :)

    This bug will be fixed in the nearest days.

    [Reply]

    rdrapeko Reply:

    Thanks once more for your response. This bug is fixed, new version is available in the store section http://wp.drapeko.com/store/php-autoloading-files/

    [Reply]

    dnavre

    1 May 09 at 11:57 pm

  7. Great job there! Really very useful tool. I’m putting it to use right now :D

    [Reply]

    rdrapeko Reply:

    Thanks dude. I will release more convenient tiny OO autoloading framework in the nearest future. The idea will be absolutely the same – it will help to collect information about classes and interfaces. Moreover, it will have several environments/modes and rich config system.

    [Reply]

    dnavre

    29 Apr 09 at 3:46 pm

Leave a Reply

Promotions (coming soon)