As Robbie stated, both of the other answers are correct, and an autoloader is the ideal solution.
An autoloader may seem (at first) to be slightly more complicated, but it presents benefits that are genuinely significant and make it well worth using.
Here are a few of them:
You do not need to manually include or require files.
You do not need to worry about files being loaded in the correct sequence - the interpreter will load any dependencies automatically. (In your case, the differences in the Windows and Linux operating system exposed this weakness in the existing code)
You can avoid loading files that are not needed.
The interpreter does not need to parse unnecessary classes and code.
Here are some things you should know about autoloaders:
You can have as many autoloaders as you need and want - they are stored in a stack and executed in sequence. If the first one does not load the class, the next one is used, and so on, until the class is loaded or there are no more autoloaders to try.
An autoloader is a callable - either a method of a class, or a function.
Exactly how you implement the autoloader is up to you - so if your project has a specific directory structure that relates to the class type or hierarchy, you can instruct it to look in specific directories, making it more efficient.
Most of us like to keep our classes in separate files. This makes it easier to find the classes we are interested in, and keeps the files smaller, which makes them easier to understand.
PHP does not enforce any kind of naming convention when it comes to the names of the files we use, but most developers prefer to save Classes in files with file names that relate to the class name.
The autoloader feature assumes that there is a way to load the correct file when presented with the file name. So a good practice is to have a simple way of generating the file name from the class name - the simplest is to use the class name as the file name.
Here is my preferred autoloader - which I have adapted from code by Jess Telford that I found online when I was learning PHPUnit - (http://jes.st/2011/phpunit-bootstrap-and-autoloading-classes/)
class ClassDirectoryAutoLoader {
static private $classNamesDirectory = array();
public static function crawlDirectory($directory) {
$dir = new DirectoryIterator($directory);
foreach ($dir as $file) {
self::addClassesAndCrawlDirectories($file);
}
}
private static function addClassesAndCrawlDirectories($file){
if (self::isRealDirectory($file)) {
self::crawlDirectory($file->getPathname());
} elseif (self::isAPhpFile($file)) {
self::saveClassFilename($file);
}
}
private static function isRealDirectory($file){
// ignore links, self and parent
return $file->isDir() && !$file->isLink() && !$file->isDot();
}
private static function isAPhpFile($file){
//ends in .php
return substr($file->getFilename(), -4) === '.php';
}
private static function saveClassFilename($file){
//assumes that the filename is the same as the classname
$className = substr($file->getFilename(), 0, -4);
self::registerClass($className, $file->getPathname());
}
public static function registerClass($className, $fileName) {
self::$classNamesDirectory[$className] = $fileName;
}
public static function loadClass($className) {
if (isset(self::$classNamesDirectory[$className])) {
require_once(self::$classNamesDirectory[$className]);
}
}
}
$classDir = dirname(__FILE__) . '/../classes'; // replace with the root directory for your class files
ClassDirectoryAutoLoader::crawlDirectory($classDir);
spl_autoload_register(array('ClassDirectoryAutoLoader', 'loadClass'));
What this code does is
Recurse through the directories (from the classDir), looking for .php files.
Builds an associative array that maps the classname to the full filename.
Registers an autoloader (the loadClass method).
When the interpreter tries to instantiate a class that is not defined, it will run this autoloader, which will:
Check if the file is stored in the associative array.
Require the file if it is found.
I like this autoloader because:
It's simple.
It's general - you can use it in virtually any project that follows a few simple conventions (see below).
It only crawls the directory tree once, not every time a new class is instantiated.
It only requires the files that are needed.
The loadClass method is super-efficient, simple performing a lookup and a require.
This code makes some assumptions:
All of the classes are stored in a specific directory.
All of the files in that directory contain classes.
The file name exactly matches the class name.
There are no side effects from requiring a file (i.e. the file contains only a class definition, no procedural code).
Breaking these assumptions will break this autoloader.
These are the conventions you need to follow to make use of this autoloader:
Keep all classes under a single directory.
Only class definition files under the class directory.
Make a seperate file for each public class.
Name each file after the class it contains.
No procedural code with side effects in these class definition files.