Autoloading is PHP's way to automatically find classes and their corresponding files without having to require all of them manually. Autoloading is also one of the reasons why PHP code is usually organized in classes because PHP only supports autoloading for classes, not simple functions for example.
Table of content
Require
PHP require
construct loads the code from an file into a script and executes that code. To load the code from a file, you specify the file path after the require
keyword. When loading the file, the require
construct executes the code in the loaded file.
PHP require_once
is the counterpart of the include_once
except that the require_once
issues an error if it fails to load the file. Also, the require_once
won’t load the file again if the file has been loaded.
Use
require
construct to load the code from another file into the script.Use
require_once
construct to load the code from another file once and won’t include the file again if the file has been loaded.The
require
andrequire_once
are language constructs, not functions.
Directory System
📦Require
┣ 📂App
┃ ┣ 📂Controller
┃ ┃ ┗ 📜PersonController.php
┃ ┗ 📂Model
┃ ┃ ┗ 📜Person.php
┣ 📂Lib
┃ ┗ 📂Book
┃ ┃ ┗ 📂Example
┃ ┃ ┃ ┣ 📜Example1.php
┃ ┃ ┃ ┣ 📜Example2.php
┃ ┃ ┃ ┗ 📜Example3.php
┗ 📜index.php
PersonController Class
<?php
namespace App\Controller;
class PersonController
{
public function show(): string
{
return 'Class PersonController';
}
}
Person Class
<?php
namespace App\Model;
class Person
{
public function show(): string
{
return 'Class Person';
}
}
Example Classes
<?php
namespace Book\Example;
class Example1
{
public function show(): string
{
return 'Class Example1';
}
}
<?php
namespace Book\Example;
class Example2
{
public function show(): string
{
return 'Class Example2';
}
}
<?php
namespace Book\Example;
class Example3
{
public function show(): string
{
return 'Class Example3';
}
}
Testing
<?php
require_once 'App/Controller/PersonController.php';
require_once 'App/Model/Person.php';
require_once 'Lib/Book/Example/Example1.php';
require_once 'Lib/Book/Example/Example2.php';
require_once 'Lib/Book/Example/Example3.php';
use App\Controller\PersonController;
use App\Model\Person;
use Book\Example\Example1;
use Book\Example\Example2;
use Book\Example\Example3;
$e1 = new Example1();
$e2 = new Example2();
$e3 = new Example3();
$p1 = new Person();
$p2 = new PersonController();
var_dump($e1);
var_dump($e2);
var_dump($e3);
var_dump($p1);
var_dump($p2);
print $e1->show() . '<br>';
print $e2->show() . '<br>';
print $e3->show() . '<br>';
print $p1->show() . '<br>';
print $p2->show();
Output
object(Book\Example\Example1)[1]
object(Book\Example\Example2)[2]
object(Book\Example\Example3)[3]
object(App\Model\Person)[4]
object(App\Controller\PersonController)[5]
Class Example1
Class Example2
Class Example3
Class Person
Class PersonController
SPL
PHP 5.1.2 introduced the spl_autoload_register()
function that automatically loads a class file whenever you use a class that has not been loaded yet.
PHP 7.2.0 deprecated the __autoload()
magic function and recommended to use the spl_autoload_register()
function instead.
When you use a class that has not been loaded, PHP will automatically look for spl_autoload_register()
function call.
The spl_autoload_register()
function accepts a callback function and calls it when you attempt to create use a class that has not been loaded.
Directory System
📦2.SPL
┣ 📂App
┃ ┣ 📂Controller
┃ ┃ ┗ 📜PersonController.php
┃ ┗ 📂Model
┃ ┃ ┗ 📜Person.php
┣ 📂Lib
┃ ┗ 📂Book
┃ ┃ ┣ 📂Core
┃ ┃ ┃ ┣ 📜AppLoader.php
┃ ┃ ┃ ┗ 📜ClassLoader.php
┃ ┃ ┗ 📂Example
┃ ┃ ┃ ┣ 📜Example1.php
┃ ┃ ┃ ┣ 📜Example2.php
┃ ┃ ┃ ┗ 📜Example3.php
┗ 📜index.php
ClassLoader Class
<?php
namespace Book\Core;
class ClassLoader
{
// Array to store namespace prefixes and their corresponding base directories.
protected array $prefixes;
// Method to register the ClassLoader's autoloader function.
public function register(): void
{
spl_autoload_register(array($this, 'loadClass'));
}
// Method to add a namespace and its base directory to the ClassLoader.
public function addNamespace(string $prefix, string $base_dir, bool $prepend = false): void
{
// Normalize namespace prefix and base directory.
// The trim() function removes whitespace and other predefined characters from both sides of a string.
$prefix = trim($prefix, '\\') . '\\';
// rtrim() removes whitespace or other predefined characters from the right side of a string.
$base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . '/';
// Initialize the namespace prefix array if not set.
if (isset($this->prefixes[$prefix]) === false) {
$this->prefixes[$prefix] = [];
}
// Retain the base directory for the namespace prefix.
if ($prepend) {
// The array_unshift() function inserts new elements in the beginning to an array.
array_unshift($this->prefixes[$prefix], $base_dir);
} else {
// The array_push() function inserts one or more elements to the end of an array.
array_push($this->prefixes[$prefix], $base_dir);
}
}
// Private method to load a class using autoloading.
private function loadClass(string $class): mixed
{
// The current namespace prefix.
$prefix = $class;
// Iterate through the namespace names to find a mapped file name.
while (false !== $pos = strrpos($prefix, '\\')) {
// Retain the trailing namespace separator in the prefix.
$prefix = substr($class, 0, $pos + 1);
// The rest is the relative class name.
$relative_class = substr($class, $pos + 1);
// Try to load a mapped file for the prefix and relative class.
$mapped_file = $this->loadMappedFile($prefix, $relative_class);
if ($mapped_file) {
return $mapped_file;
}
// Remove the trailing namespace separator for the next iteration.
$prefix = rtrim($prefix, '\\');
}
// Class file not found.
return false;
}
// Private method to load a mapped file based on namespace and class name.
private function loadMappedFile(string $prefix, string $relative_class): mixed
{
// Check if there are base directories for this namespace prefix.
if (isset($this->prefixes[$prefix]) === false) {
return false;
}
// Iterate through base directories for this namespace prefix.
foreach ($this->prefixes[$prefix] as $base_dir) {
/**
* Replace the namespace prefix with the base directory,
* replace namespace separators with directory separators
* in the relative class name, append with .php
* */
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
// If the file is successfully required, return its path.
if ($this->requireFile($file)) {
return $file;
}
}
// File not found in any base directory.
return false;
}
// Private method to require a file if it exists.
private function requireFile(string $file): bool
{
// If the file exists, require it and return true.
if (file_exists($file)) {
require_once $file;
return true;
}
php // File does not exist.
return false;
}
}
- ClassLoader is a class that implements an automatic class loader in PHP, allowing the organization of code using namespaces and facilitating the dynamic loading of classes as needed.
Testing
<?php
require_once 'Lib/Book/Core/ClassLoader.php';
$al = new Book\Core\ClassLoader();
$al->addNamespace('Book', 'Lib/Book');
$al->register();
use Book\Example\Example1;
use Book\Example\Example2;
use Book\Example\Example3;
$e1 = new Example1();
$e2 = new Example2();
$e3 = new Example3();
var_dump($e1);
var_dump($e2);
var_dump($e3);
print $e1->show() . '<br>';
print $e2->show() . '<br>';
print $e3->show();
Output
object(Book\Example\Example1)[2]
object(Book\Example\Example2)[3]
object(Book\Example\Example3)[4]
Class Example1
Class Example2
Class Example3
AppLoader Class
<?php
namespace Book\Core;
use RecursiveIteratorIterator;
use RecursiveDirectoryIterator;
use Exception;
class AppLoader
{
// Array to store directories to search for classes.
protected $directories;
// Method to add a directory to the AppLoader.
public function addDirectory($directory)
{
$this->directories[] = $directory;
}
// Method to register the AppLoader's autoloader function.
public function register()
{
spl_autoload_register(array($this, 'loadClass'));
}
// Method to load a class using autoloading based on registered directories.
public function loadClass($class)
{
// Get the directories to search for the class.
$folders = $this->directories;
// Iterate through the registered directories.
foreach ($folders as $folder) {
// Check if the class file exists directly in the current directory.
if (file_exists("{$folder}/{$class}.php")) {
require_once "{$folder}/{$class}.php";
return true;
} else {
// If the directory itself exists, search for the class file recursively.
if (file_exists($folder)) {
// Iterate through files and subdirectories in the current directory.
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($folder),
RecursiveIteratorIterator::SELF_FIRST) as $entry) {
// Check if the entry is a directory.
if (is_dir($entry)) {
// Check if the class file exists in the current subdirectory.
if (file_exists("{$entry}/{$class}.php")) {
require_once "{$entry}/{$class}.php";
return true;
}
}
}
}
}
}
// Class file not found in any registered directory.
return false;
}
}
The code checks whether the class file is directly in the main directory or recursively in the subdirectories of the registered directories.
remove the namespace from the classes
Person
andPersonController
.
Testing
require_once 'Lib/Book/Core/AppLoader.php';
$loader = new Book\Core\AppLoader;
$loader->addDirectory(__DIR__ . '/App');
$loader->register();
$p1 = new Person();
$p2 = new PersonController();
var_dump($p1);
var_dump($p2);
print $p1->show() . '<br>';
print $p2->show();
Output
object(Person)[6]
object(PersonController)[7]
Class Person
Class PersonController
Use the
spl_autoload_register()
function to autoload the classes, interfaces, and traits.The problem with the
spl_autoload_register()
function is that you have to implement the autoloader functions by yourself. And your autoloaders may not like autoloaders developed by other developers.Therefore, when you work with a different codebase, you need to study the autoloaders in that particular codebase to understand how they work.
Composer
Composer is a dependency manager for PHP. Composer allows you to manage dependencies in your PHP project. In this tutorial, we’ll focus on how to use the Composer for autoloading classes.
First create a new file called composer.json
under the project’s root folder. In the composer.json
, you add the following code:
{
"autoload": {
"classmap": ["Lib/Book/Example", "App/Controller", "App/Model"]
}
}
Next, launch the Command Prompt on Windows or Terminal on macOS and Linux, and navigate to the project directory.
Then, type the following command from the project directory:
composer dump-autoload
Composer will generate a directory called vendor
that contains a number of files the most important file to you for now is autoload.php
file.
Directory System
📦Composer
┣ 📂App
┃ ┣ 📂Controller
┃ ┃ ┗ 📜PersonController.php
┃ ┗ 📂Model
┃ ┃ ┗ 📜Person.php
┣ 📂Lib
┃ ┗ 📂Book
┃ ┃ ┣ 📂Core
┃ ┃ ┃ ┣ 📜AppLoader.php
┃ ┃ ┃ ┗ 📜ClassLoader.php
┃ ┃ ┗ 📂Example
┃ ┃ ┃ ┣ 📜Example1.php
┃ ┃ ┃ ┣ 📜Example2.php
┃ ┃ ┃ ┗ 📜Example3.php
┣ 📂vendor
┃ ┣ 📂composer
┃ ┗ 📜autoload.php
┣ 📜composer.json
┗ 📜index.php
Testing
<?php
require_once 'vendor/autoload.php';
use App\Controller\PersonController;
use App\Model\Person;
use Book\Example\Example1;
use Book\Example\Example2;
use Book\Example\Example3;
$e1 = new Example1();
$e2 = new Example2();
$e3 = new Example3();
$p1 = new Person();
$p2 = new PersonController();
var_dump($e1);
var_dump($e2);
var_dump($e3);
var_dump($p1);
var_dump($p2);
print $e1->show() . '<br>';
print $e2->show() . '<br>';
print $e3->show() . '<br>';
print $p1->show() . '<br>';
print $p2->show();
Output
object(Book\Example\Example1)[4]
object(Book\Example\Example2)[2]
object(Book\Example\Example3)[5]
object(App\Model\Person)[6]
object(App\Controller\PersonController)[7]
Class Example1
Class Example2
Class Example3
Class Person
Class PersonController
PSR-4
PSR-4 is auto-loading standard that describes the specification for autoloading classes from file paths.
According to the PSR-4, a fully qualified class name has the following structure:
\<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>
The structure starts with a namespace, followed by one or more sub namespaces, and the class name.
The composer.json
file looks like the following:
{
"autoload": {
"psr-4": {
"App\\":"App/",
"Book\\":"Lib/Book"
}
}
}
Instead using the classmap
, the composer.json
file now uses psr-4
. The psr-4
maps the namespace "App\\"
to the "App/"
folder and namespace "Book\\"
to the "Lib/Book"
folder .
Note that the second backslash (\
) in the App\
namespace is used to escape the first backslash (\
).
Run the composer dump-autoload
command to generate the autoload.php
file:
composer dump-autoload
Directory System
📦PSR4
┣ 📂App
┃ ┣ 📂Controller
┃ ┃ ┗ 📜PersonController.php
┃ ┗ 📂Model
┃ ┃ ┗ 📜Person.php
┣ 📂Lib
┃ ┗ 📂Book
┃ ┃ ┣ 📂Core
┃ ┃ ┃ ┣ 📜AppLoader.php
┃ ┃ ┃ ┗ 📜ClassLoader.php
┃ ┃ ┗ 📂Example
┃ ┃ ┃ ┣ 📜Example1.php
┃ ┃ ┃ ┣ 📜Example2.php
┃ ┃ ┃ ┗ 📜Example3.php
┣ 📂vendor
┃ ┣ 📂composer
┃ ┗ 📜autoload.php
┣ 📜composer.json
┗ 📜index.php
Testing
<?php
require_once 'vendor/autoload.php';
use App\Controller\PersonController;
use App\Model\Person;
use Book\Example\Example1;
use Book\Example\Example2;
use Book\Example\Example3;
$e1 = new Example1();
$e2 = new Example2();
$e3 = new Example3();
$p1 = new Person();
$p2 = new PersonController();
var_dump($e1);
var_dump($e2);
var_dump($e3);
var_dump($p1);
var_dump($p2);
print $e1->show() . '<br>';
print $e2->show() . '<br>';
print $e3->show() . '<br>';
print $p1->show() . '<br>';
print $p2->show();
Output
object(Book\Example\Example1)[4]
object(Book\Example\Example2)[2]
object(Book\Example\Example3)[5]
object(App\Model\Person)[6]
object(App\Controller\PersonController)[7]
Class Example1
Class Example2
Class Example3
Class Person
Class PersonController
- Use PSR-4 for organizing directory and class files.
- Use the
composer dump-autoload
command to generate theautoload.php file
.