Using an Autoloader to Load Classes

In the previous articles, we looked at ways to prevent collisions betweMODX logoen functions and classes. In this article, we’ll see how to make that more convenient by using an autoloader that loads class files automatically without include or require statements.

 

 

 

Autoloaders Explained

In modern versions of PHP (since Version 5.1.2), whenever you instantiate a class with new and the code for that class has not been loaded, PHP checks to see if anyone has registered an autoload function. If there is a registered autoloader, PHP calls it with the class name as an argument. If there is more than one registered autoloader, PHP will call each of them in turn until the class is successfully loaded, or all the autoloaders fail.

With an autoloader, you only need a single include or require statement to include the autoloader’s PHP file. Once you’ve done that you can instantiate any class with new and the autoloader will find and include the class file. In addition to the convenience, using an autoloader insures that you are only loading the classes that are actually used. This is especially useful if you have many classes stored in separate files.

In this article, we’ll create a simple autoloader to load classes for a project. The example autoloaders in this article are not meant for actual use (though they’re fully functional). I’ve intentionally made them crude and simple to make it easier to understand how autoloaders work.

 

In the latest versions of PHP, namespaces are often used to help prevent collisions between functions, classes, and files with the same name (more on that in the next article). In addition, Composer is available to handle the installation and upgrade of packages. Composer provides its own autoloader which makes creating your own unnecessary. Those options are beyond the scope of this article, which just implements a simple autoloader to help you understand how they work.

 

Our Project

Our imaginary project is called “pets”. It only has two classes Cat and Dog. Our project is under MODX_CORE_PATH . 'components/pets/ and has the following structure:

[code language=”html”]
MODX_CORE_PATH/pets/
autoload.php
model/
pets/
cat.class.php
dog.class.php
[/code]

 

Our dog.class.php contains this code:

[code language=”php”]
<?php
class Dog {

function __construct() {

}

function speak() {
return ‘arf’;
}

}

[/code]

 

Our cat.class.php file looks like this:

[code language=”php”]
<?php
class Cat {

function __construct() {

}

function speak() {
return ‘meow’;
}

}
[/code]

 

Yes, I know that these should probably be children of (extend) a base class called something like “Animal,” but I’m trying to keep things simple. Bear with me.

 

The Autoloader

The autoload.php file (which goes in the MODX_CORE_PATH/components/pets/ directory) looks like this:

[code language=”php”]
<?php
function my_autoloader($class) {
include MODX_CORE_PATH . ‘/components/pets/model/pets/’ .
strtolower($class) . ‘.class.php’;
}

spl_autoload_register(‘my_autoloader’);
[/code]

 

