Autoloading in PHP
Have 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:
- Asks you: what is an autoloading? Do you really understand how __autoload() function works?
- Examines several autoloading approches
- 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:
- Two problems
- Check yourself
- First question
- Second question
- Third question
- Naming standards
- Zend approach
- ‘Include all’ approach
- Associations between files ant their locations
- Generate association array
- Generator
- Extended generator
- Conclusion
- Necessary Files
- Generators
- auto_generator.php
- auto_ext_generator.php
- Description of examples
- You don’t know where the class/interface is located
- 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):
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″ is printed. Interface I3 is loaded.
- “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″ and “2″ are printed
- “3″ is printed. Made attempt to load class A. Another function is called to load I1 interface
- “1″, “2″, “3″ are printed.
- “4″ is printed. I1 is loaded, the goal is achieved.
- “5″ is printed. I2 is loaded. Function finishes the job.
- Class C is completely loaded. “4″ and “5″ are printed, as all the necessary classes are loaded. Function finishes the job.
- 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″ 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″ is printed.
- “2″ is printed. I3 is loaded.
- “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″, “2″, “3″ are printed
- “4″ is printed. I1 is loaded.
- “5″ is printed. I2 is loaded. Function finishes the job.
- B is completely loaded. “4″ and “5″ are printed.
- 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″ 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″ is printed.
- “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
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 ↓
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 ↓)
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
TODO list
- 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.
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.
Archive consists of
- Three questions that are examined in the article:
- question1.php
- question2.php
- question3.php
- autoload_generated.php – file generated by autoload generator script and is used by autoload_example.php
- autoload_example.php – example of autoload realization
- Project directory – data for tests
–
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.
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
Hi,
this framework is already finish?
I need this!!!
[Reply]
drapeko Reply:
April 10th, 2010 at 9:04 pm
@me,
Hey man,
The third refactoring is almost finished. Give me a couple of days :)
[Reply]
me Reply:
April 10th, 2010 at 9:23 pm
@drapeko,
FINE… thanks
[Reply]
drapeko Reply:
April 21st, 2010 at 9:07 am
@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
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
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:
November 2nd, 2009 at 9:16 am
@Gonzalo Díaz, What do you exactly mean? Are not they correct?
[Reply]
Gonzalo Díaz
2 Nov 09 at 12:55 am
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:
June 22nd, 2009 at 9:01 am
Hello Vadim,
Thanks for your response, you are absolutely right. I have corrected, thanks :)
[Reply]
Vadim Reply:
June 22nd, 2009 at 12:47 pm
@rdrapeko,
Nevertheless it’s a great job.
[Reply]
rdrapeko Reply:
June 22nd, 2009 at 12:54 pm
@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:
April 2nd, 2010 at 12:52 pm
@rdrapeko,
this framework is finish?
[Reply]
Vadim
21 Jun 09 at 11:00 pm
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:
May 7th, 2009 at 9:13 am
Hi dnavre,
I was offline for several days. Thanks for your response. :)
This bug will be fixed in the nearest days.
[Reply]
rdrapeko Reply:
May 7th, 2009 at 12:18 pm
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
Great job there! Really very useful tool. I’m putting it to use right now :D
[Reply]
rdrapeko Reply:
May 7th, 2009 at 9:56 am
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