Autoloading Classes

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. The
spl_autoload_register() function registers any number of
autoloaders, enabling for classes and interfaces to be automatically loaded
if they are currently not defined. By registering autoloaders, PHP is given
a last chance to load the class or interface before it fails with an error.

Tip

Although the __autoload() function can also be used for
autoloading classes and interfaces, it's preferred to use the
spl_autoload_register() function. This is because it is
a more flexible alternative (enabling for any number of autoloaders to be
specified in the application, such as in third party libraries). For this
reason, using __autoload() is discouraged and it may be
deprecated in the future.

Note:

Prior to PHP 5.3, exceptions thrown in the __autoload()
function could not be caught in the
catch block and would result in
a fatal error. From PHP 5.3 and upwards, this is possible provided that if
a custom exception is thrown, then the custom exception class is available.
The __autoload() function may be used recursively to
autoload the custom exception class.

If the class name is used e.g. in call_user_func() then
it can contain some dangerous characters such as ../.
It is recommended to not use the user-input in such functions or at least
verify the input in __autoload().

Example #1 Autoload example

This example attempts to load the classes MyClass1
and MyClass2 from the files MyClass1.php
and MyClass2.php respectively.

Then you will just have to use the folder structure and name the classes accordingly. If you want to have a class named Page, which will be in the pseudo namespace System.Web.UI, create a directory named System in /classes, then create Web, then UI, then name the class System_Web_UI_Page. Kind of long to type if you don't have autocomplete, but at least you will not have to manage the loading of all the classes' definitions.

This doesn't trigger any warnings, but it has the undesirable (if not downright buggy) effect of calling my __autoload() function with the argument 'parent'. I had to modify __autoload() to handle this special situation:

Before you start using __autload, remember that it holds no scope/namespace. This means that if you are depending on third party applications and they have an autoload function defined and so do you, your application will error.

To remedy this, everyone should look at the spl_autoload functions, eg: spl_autoload_register. This function allows more than one custom functions to be called through the default spl_autoload (default __autoload) handler.

My objective was to allow __autoload() to be easily extended in complex systems/frameworks where specific libraries etc may need loading differently but you don't want to hard-code little adjustments into your working __autoload() to allow this to happen.

Using a ServiceLocator object with some static methods and properties to allow loosely coupled locators to be attached to it you can swap/change and add to the functionality of your __autoload() at runtime.

Note to Ricos posting:A lot of useless Coding. However, I improved the code, so now it will be able to find any folders ("." and ".." will not being tested... oO) and search as deep as possible. Now it will find CLASS_DIR/foo/bar.class.php also like CLASS_DIR/foo/bar/baz/buz/fii/and/so/on/class.php

Warning: This code will check ALL dirs who're "deeper" / "lower" than the class dir, so prevent deeply hidden files (or use just a few folders).

Improved Version:<?php

// change this, if this code isn't "higher" than ALL classfilesdefine("CLASS_DIR", dirname(__FILE__));

I found out a neat way to centralize one single class which will give accessibility to other classes. I also added a parameter to the __construct method which would be an array of classes you want loaded. This isn't completely necessary, but it will stop "excessive memory" if you're loading a bunch of unused classes.

On the if/else, you are better off either entering a break after success and continue on else (if you feel as though you have to return something), or otherwise not putting in an else. the else with return will cause a premature end to the function.

Also, if you use an array of directories, it may be a good idea to enter a blank ('') as the first value so that the script will check locally.

Because static classes have no constructor I use this to initialize such classes.
The function init will (if available) be called when you first use the class.
The class must not be included before, otherwise the init-function wont be called as autoloading is not used.

While using an "autoloading" method you should pay attention to variables scope. Because of new file will be included INSIDE of magic function __autoload - all of declared in such file global scope variables will be only available within this function and nowhere else. This will cause strange behaviour in some cases. For example:

USAGE EXAMPLE:
must be caled using the X function giving the real function as first parameter, like:
$result = ex("add",1,2);
// returns 3 if add function defined in add.php sums the first and second parameter..

do not use is_subclass_of() in your __autoload() function to identify a class type and thereby its path (f.e exceptions). is_subclass_of() needs to know the class, but you want to check BEFORE you include the class.

As your web application develops, new paths containing class files can be added into the '$classPath' variable within '__autoload()'. If hard-coding the '$classPath' variable isn't to your taste, you could arrange for its value to come from 'outside' in whatever way you like.

Autoloaders & Namespaces: The effective namespace of an autoloaded file must match that of its original reference; just finding and loading the file isn't enough. (I know, seems obvious now, but...)