Our crude autoloader simply includes the class file based on its name. Now we can freely instantiate our classes (and any others in the pets directory just by calling new.

 

Using Our Autoloader

This simple file sets up our autoloader and calls a class method:

[code language=”php”]
include_once MODX_CORE_PATH . ‘/components/pets/autoload.php’;

$dog = new Dog();
$cat = new Cat();

echo "\n Dogs say: " . $dog->speak();
echo "\n Cats say: " . $cat->speak();
[/code]

 

Notice that we didn’t have to include either of the Cat or Dog class files. The autoloader does that for us. We can also add new class files (e.g., horse.class.php, sheep.class.php) and the autoloader will automatically load them as well when we need to use them. An additional advantage is that only the classes actually used in a particular file will be loaded.

 

Limitations

This is a pretty bargain-basement autoloader. It has no error handling, and it assumes that all the class files are in the model/pets/ directory. If we want to add a controller class file, we either have to put it in the model/pets/ directory, or change our code.

One solution is to name our classes after their locations, using an underscore for the directory separator. In that case, we’d have Model_Pets_Dog class and a Model_Pets_Cat class. Our class file names would remain unchanged. In our autoloader, we’d use code like this:

[code language=”php”]
<?php
function my_autoloader($class) {

$path = str_replace(‘_’, DIRECTORY_SEPARATOR, $class);
include_once MODX_CORE_PATH . ‘/components/pets/’ .
strtolower($path) . ‘.class.php’;
}

spl_autoload_register(‘my_autoloader’);
[/code]

 

Now, to use our classes, we’d have to do it like this:

[code language=”php”]
<?php
include_once MODX_CORE_PATH . ‘/components/pets/autoload.php’;

$dog = new Model_Pets_Dog();
$cat = new Model_Pets_Cat();
$controller = new controllers_MyController();
[/code]

 

If you’re thinking that this is an ugly solution, I’d have to agree, though it is common in older autoloaders. It’s difficult to remember the path to each class, which is now part of the class name. In addition, you have to make sure that no class has an extra underscore in its name.

 

Another Way

A somewhat cleaner solution, is to include the possible paths to class files in the autoloader and search all of them. By doing this, we can go back to our original class name. Here’s an example with added error handling:

[code language=”php”]
<?php
function my_autoloader($class) {
$dirs = array(
‘model/pets/’,
‘controllers/’,
‘processors/’,
);

$file = ”;
foreach($dirs as $dir) {
$file = MODX_CORE_PATH . ‘/components/pets/’ . $dir .
strtolower($class) . ‘.class.php’;
if (file_exists($file)) {
include_once $file;
}
}
}

spl_autoload_register(‘my_autoloader’);
[/code]

 

Now, we can do this to use our classes:

[code language=”php”]
<?php
include_once MODX_CORE_PATH . ‘/components/pets/autoload.php’;

if (class_exists(‘Dog’)) {
$dog = new Dog();
echo "\nDogs say:" . $dog->speak();
} else {
/* Handle Error */
}

if (class_exists(‘MyController’)) {
$controller = new MyController();
} else {
/* Handle Error */
}
[/code]

 

You might be tempted to throw an exception at the end of your autoload function if the file hasn’t been found. This is a *very* bad practice, though, because yours might not be the only autoloader registered at the time. If someone else’s code is counting on their autoloader and yours runs first, your autoloader won’t find their file and will throw an exception, though the other autoloader would find the file with no trouble. It’s not really “your” exception, and depending on how you handle it, the other autoloader may never run, even though nothing is really wrong.

If your autoloader fails, the class will be an empty object, so using this code will handle errors more gracefully:

[code language=”php”]
$dog = new Dog();
if (! $dog) {
/* Handle error */
}
[/code]

 

Yet Another Way

For a really efficient autoloader, we can actually specify the path to each class file in an array in the autoloader, like this:

[code language=”php”]
<?php
function my_autoloader($class) {
$petDir = MODX_CORE_PATH . ‘/components/pets/model/pets/’;
$controllerDir = MODX_CORE_PATH . ‘/components/pets/controllers/;

$paths = array(
‘Cat’ = $petDir . ‘cat.class.php’,
‘Dog’ = $petDir . dog.class.php’,
‘MyController’ = $contollerDir . mycontroller.class.php’,
);
if (array_key_exists($class, $paths)) {
include $paths[$class];
}
}

spl_autoload_register(‘my_autoloader’);
[/code]

 

This method is faster at run-time, but not so convenient for us, since we have to add a new line to the autoloader for every class file we create. If you are using the Composer autoloader, though, it will create the array for you automatically and inject it into the autoloader file. It will even do this for your own class files in addition to the files Composer has installed.

 

Wrapping Up

Though we used an array of directories in our autoloader to solve the problem of class files located in different places, a much more elegant solution is to use namespaces. That’s beyond the scope of this article, but we’ll see how to use namespaces in the next article, and in the following article, we’ll see how to use namespaces with an autoloader.

 


For more information on how to use MODX to create a web site, see my web site Bob’s
Guides
, or
better yet, buy my book: MODX: The Official Guide.

Looking for quality MODX Web Hosting? Look no further than Arvixe Web Hosting!

Tags: , | Posted under MODX, MODX | RSS 2.0

Author Spotlight

Bob Ray

Bob Ray is the author of MODX: The Official Guide and over 30 MODX add-on components. He hosts Bob's Guides, a source of valuable information for MODX users, and has been very active in the MODX Forums with over 19,000 posts.

Leave a Reply

Your email address will not be published. Required fields are marked *