Making Life Better With The SPL Autoloader

« »

How many of us have seen this example in code we’ve worked on?

<?php

require '/path/to/lib/DatabaseI.php';
require '/path/to/lib/Database.php';
require '/path/to/lib/Authenticate.php';
require '/path/to/lib/User.php';
require '/path/to/lib/BlogEntry.php';
require '/path/to/lib/Comments.php';
require '/path/to/lib/TemplateLoader.php';

?>

<span id="more-492"></span>

…and well you get the point…

If you’ve been in the business for any length of time you’d recognize this because almost every single PHP developer does it at one point or another.

Until they learn about SPL’s autoload functions, that is.

There’s a nifty feature built into PHP called spl_autoload_register(). This nifty function allows us to define a callback function that we can then use to automatically load things.

For example, we can do this:

<?php

set_include_path(realpath('/path/to/lib') . PATH_SEPARATOR . get_include_path());

function autoload($className)
{
		require($className . '.php');
}

spl_autoload_register('autoload');

$Database = new Database();

?>

That’s it! That’s all there is to defining your own autoloader. This autoloader will then try to automatically get the files needed to instantiate the classes you’re calling.

This has the benefit of not loading excess code that you may never need, which will improve your performance and the time it takes to execute your scripts. In fact, some people have found major performance improvements by using the autoloader.

Now someone is probably thinking “wait, my classes are organized into libraries and placed into their directories. I don’t have to define all of those directories, do I?”

The short answer is no. The long answer is that the autoloader allows us to define any rules we like, so we can do any number of things. Some of my favorite solutions:

* Build a list when the application runs the first time, and allow the autoloader to rebuild its list if something isn’t found, and then fail gracefully.
* Build a list on the fly, and store that in memory (memcached, APC, etc.)
* Have a hard-coded list, and throw an exception if a class outside that list is called.
* Apply the PECL naming conventions, and use a regular expression or explode() to derive the file path.

Because the spl_autoload_register() function lets you write any logic you like, you can make your rules as stringent or as loose as you like.

So spl_autoload_register() in, long sets of include and require statements out!

Brandon Savage is the author of Mastering Object Oriented PHP and Practical Design Patterns in PHP

Posted on 7/24/2009 at 3:00 am
Categories: Best Practices, System Architecture, PHP 5
Tags: , ,

mihailt (@mihailt) wrote at 7/24/2009 4:08 am:

the feature is nice yes, but from my experience if you use this solution in highload environment that it can dramatically reduce performance, but in most other areas that can make a great deal.

Botnary (@Botnary) wrote at 7/24/2009 4:31 am:

thanks :) great tool and good idea to load classes..

Jan! wrote at 7/24/2009 5:11 am:

In PHP 5.3, use a directory structure that matches the namespace scheme. Then, all you need to do, is include the class name after replacing ‘\’ with \DIRECTORY_SEPARATOR (and appending ‘.php’, or whatnot).

SchizoDuckie wrote at 7/24/2009 6:22 am:

What’s the difference between this and __autoload() ? I noticed that you can register more than one __autoload or so ? (whatever the use of that is)

Brandon Savage (@brandonsavage) wrote at 7/24/2009 8:55 am:

@mihailt – I’ve typically found sometimes it’s better to do it “right” than “efficiently” since boxes are inexpensive. However, you are right, that sometimes it can be slow.

@SchizoDuckie spl_autoload_register lets you register a callback which can then be used multiple times without having to individually register the classes. This means that when you add new classes, new registration isn’t required.

SchizoDuckie wrote at 7/24/2009 8:57 am:

@brandon

I see now, really handy to register classes that have multiple dependencies, nice :-)

Herman Radtke (@hermanradtke) wrote at 7/24/2009 10:39 am:

The real beauty of spl_autoload_register() is that it is a stack. That means I can register multiple __autoload functions and let PHP do the hard work.

Example:
spl_autoload_register(‘library_autoloader’);
spl_autoload_register(‘app_autoloader’);
spl_autoload_register(‘3rdparty_autoloader’);

$calc = new App_Calc; // the library_autoloader() is tried first and fails. the app_autoloader() is tried second and succeeds.

Brandon Savage (@brandonsavage) wrote at 7/24/2009 10:43 am:

That’s a very good point, Herman. I didn’t want to go into that sort of depth (it’s a brief introduction) but you are 100% right. And that’s the real beauty – PHP will do the work. It makes life easy when you integrate multiple frameworks together, like I do, to be able to let each of them define their own autoload function.

passerby (@birdpoop) wrote at 7/24/2009 3:47 pm:

If you don’t want to go into that depth why didn’t you just talk about __autoload() instead?

passerby (@birdpoop) wrote at 7/24/2009 3:48 pm:

Thanks for the explanation Herman.

Brandon Savage (@brandonsavage) wrote at 7/24/2009 4:16 pm:

@passerby Because it’s my blog. And simply put, I don’t feel a need to regurgitate the manual. I provided an example and that was sufficient.

iflyhigh (@iflyhigh) wrote at 7/31/2009 6:04 am:

Autoloading is dumb. Here’s why:

– It makes your code WAY less clear. Explicit imports document the classes your code relies on.
– Large code bases become real confusing real fast.
– It’s slow. stat() Not there? stat() Not in this other place? stat() Not over here? stat() Oh, here it is?
– It’s lazy, not “right”.

David Stockton (@dstockto) wrote at 8/12/2009 2:29 am:

@iflyhigh:

– I agree that explicit imports show what files your code relies on, that doesn’t necessarily translate to classes. With a good autoloading strategy (ie, one class per file, and having the class name correspond to the location and name of the class) it’s very easy to figure out what classes you’re using.
– Large codebases get confusing real fast if there is not a defined mapping of how to name files and classes. If you’ve got two different “Account” classes and don’t have them either namespaced or class prefixed, you’re probably going to have problems anyway.
– The stat call is something you’ve got to do explicitly in your autoloader if that’s the behavior you want. It’s not required. Zend Framework’s autoloader up until 1.8 didn’t use a stat call. It just did a require on the code which meant 2 things… First it was faster since it didn’t have a stat call, but second, if you were stacking autoloaders, the Zend loader would need to be done last since require will bomb if the file doesn’t exist. The current version lets you configure this behavior.

Say perhaps you’re writing code using a strategy pattern. The code to provide that strategy is determined at run-time. If you go with the autoload way of doing things, only the classes and interfaces you need will be loaded. If you go with require[_once], you’ll be including all the classes all the time, even if they aren’t needed. That adds parsing and memory overhead. If you go with include[_once] and only load the classes right before they are needed, you’ve no longer got the benefit of having all the classes you’re using in one place (typically the top of the file), and if it’s critical those files be loaded, include[_once] isn’t going to bail if there was a problem loading.

– Lastly, who says lazy cannot be right? I think the benefits of using autoloader(s) greatly outweigh the downsides. The benefits are there both for the developer (not having to jump up to the top to include a new class for another file), the performance (only including files that are requested, even if the code may have references to other classes on non-executed lines). Additionally, as Brandon mentioned, new classes don’t need to be registered somewhere. If you have any code that dynamically generates classes that may or may not actually exist, this can be a huge benefit.

« »

Copyright © 2023 by Brandon Savage. All rights reserved.