Despite its unmerited bad reputation, PHP remains the de facto server-side scripting language of the web. This is largely due to its relatively gentle learning curve and low barrier to entry. However, what you gain from the ease of use, you lose in the lack of versatility and language abstraction. Take the simple action of loading files; PHP requires its users to load any file on the current page if they want to use functions within that file. This is fine as long as you’re only dealing with a few files, but things start to get a bit messy once you start working with anything resembling the scale of a real-life project.
So, let’s go through some of the different ways of loading files into your project. We’ll use a sample application below to illustrate:
MyApp/
├── Core/
│ ├── App/
│ │ ├── Mail.php
│ │ ├── Payment.php
│ │ └── Shop.php
│ ├── Views/
│ │ ├── Archive.php
│ │ ├── Page.php
│ │ └── Single.php
├── Pages/
│ ├── Cart.php
│ ├── Checkout.php
│ ├── List.php
│ └── Product.php
└── index.php
Loading files individually
The basic premise is this: If you want to use code from an external file inside your current script, you have to manually include it. If the file is required to run your script, then you can require it. Both the include and require commands are used to load files indiscriminately and will throw a warning or error message, respectively, if the file is not found at the given path. To prevent code replication or function duplication errors, you must use the include_once or require_once command instead.
How to
You can manually call each file, keeping in mind that if the source and requested files are not in the same directory, we’d need to traverse back to the root path of the application first.
// File: index.php
require_once 'Core/App/Shop.php';
require 'Core/App/Shop.php'; // Load ignored. File already loaded
include 'Core/Views/Page.php';
// File: cart.php
require_once __DIR__ . 'Core/App/Shop.php';
include __DIR__ . 'Core/Views/Page.php';
Why use it?
- Easy to understand and implement with no additional coding overhead. Ideal for small sites with limited files.
Limitations
- A lot of time can be spent individually loading files.
- It can be hard to track which files are loaded where. If the location of a file changes, all calls to that file in the codebase must be updated.
Loading multiple files from a single directory
A major headache of loading files individually is that you may have hundreds of files that need to be loaded into your host script. In this case, you want to write a function that loads all the files in a directory with a single call.
How to
function autoload($dir){
$files = array();
$file_dir = opendir($dir);
// loop through folder add files to array
while (false !== ($file = readdir($file_dir))):
if (stristr($file, '.php')):
$files[] = $file;
endif;
endwhile;
sort($files); // arrange in a-z
foreach ($files as $file):
$filename = pathinfo($dir . '/' . $file, PATHINFO_BASENAME);
require_once ("$dir/$filename"); // require file
endforeach;
}
// in index.php
autoload('Core/App');
autoload('Core/Views');
Why use it?
- You can load multiple files within a single directory with a single function call.
Limitations
- You cannot load files in nested directories, so you’d have to make additional calls for the inner directories.
Loading multiple files within a nested directory
To load multiple files within a nested directory, we need to utilise PHP’s built-in Iterator functionality. Namely RecursiveDirectoryIterator, RecursiveIteratorIterator, RegexIterator and RecursiveRegexIterator. These functions combine to enable us to traverse nested directory nodes and search for PHP files to load.
How to
function autoload($dir) {
$directory = new RecursiveDirectoryIterator($dir);
$iterator = new RecursiveIteratorIterator($directory);
$files = new RegexIterator($iterator, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH); // only PHP files
foreach ($files as $file_object) {
$file = pathinfo($file_object[0]);
$filename = $file['basename'];
$filepath = $file['dirname'] . DIRECTORY_SEPARATOR . $filename;
require_once ($filepath); // require file
}
}
// in index.php
autoload('Core');
Why use it?
- Load multiple files in a nested directory with a single function call. This is useful in applications where a root access point is available. E.g. ‘functions.php’ file in WordPress.
Limitations
- You have to load all root folders explicitly. Still, depending on how you structure your project, you might need to declare only one root folder.
Loading files using PHP spl_autoload_register
Although the previous two procedures are technically “autoloaders”, they still involve having to include a custom autoloading function in your code. This can be avoided through the use of PHP’s built-in spl_autoload_resgister function. The spl_autoload_register essentially registers an anonymous function to be used as an autoloader. However, we do not have to implement that function (like we’ve done with the recursive function above); instead, we define a path to the directory we want to call inside the spl_autoload_register
How to
// File: autoload.php
spl_autoload_register(function ($className) {
$filePath = 'core/app' . $className . '.php'; // Note: we are loading our classes from the core/app directory
if (file_exists($filePath)) { require_once $filePath; }
});
Why use it?
- Native PHP autoload functionality prevents the need for a custom autoloading code. This article shows you how to implement spl_autoload_register within your code elegantly.
Limitations
- All class names must match the class file names to locate the correct files. This is good practice anyway, but be aware of pitfalls resulting from typos and capitalisations.
- Since spl_autoload_register works with classes, it may not be compatible with a functional/procedural programming paradigm.
Loading files using PS4 autoloader and composer
Depending on the size and scope of your project, it may be beneficial to use a dependency manager such as Composer. One of the benefits is the built-in autoloading of your code and other installed packages. In simple terms, Composer saves you from having to write thousands of include and require commands or your own spl_autoload_register autoloader to load packages in different directories.
How to
1. First, Download and Install Composer.
2. Create all the directories you want to autoload and add your files. This step assumes you’ve already set up Composer and installed all dependencies.
2. Set up PSR-4 specification for autoloading the classes inside your composer.json file. Each namespaced prefix, e.g. “App\\“, must correspond to the directory we want to load our files. This may be a nested file as illustrated above. Remember to pay attention to case sensitivity when naming directories and classes.
"autoload": {
"psr-4": {
"App\\": "Core/App/",
"Ui\\": "Pages/"
}
}
3. Create all the directories you want to autoload, giving each a label. This is important as the label will be used as the root directory for your namespaces.
4. Add namespaces to your class files. This ensures your classes are uniquely identified. For example, another package may have the same class name as yours. Namespaces ensure the right class is being called. It also consolidates all your files for automatic loading.
// in Core/App/Mail.php
namespace App\Core;
class Mail{
}
// in /Pages/Cart.php
namespace Pages;
use App\Core\Mail;
class Cart{
}
5. Dump all files in composer. This is an important step as it tells Composer where your classes are located. You only need to re-run this when your root directory changes. In other words, you do not need to run this when a new class is added.
composer dump-autoload
Why use it?
- This is the best method to load files if you’re already assuming the overhead of using a dependency manager in your project.
Limitations
- The extra verbosity that comes with using namespaces can be a pain at first, but nothing a good IDE or code editor can’t mitigate.