In earlier articles, we looked at namespaces as a way to prevent collisions between the names of classes and their methods and at autoloading. In this article, we'll see how to use the combination of namespaces and an autoloader.
Our Project
In this article, we'll continue to use our VeterinaryOffice project from the previous articles. (If necessary, see my previous articles on namespaces for a more complete explanation of how they work.) Here's the directory structure of the VeterinaryOffice project:
/* The base path is the full path to the VeterinaryOffice
directory; it ends in a slash */
PROJECT_BASE_PATH
src/
Controllers
Controller.php
Model
Pets
Dog.php
Cat.php
The Files
If you still have the files from the previous article, just uncomment the namespace
line at the top. For reference, here are the class files (I've left out the inside and outside constants and functions because they're not relevant to the autoload process, though leaving them in won't hurt anything):
Dog class file:
<?php
namespace VeterinaryOffice\Model\Pets;
class Dog {
function __construct() {
/* constructor code here */
}
function speak() {
return 'arf';
}
}
Cat class file:
<?php
namespace VeterinaryOffice\Model\Pets;
class Cat {
function __construct() {
}
function speak() {
return 'meow';
}
}
Controller class file:
<?php
namespace VeterinaryOffice\Controllers;
class Controller {
function __construct() {
/* constructor code here */
}
public static function sayHello() {
echo "\nController says Hello!";
}
/* Other controller methods here */
}
Autoloader with Namespaces
When a registered autoloader is called in a file with a namespace specified at the top of the file, PHP sends the autoloader the namespace specification as a prefix to the class name. With an appropriate use
statement, the fully qualified class name is sent to the autoloader. All our autoloader needs to do is translate that specification to a file path and include the file. Let's look at our new autoloader code:
<?php
/* The fully qualified $class specification will look something like this:
VeterinaryOffice\Model\Pets\Dog */
function my_autoloader($class) {
/* We'll need to remove this */
$prefix = 'VeterinaryOffice\\';
/* get path to VeterinaryOffice directory and put it in $baseDir */
$vendorDir = dirname(__FILE__);
$baseDir = dirname($vendorDir) .'/src/';
// echo "\nBase Dir: " . $base_dir;
/* Remove 'VeterinaryOffice/' prefix; */
$len = strlen($prefix);
/* $endOfClass will be the part of the
classname after VeterinaryOffice/ */
$endOfClass = substr($class, $len);
/* Get the actual path to the file */
$file = $baseDir . str_replace('\\', '/', $endOfClass) . '.php';
/* if the file exists, require it */
if (file_exists($file)) {
require $file;
return true;
}
return false;
}
spl_autoload_register('my_autoloader');
When PHP calls the autoloader, it sends the fully qualified class name to the autoloader as the only argument ($class
in this case). All we have to do to include the file is to remove the VeterinaryOffice\
prefix, convert the backslashes to the current server's directory separator (provided by PHP in the global constant, DIRECTORY_SEPARATOR
), add .php
to the end of the path, and require it. (Using require
is a tiny bit faster than include
, and since we used class_exists()
in the autoloader, we know it exists). The src/
directory is added in the $baseDir
variable.
Why do we have to remove the VeterinaryOffice prefix?
We have to do that because we've set the $baseDir
to the path to the src/
directory. If we tried to use the fully qualified class name to load the file, we'd have to insert the src/
directory between VeterinaryOffice/
and the rest of the class name. Doing it this way is simpler.
Using Our Autoloader
The changes to our previous code are actually quite small (we'll use our namespace aliases from the previous article). The code to use the classes is essentially the same. We've removed the class_exists()
calls because the autoloader is calling it. If the class isn't found, PHP will tell us with an error message.
We've removed the PROJECT_BASE_PATH
constant from the autoloader code because we no longer need it. In the autoloader, the path to the src/
directory is calculated relative to the directory of the autoloader with dirname()
. A bonus here, is that we can now transfer this autoloader to other projects without changing the code as long as we put it in the vendor/
directory. The constant is only used in the code below to load the autoloader.
<?php
/* namespace here is optional; the code works with or without it */
namespace VeterinaryOffice
const PROJECT_BASE_PATH = 'full/path/to/project/VeterinaryOffice/';
require PROJECT_BASE_PATH . 'vendor/autoload.php';
use VeterinaryOffice\Model\Pets\Dog;
use VeterinaryOffice\Model\Pets\Cat;
use VeterinaryOffice\Controllers\Controller;
$dog = new Dog();
echo "\nDogs say: " . $dog->speak();
$cat = new Cat();
echo "\nCats say: " . $cat->speak();
$controller = new Controller();
$controller::sayHello();
That's all there is to it. We've specified the namespace in our class files and the file that uses the classes. Once that's done, our autoloader will find the files automatically.
Output
Here's the output of the code above:
Dogs say: arf
Cats say: meow
Controller says Hello!
Modern Autoloaders
Our autoloader is still a little primitive, though it's simple enough that it's easy to understand.
Modern autoloaders are often classes themselves with methods that let you set the base path and have multiple, unrelated locations for the class files. Often, they use an anonymous PHP function rather than calling spl_autoload_register()
with a regular function as an argument. Some are quite complex. There are a couple of relatively simple ones here.
One common autoloader comes with Composer and you can integrate it into a project, though it's not trivial to do so unless the project itself is installed with Composer. We'll see how to use the Composer autoloader in my next article.
Bob Ray is the author of the MODX: The Official Guide and dozens of MODX Extras including QuickEmail, NewsPublisher, SiteCheck, GoRevo, Personalize, EZfaq, MyComponent and many more. His website is Bob’s Guides. It not only includes a plethora of MODX tutorials but there are some really great bread recipes there, as well.