Don't get me wrong, Composer is an amazing product. It has an easy-to-use API for quick autoloading, supports multiple standards, and has autoloading optimization built-in - it even lets you search and require packages easily. It's a reason Composer is the most used package manager for PHP.
If your project is a lightweight OOP project with no external packages though. Using Composer might be a bit overkill, you could easily implement autoloading using a simple array
and PHP's spl_autoload_register
function.
For those not familiar, spl_autoload_register
is what we can use to add to our own autoloading function to PHPs autoloading queue. We can use that together with a simple array $classmap
to autoload our small project. Here's our project structure.
- /public/index.php
- /src/*/.php
- autoload.php
/public
will be our front-facing entry point, /src
will hold our classes, and autoload.php
will handle our autoloading. Our classes will be formatted in PSR-4, with our vendor namespace prefix being used as the key for our internal $classmap
.
Let's create our autoload.php
file and add some autoloading to our project.
define('PHPNEXUS_VERSION', '0.0.1');
$classmap = [
'PHPNexus' => __DIR__ . '/src/',
];
spl_autoload_register(function(string $classname) use ($classmap) {
$parts = explode('\\', $classname);
$namespace = array_shift($parts);
$classfile = array_pop($parts) . '.php';
if (! array_key_exists($namespace, $classmap)) {
return;
}
$path = implode(DIRECTORY_SEPARATOR, $parts);
$file = $classmap[$namespace] . $path . DIRECTORY_SEPARATOR . $classfile;
if (! file_exists($file) && ! class_exists($classname)) {
return;
}
require_once $file;
});
Going over our code shows we first define a new constant - PHPNEXUS_VERSION
, this will be used to check if our autoload file has already been loaded later. $classmap
is an associative array - as previously mentioned our vendor namespace is our key. The value of which is the location to our source files, in this case, that is /src
. And since autoloading.php
is in our root, we can take advantage of the __DIR__
constant.
The first argument of spl_autoload_register
is the callback function - that's our autoloading function. That function will take a string parameter - when you instantiate a class with new
, the FQCN gets passed as that parameter's argument.
Next, we are destructuring our $classname
argument by separating each value into an array $parts
variable. Since we know our first $parts
value is our vendor namespace, we use array_shift
to grab it and use array_pop
to grab the actual filename since we also know that's the last part of our $classname
.
We then do a quick check to see if the $namespace
is in our $classmap
so we can handle the file location, if that's not the case we simply do an early return out of the function.
$path
will be the variable holding the remaining parts of our $parts
array, since the filename and namespace are already removed, we can assume the rest of the pieces are the rest of our file path.
We then reassemble our file path info into a temporary $file
variable and check if that file exists. As a precaution, we also include a check to see if the class has already been defined. If either fails - as before, we return out of the function and be on our merry way.
Lastly, if nothing fails all we do is require the $file
that contains our class so PHP can now initialize it.
To then use our simple autoloading file, in our public/index.php
we simply add the following if statement.
if (! defined('PHPNEXUS_VERSION')) {
require_once dirname(__DIR__) . '/autoload.php';
}
This does a check for the earlier constant to see if the file has already been included or required earlier, if that fails we require our autoload.php
file and now we can use our classes, e.g. if we had a PHPNexus\Request\Request
class located in /src/Request/Request
.
if (! defined('PHPNEXUS_VERSION')) {
require_once dirname(__DIR__) . '/autoload.php';
}
use PHPNexus\Request\Request;
$request = new Request($_SERVER);
And that's how we can create simple autoloading for our project without having to rely on Composer. As mentioned above though, if your project already requires packages from other sources via Composer, then it's best to stick with Composers autoloader instead.
To leave off, have you ever written your own function to handle autoloading before? Perhaps you didn't know how to do it and you know do, let me know by replying to the topic. Would love to hear your thought on implementing simple autoloading for projects.
Top comments (6)
Well, like you said, composer is a package manager. Auto loading isn't it's primary purpose, it's more of an UX kind of candy (where the users are developers). When you say maybe you don't need composer, what is the scope of the statement? You have a different way of replacing package management?
In which context would you not need composer? Well, if you don't have dependencies and all you need is auto load, then this definitely fits the bill.
I would go farther and say: don't use composer if all you need is auto load. If you don't need external packages you are better off writing your own. Composer comes with bits of overhead and bits of restrictions ( you can restrict auto load compatibility to various PSR standards but your own has ultimate flexibility and it's trivial to write a small auto load function and provide a hook for "manual" loading classes they may not respect a standard)
I completely agree with the statement "If you don't have dependencies and all you need is auto load". That is what this post is meant to demonstrate. I've seen many projects that don't use any external packages use Composer just for their autoloading - which I can admit I've done myself before.
After reading a bit on autoloading though, I found out that implementing simple autoloading wasn't that difficult thing to do, hence this post. I can admit though the title was a cheeky attempt to be a bit provocative. And yes, Composer does more than just autoloading, it's a package manager after all. But as I state in the post - "If your project already requires packages from other sources via Composer, then it's best to stick with Composer...". This post is all about implementing simple autoloading after all, not package management.
Isn't composer a Dependency Manager?
That's interesting how you made autoloader using array functions. Mine is a bit simplier, using
str_replace
to achieve your goal of making filepath.I'm in a middle of project where namespaces are not fully implemented, so I created my own version which is quite similar:
Wonderful. Thank you... It's too sad that PHP does not let us autoload on per-function level. We can use "use function My\Full\functionName;" but it doesn't go to spl_autoload_register() for functions
Really great post! I just learned something new today.