PHP - Autoload only needed classes
All registered autoloader functions will be called when you try to instantiate a new class or until it finally loads the class or throws an error. The way you have it now, you're registering the same autoloader function again and again for each directory, and file.
What you'd want to do is something along the lines of this.
namespace autoloader;
class autoloader
{
public function __construct()
{
spl_autoload_register([$this, 'autoload']);
}
public function autoload($classname)
{
if (! file_exists("{$classname}.class.php")) {
return;
}
include_once "{$classname}.class.php";
}
}
new autoloader();
Every autoloader function gets the class FQCN passed into it, and from there you'll have to parse it and figure out if you can load the file where that class exists. For instance, if I do the following.
use Some\Awesome\ClassFile;
$class = new ClassFile();
The autoloader we've registered will get the string Some\Awesome\ClassFile
passed in as an argument, which we can then parse and see if we have a file for that class, if we don't we return out of the function and let the next registered autoloader function try and find the class.
You can read more about autoloaders in the documentation, I also wrote a blog post about it like 2 months ago that might interest you.
Most efficient way to load classes in PHP
If you're using PHP 5.x, you probably want autoloading.
PHP autoloader class to load files from two or three different path
I think your problem basically boils down to not checking if the file exists before require
ing it... that will produce a fatal error if the file doesn't exist in the first folder that you attempt to include from.
I don't know your use case, but here are some suggestions:
Can you just use a single autoloader?
function my_autoload($class_name) {
if (is_file('CLASSES/' . $class_name . '.php')) {
require_once 'CLASSES/' . $class_name . '.php';
} else if (is_file('ADMIN/TPL/SOME_FOLDER2/' . $class_name . '.php')) {
require_once 'ADMIN/TPL/SOME_FOLDER2/' . $class_name . '.php';
}
}
spl_autoload_register("my_autoload");
Or if you need to declare them independently (ie. two or more autoloaders):
function classes_autoload($class_name) {
if (is_file('CLASSES/' . $class_name . '.php')) {
require_once 'CLASSES/' . $class_name . '.php';
}
}
spl_autoload_register("classes_autoload");
function admin_autoload($class_name) {
if (is_file('ADMIN/TPL/SOME_FOLDER2/' . $class_name . '.php')) {
require_once 'ADMIN/TPL/SOME_FOLDER2/' . $class_name . '.php';
}
}
spl_autoload_register("admin_autoload");
Or make your autoloader class generic:
class MyAutoLoader {
private $path;
public function __construct($path) {
$this->path = $path;
spl_autoload_register( array($this, 'load') );
}
function load( $file ) {
if (is_file($this->path . '/' . $file . '.php')) {
require_once( $this->path . '/' . $file . '.php' );
}
}
}
$autoloader_classes = new MyAutoLoader('CLASSES');
$autoloader_admin = new MyAutoLoader('ADMIN/TPL/SOME_FOLDER2');
If you don't want to manually keep the list of child folders inside ADMIN/TPL
up to date you could even do something like this to autoload from any of them (this obviously assumes that all subfolders of ADMIN/TPL
contains classes):
function my_autoload($class_name) {
if (is_file('CLASSES/' . $class_name . '.php')) {
require_once 'CLASSES/' . $class_name . '.php';
} else {
$matching_files = glob('ADMIN/TPL/*/' . $class_name . '.php');
if (count($matching_files) === 1) {
require_once $matching_files[0];
} else if (count($matching_files) === 0) {
trigger_error('Could not find class ' . $class_name . '!', E_USER_ERROR);
} else {
trigger_error('More than one possible match found for class ' . $class_name . '!', E_USER_ERROR);
}
}
}
spl_autoload_register("my_autoload");
PHP cannot autoload classes
Try this autoload function instead :
function __autoload($classname)
{
$filename = str_replace("\\", "/", $classname).".php";
include __DIR__."/$filename";
}
- It replaces
\
with/
to match paths - It searches from the
includes/
directory.
You might also consider adding the includes/
path to your include_path
php directive :
set_include_path(get_include_path() . PATH_SEPARATOR . "{project_root}/includes");
PHP: Is AutoLoader able to load multiple class in a single php file?
You would have to have some complex function to autoload those classes from the file named Client.php
. The idea is to translate your namespace\classname
into a directory\filename.php
In this instance you would need to name your file A.php
then when you call new Protobuf\A()
it will find it. Otherwise you will have to create an overly-complex autoloader.
Let's say you do create the autoloader so it finds the A
class, then you can have B
on the same file, but only if you have already autoloaded A
otherwise you have to make some algorythm to know that A
and B
are on the same page.
I would do the above pattern or the pattern adopted by apps like Magento that turn class names into directory paths by replacing underscores:
$class = new Core_Classes_MyClass_Client();
Your autoloader would replace the underscores and will load:
Core/Classes/MyClass/Client.php //or similar scheme
This to me is an easy way to do it, but I prefer using namespace and class. The above method is not in favor at the moment and from a naming standpoint, very easy to get mixed up since a lot of classes may be in the same folder or nested really deep into sub folders. You could get some really long naming for classes.
Autoload classes from different folders
You should name your classes so the underscore (_
) translates to the directory separator (/
). A few PHP frameworks do this, such as Zend and Kohana.
So, you name your class Model_Article
and place the file in classes/model/article.php
and then your autoload does...
function __autoload($class_name)
{
$filename = str_replace('_', DIRECTORY_SEPARATOR, strtolower($class_name)).'.php';
$file = AP_SITE.$filename;
if ( ! file_exists($file))
{
return FALSE;
}
include $file;
}
Also note you can use spl_autoload_register()
to make any function an autoloading function. It is also more flexible, allowing you to define multiple autoload type functions.
If there must be multiple autoload functions, spl_autoload_register() allows for this. It effectively creates a queue of autoload functions, and runs through each of them in the order they are defined. By contrast, __autoload() may only be defined once.
Edit
Note : __autoload has been DEPRECATED as of PHP 7.2.0. Relying on this feature is highly discouraged. Please refer to PHP documentation for more details. http://php.net/manual/en/function.autoload.php
PHP Autoload Classes Is Not Working With Namespaces
As already pointed out in the comments, you'll need to strip everything besides the class name, like so:
$classname = substr($classname, strrpos($classname, "\\") + 1);
Within the context of your autoloading function:
spl_autoload_register(function($classname){
$classname = substr($classname, strrpos($classname, "\\") + 1);
require_once "src/{$classname}.php";
});
Let's take this a step further by making use of the fact that an autoload function always receives the qualified namespace as opposed to, for example, the relative namespace:
<?php
namespace Acme;
$foo = new \Acme\Foo(); // Fully qualified namespace
$foo = new Acme\Foo(); // Qualified namespace
$foo = new Foo(); // Relative namespace
In all three instances, our autoload function is always given Acme\Foo
as argument. With this in mind, it's fairly easy to implement an autoloader strategy that maps a namespace and any sub-namespaces to a file system path - especially if we include the top-level namespace (Acme
, in this case) in the filesystem hierarchy.
For example, given these two classes within some project of ours...
<?php
namespace Acme;
class Foo {}
Foo.php
<?php
namespace Acme\Bar;
class Bar {}
Bar.php
...within this file system layout...
my-project
`-- library
`-- Acme
|-- Bar
| `-- Bar.php
`-- Foo.php
...we could implement a simple mapping between a namespaced class and its physical location like so:
<?php
namespace Acme;
const LIBRARY_DIR = __DIR__.'/lib'; // Where our classes reside
/**
* Autoload classes within the current namespace
*/
spl_autoload_register(function($qualified_class_name) {
$filepath = str_replace(
'\\', // Replace all namespace separators...
'/', // ...with their file system equivalents
LIBRARY_DIR."/{$qualified_class_name}.php"
);
if (is_file($filepath)) {
require_once $filepath;
}
});
new Foo();
new Bar\Bar();
Also note that you can register multiple autoloading functions, for example, to handle different top-level namespaces in different physical locations. In a real-word project, though, you might want to get yourself acquainted with Composer's autoloading mechanism:
- https://getcomposer.org/doc/01-basic-usage.md#autoloading
- https://getcomposer.org/doc/04-schema.md#autoload
- https://getcomposer.org/doc/articles/autoloader-optimization.md
At some point, you might also want to have a look into PHP's autoloading specification:
- https://www.php-fig.org/psr/psr-4/
Related Topics
How to Select Rows Where Column Value Is Not Null Using Codeigniter's Activerecord
Highlight Multiple Keywords in Search
PHP Session Ids -- How Are They Generated
How to Store JavaScript Variable Output into a PHP Variable
Automatically Refresh Token Using Google Drive API with PHP Script
Weak Typing in PHP: Why Use Isset at All
MySQL Stored Procedure VS. Complex Query
PHP Datetime Createfromformat Functionality
PHP Datetime Microseconds Always Returns 0
PHP Remove Elements from Associative Array
Php: Check Who Had Read Sent Email
Xml Creation Using Codeigniter
How to Call a Python Script from PHP