My namespaces may serve as hints to locations within the file system, and I tried to be cute: If a class (file) wasn't found where it was expected, my autoloader also looked in an alternate location. This worked fine, except that the alternate's own qualified namespace was thus slightly different (reflecting where *it* lived). So although the desired class was ultimately loaded (name and all), the original caller's reference remained unsatisfied because of the namespace discrepancy (as it should, really), but it was subtle.

Of course, this scheme works fine within the same namespace (explicit or not).

And kudos to the autoload devs for anticipating what could have been an endless autoload loop.

Because the scripting engine just find the class declaration in the body of the __autoload() function, you also can declare the missing class in the __autoload() function. (No need to include or require a file.)

I have developed a small script, that can scan recursively folders and files ang generate array of associations between classes/interfaces and their locations. It accepts several incoming parameters and it's very simple to use.

About static classes that need initialisation before use (problem discussed by adam at greatbigmassive dot net and kalkamar at web dot de below).

Simple problems have often simple solutions. Here is my approach:

First, my __autoload function is very simple:<?phpfunction __autoload ($class_name){ if (preg_match('|^\w+$|', $class_name)) include "./packages/$class_name.php";}?>(The "if/preg_match" line is just a simple yet robust security check. Moreover I use "include" and not "require"/"require_once", so that if the file is not found, the __autoload function does nothing, and my script dies eventually with a meaningful "Class 'foo' not found" fatal error.)

Now, when I define a class "foo" which requires initialisation before use, I just write the initialisation code after the definition of the class in the file "packages/foo.php":

<?php/** Content of file "packages/foo.php" **/class foo{/* definition of the class is found here */}

/* initialisation code of the class is found here. */

/** End of file "packages/foo.php" **/?>

That's it. No need for an <? init() ?> or a <? __construct() ?> method.

If you placed your autoload function in an external file which you're requiring at the head of every script, be cautious of some odd behavior regarding PHP's idea of the current working directory.

I ran into some unexpected path issues when my include file was placed in a subdirectory directory. The solution to my problems was to make sure that the autoload script being included is in the same directory as the calling script.

An issue I've had with using the __autoload function is getting it into the application.

You have to have the function included in every topmost script. This is a pain if the entire application is OOP and an "app" can be just a component of another "app".

A solution I've found is to use php.ini's auto_prepend_file setting.

Mine is set to ...

auto_prepend_file = auto_loader.php

The auto_loader.php script contains a single function. The __autoload() function.

The include_dir path IS examined to find this file, so you can just put it with the rest of your includable files.

A useful additional facility here is that you could log which classes are used by a script at runtime. Very useful if you have object factories and can't know the load at design time.

Also, assigning the uncaught exception handler and the error handlers in this file means your entire site WILL have some global protection without you having to deal with it on a script by script basis.

If you do not have access to the PHP.INI file, or you are running on a shared server, you may not be able to set this property. In those cases, you may be able to set the value using .htaccess. (NOTE: UNTESTED as I don't use Apache).

Generally, I would advise for each class to have it's own file, and hold nothing besides that class. Just define __autoload() in a/the infrastructure file -- a/the file that does the behavioral logic, and there should be no need to redefine it in a class' file.

The autoload-feature allows to add the behavior of static constructors (like in C#). Static constructors should be called on the first occurence of a class reference - typically a 'new' operator or a static call to a class's operation.

They can be used used to initialize complex static properties.

And here is an easy and save way how it can be done:

Content of MyClass.class.php5:<?php

// demo class persisting of a static and a dynamic constructorclass MyClass{

As an addendum to #91119 I would suggest adding class_exists() into that solution. I've just implemented autoloading based on the code provided there and ran into a problem where a file had the same name as a class, existed in the directory structure prior to the file that had the actual class and as a result was being included first and resulting in a 'class not found' error.

if you create the exception in the __autoload() and serialize it, then when you try to access the same missing class later (in another place in the code), then the exception will contain invalid stack trace.

Also, here's an excellent blog post that discusses the consequences of using eval() as well as provides an example to handle static method calls and namespaces: http://www.onphp5.com/article/61

Be careful with using that eval() trick within __autoload().If you use reflection in your code, the so called trick, *can* provide ill side effects.For example ->$reflection = new reflectionClass('some_class');if (FALSE === $reflection->isSubClassOf('another_class')){ throw new Exception('Class "some_class" must extend base class "another_class"');}

If the real class "another_class" doesnt exist at the time, or "some_class" doesn't extend "another_class", with the reflection test, the so called eval() trick, creates a dummy "another_class", thereby making the reflection test useless...