Update

It’s 2013. Composer solved outloading. Even for legacy projects classmaps solve all problems.

Go use that.




No seriously. Stop reading. You don’t need anything except composer classmaps.




Old blog post:

Using an autoloader seems to be the cool thing to do at the moment.

Everyone is doing it. There is even a proposal for the one true autoloader.

Motivation

So why use an autoloader?

The first argument usually is “easy of use”. It can get pretty annoying when you have to clutter your whole application with “require this file here and that file over there” statements. Especially once you loose track of what is actually already loaded. You can hand the Task to PHP when using require_once but that will result in some not so fast realpath() calls and some, mostly negligible, overhead in php.

The main motivation for autoloading usually is “getting rid of all the require statements and the problems resulting from forgetting one at one point where you don’t notice it because on your machine it worked.

Legacy- or non-framework applications

If you are developing using a framework then chances are you will be using the autoloader provided by that framework. Maybe, for whatever reasons, you choose not to do so but for the most part this will because you are dealing with an older application or building something from components. In my case there was a old project containing over 1500 Classes and having no classname to filesystem mapping.

A decision that makes it easier to move stuff around but makes autoloading a little tricky. While everything worked fine (in that regard at least) for many years the idea was to make life a little easier for developers while improving performance at the same time. Not only did the bootstrap pull in over 270 classes, those files also contained lots and lots of defines so that, using apc, an average bootstrap took over 100ms. Cutting that down without much hassle seemed like a nice idea.

As it turned out it was. After implementing an autoload solution and removing about 20% of the requires in the codebase (going through everything right from the start would have taken to long) the bootstrap went down to only pulling in 36 classes and a normal page didn’t use many more than those. For many short running cli scripts the benefit was even greater.

Solution

Well.. you could use an autoloader that scans the file system on request and loads a file called MyClassname.php when you want the MyClassname class but that just doesn’t seem like a good idea to do that every time a class is needed.

Since there is no way of magically knowing where a class is the idea was to build a mapping "classname" => "file" and thanks to the PHP Autoload Builder there is a tool that generates that mapping for you and stores it in a file.

Just to show how this is done let’s create a very simple project.

mkdir src/ src/stuff src/otherStuff src/config

and create some classes

echo '<?php class StuffClass {} ' > src/stuff/StuffClass.php
echo '<?php class OtherStuffClass {} ' > src/otherStuff/OtherStuffClass.php

Now install phpab:

sudo pear channel-discover pear.netpirates.net
sudo pear channel-discover components.ez.no
sudo pear install theseer/Autoload

and let’s generate the autoload file and put it in src/config (Note: many projects i’ve seen put that file directly into the src folder, i just want to show that it doesn’t matter where you put it)

phpab -b src/config/ -o src/config/autoload.php src/

and it has created a nice file for us that we put in the scm and just need to require in our bootstrap code

<?php // this is an autogenerated file - do not edit (created Tue, 08 Mar 2011 22:33:16 +0100)
spl_autoload_register(
   function($class) {
      static $classes = null;
      if ($classes === null) {
         $classes = array(
            'otherstuffclass' => '/../otherStuff/OtherStuffClass.php',
            'stuffclass' => '/../stuff/StuffClass.php'
          );
      }
      $cn = strtolower($class);
      if (isset($classes[$cn])) {
         require __DIR__ . $classes[$cn];
      }
   }
);

What we did while migrating away from the require statements is to use a different template file for phpab (-t option) and use a “require_once” statement just to make sure we never run into a “already defined class” problem. When you are using autoload from scratch you don’t need this but for that project it was possible that:

  • Class A gets loaded from the autoload
  • Class B get loaded from the autoload
  • Class B has a require_once statement pulling in ClassA.php

and we wanted to avoid running into those troubles. PHPAB will work for PHP 5.2 and PHP 5.3 (while the generation only works with 5.3 the generated code and be 5.2 compatible (-c option) and of course it also works with interfaces and namespaces.

Working with PHPAB

To add newly created classes to the mapping there are 3 options i’ve played around with

Add them by hand

This is quickest way to add a newly created class as it usually involves copying one line and changing the path and classname.

Rerunning the tool

Works well. I’d advice you to wrap the call in a createAutoload.sh or put it in your buildscript so you don’t have to remember the parameters. This will also clean up all deleted classes.

and if that is too much hassle for you

You could even put a little code in your autoload template that just automatically reruns the autoloader when a class isn’t found. So after adding a new class and using it somewhere in your code you get a “class not found” error, hit F5 again (or rerun the cli command) and it just works.


Published

08 March 2011

Tags


blog comments powered by Disqus