How can I load classes from multiple directories with __autoload?
but this only loads the classes from one directory, what about other directories? Because I group the classes in different directories. So I will get error if I want to load classes from other directories,
function __autoload($class_name) < include AP_SITE."classes_1/class_".$class_name.".php"; include AP_SITE."classes_2/class_".$class_name.".php"; >
Warning: include(C:/wamp/www/art_on_your_doorstep_2011_MVC/global/applications/CART/classes_2/class_database_pdo.php) [function.include]: failed to open stream: No such file or directory in .
which refers to this line — include AP_SITE.»classes_2/class_».$class_name.».php»; So, my question is — how can I load classes from multiple directories with __autoload ? a possible solution:
function autoload_class_multiple_directory($class_name) < # List all the class directories in the array. $array_paths = array( 'classes_1/', 'classes_2/' ); # Count the total item in the array. $total_paths = count($array_paths); # Set the class file name. $file_name = 'class_'.strtolower($class_name).'.php'; # Loop the array. for ($i = 0; $i < $total_paths; $i++) < if(file_exists(AP_SITE.$array_paths[$i].$file_name)) < include_once AP_SITE.$array_paths[$i].$file_name; >> > spl_autoload_register('autoload_class_multiple_directory');
2 Answers 2
You can register multiple autoload functions by using spl_autoload_register instead of the single __autoload function. That’s the recommended way.
If one autoloader was able to load the file, the next one in the stack won’t be called.
Each autoloader however should only load the classes it is for, so you need to check that by the classname and/or with is_file . By classname often is better because trying wildly on the file-system can stress a system if your application grows.
To not re-invent the wheel, you could even use an autoloader that already exists which is able to deal with the PSR-0 standard on file-name-calling. Those often allow to register a specific namespace on a base-directory. In your case that would mean that you must rename and organize your files according to the PSR-0 convention.
The quick solution (bound to your question):
function __autoload($class_name) < $file = sprintf('%sclasses_1/class_%s.php', AP_SITE, $class_name); if (is_file($file)) < include $file; return; >$file = sprintf('%sclasses_2/class_%s.php', AP_SITE, $class_name); if (is_file($file)) < include $file; return; >>
As you can see, there is already code duplicated (as in yours). So this should just be a temporary solution as you will end up with more and more duplicated lines for each directory you would like to test for. If you consider to change the design, please take the PSR-0 shema into account, it helps to streamline one’s codebase and makes it easy to re-use other existing compontents in the PHP world.
function autoload_class_multiple_directory($class_name) < # List all the class directories in the array. $array_paths = array( 'classes_1/', 'classes_2/' ); foreach($array_paths as $path) < $file = sprintf('%s%s/class_%s.php', AP_SITE, $path, $class_name); if(is_file($file)) < include_once $file; >> > spl_autoload_register('autoload_class_multiple_directory');
PHP Autoloading Class Files
Summary: in this tutorial, you will learn how to organize your class files and load them automatically using PHP spl_autoload_register() function.
It is good practice to keep each PHP class in a separate file. Also, the name of the class should be the same as the file name. For example, the Contact.php file should contain the Contact class.
Before using a class, you need to:
- First, define the class in a file.
- Second, load it using the require , require_once , include , or include_once statement.
Suppose that you have the following project directory structure:
. ├── index.php └── models └── Contact.php
Code language: CSS (css)
The models directory has the Contact.php file that contains the following Contact class:
class Contact < private $email; public function __construct(string $email) < $this->email = $email; > public function getEmail() < return $this->email; > >
Code language: HTML, XML (xml)
From the index.php file, you can load the models/Contact.php file and use the Contact class as follows:
require_once 'models/Contact.php'; $contact = new Contact('john.doe@example.com');
Code language: HTML, XML (xml)
This solution works fine if you have a small number of files. When the number of files grows, the require_once statement doesn’t scale well.
To resolve it, you can define a function that takes a class name as an argument and includes the file that contains the class definition. For example:
function load_model($class_name) < $path_to_file = 'models/' . $class_name . '.php'; if (file_exists($path_to_file)) < require $path_to_file; > >
Code language: HTML, XML (xml)
The load_class() function looks for the class file in the models directory and includes it if the file exists. And you can place the load_model() function in the functions.php file:
. ├── functions.php ├── index.php └── models └── Contact.php
Code language: plaintext (plaintext)
To use the load_model() function in the index.php file, you can include the functions.php file and call the load_model() function:
require_once 'functions.php'; load_model('Person'); $person = new Person();
Code language: HTML, XML (xml)
Autoloader with spl_autoload_register() function
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.
To use the spl_autoload_register() function, you can pass the load_model function to it as follows:
function load_model($class_name) < $path_to_file = 'models/' . $class_name . '.php'; if (file_exists($path_to_file)) < require $path_to_file; > > spl_autoload_register('load_model');
Code language: HTML, XML (xml)
And from the index.php file, you don’t need to call the load_model() function whenever you use a class in the models directory:
require 'functions.php'; $contact = new Contact('john.doe@example.com');
Code language: HTML, XML (xml)
Multiple autoload functions
The spl_autoload_register() function allows you to use multiple autoloading functions. The spl_autoload_register() function will create a queue of autoloading functions and runs through each of them in the order that they are defined.
spl_autoload_register('autoloader1'); spl_autoload_register('autoloader2'); spl_autoload_register('autoloader3');
Code language: HTML, XML (xml)
In this example, PHP will run the autoloader1 , autoload2 , and autoloader3 sequentially to load the class files.
To demonstrate this, let’s create a new directory called services that stores service class files and create an Email.php file inside the services directory.
The following defines the Email class:
class Email < public static function send($contact) < return 'Sending an email to ' . $contact->getEmail(); > >
Code language: HTML, XML (xml)
The project directory now looks like this:
. ├── functions.php ├── index.php ├── models │ └── Contact.php └── services └── Email.php
Code language: plaintext (plaintext)
In the functions.php file, you can define a function that loads the classes from the services directory and pass the function name to the spl_autoload_register() function like this:
function load_model($class_name) < $path_to_file = 'models/' . $class_name . '.php'; if (file_exists($path_to_file)) < require $path_to_file; > > function load_service($service_name) < $path_to_file = 'services/' . $service_name . '.php'; if (file_exists($path_to_file)) < require $path_to_file; > > spl_autoload_register('load_model'); spl_autoload_register('load_service');
Code language: HTML, XML (xml)
From the index.php , you can use the Contact and Email classes as follows:
require 'functions.php'; $contact = new Contact('john.doe@example.com'); echo Email::send($contact);
Code language: HTML, XML (xml)
Sending an email to john.doe@example.com
Code language: plaintext (plaintext)
Like classes, you can also load the interfaces and traits using same autoloading function.
Summary
- An autoloading function loads a class, an interface, or a trait from a PHP file.
- Use the spl_autoload_register() function to autoload the classes, interfaces, and traits.
Autoload classes from different folders
But I have classes in models folder as well and I want to autoload them too — what should I do? Should I duplicate the autoload above and just change the path to models/ (but isn’t this repetitive??)? Thanks. EDIT: these are my classes file names in the controller folder:
class_controller_base.php class_factory.php etc
class_model_page.php class_model_parent.php etc
class controller_base < . >class controller_factory
class model_page < . >class model_parent
Shouldn’t class_factory.php be class_controller_factory.php? You’re going to need some consistency if you want to code to stay simple.
12 Answers 12
I see you are using controller_***** and model_***** as a class naming convention.
I read a fantastic article, which suggests an alternative naming convention using php’s namespace .
I love this solution because it doesn’t matter where I put my classes. The __autoload will find it no matter where it is in my file structure. It also allows me to call my classes whatever I want. I don’t need a class naming convention for my code to work.
You can, for example, set up your folder structure like:
- application/
- controllers/
- Base.php
- Factory.php
- Page.php
- Parent.php
Your classes can be set up like this:
The autoloader could look like this (or see ‘a note on autoloading’ at the end):
function __autoload($className) < $file = $className . '.php'; if(file_exists($file)) < require_once $file; >>
Then. you can call classes in three ways:
$controller = new application\controllers\Base(); $model = new application\models\Page();
EDIT — a note on autoloading:
My main auto loader looks like this:
// autoload classes based on a 1:1 mapping from namespace to directory structure. spl_autoload_register(function ($className) < # Usually I would just concatenate directly to $file variable below # this is just for easy viewing on Stack Overflow) $ds = DIRECTORY_SEPARATOR; $dir = __DIR__; // replace namespace separator with directory separator (prolly not required) $className = str_replace('\\', $ds, $className); // get full name of file containing the required class $file = ".php"; // get file if it is readable if (is_readable($file)) require_once $file; >);
This autoloader is a direct 1:1 mapping of class name to directory structure; the namespace is the directory path and the class name is the file name. So the class application\controllers\Base() defined above would load the file www/application/controllers/Base.php .
I put the autoloader into a file, bootstrap.php, which is in my root directory. This can either be included directly, or php.ini can be modified to auto_prepend_file so that it is included automatically on every request.
By using spl_autoload_register you can register multiple autoload functions to load the class files any which way you want. Ie, you could put some or all of your classes in one directory, or you could put some or all of your namespaced classes in the one file. Very flexible 🙂