Drop RobotLoader and let Composer Deal with Autoloading
Tomáš VotrubaUsing 2 tools for one thing, in this case 2 packages to autoload classes, are sign of an architecture smell. Many applications I see contain RobotLoader for historical reasons. I will borrow this from psychology: pathological behavioral patterns tear us down in the present, but were useful in past.
The best way to deal with them is acknowledge their purpose and then, let them go and enjoy the gift of present.
Martin Zlámal already wrote about this (Czech) a year ago and I think this needs to be promoted even more.
Where is RobotLoader Useful
RobotLoader is a Nette Component that is used to autoload classes. Its killer feature is simple: in whatever file it is and whatever the class name is, I will load it. You can have 20 classes in 1 file or classes located in various locations, they won't hide.
Before Composer appeared, it was The Best
RobotLoader was a very useful tool in times before Composer, because there were not so many efficient tools for loading classes.
Also, when people could not agree upon where to put their classes, how to name them, whether use or don't use namespace and how many classes to put in one file, we can say this tool saved a lot of argument-hours.
After many discussions, followed by the first standard, PSR-0, people agreed upon PSR-4, a more mature replacement of PSR-0.
Why it's not Anymore
Have your heard about PSR-4? It is a PHP Standard Recommendation about naming and location of the classes. This says you completely nothing, but in the simplest form it means:
1 class (or interface/trait) = 1 file
class name = file name.php
namespace\class name = directory/file name.php
# class => file
MyClass => MyClass.php
App\MyClass => App/MyClass.php
App\Presenter\MyClass => App/Presenter/MyClass.php
I know I can rely on this in 99% of places when composer.json
is used.
When I see App\Presenter\MyClass
I it's located in App/Presenter/MyClass.php
file.
And this is the place where RobotLoader (or any custom ultimate loader) fails. I came around many applications where classes are located at random. And I had to use my brain to find them. But I don't want to focus my mental activity on thinking about their location, I want to develop my application.
How to move to Composer in a Nette application?
There are 2 levels of how to achieve this.
Level 1: Change your Composer
The first level requires 3 small steps.
1. Tune composer.json
Autoloading
{
"require": {
"..."
},
"autoload": {
"psr-4": {
"App\\Forms\\": "app/forms",
"App\\Model\\": "app/model",
"App\\Presenters\\": "app/presenters",
"App\\Router\\": "app/router"
}
}
}
This means, all classes residing in App\\Forms
namespace have to be located in app/forms
directory.
One important rule - it works in case-sensitive manner.
So this will work:
App\Presenters\HomepagePresenter => app/presenters/HomepagePresenter.php
But this will not:
App\Presenters\HomepagePresenter => app/presenters/homepagePresenter.php
2. Disable RobotLoader
Now you can clean up your app/bootstrap.php
:
// $configurator->createRobotLoader()
// ->addDirectory(__DIR__)
// ->register();
But RobotLoader is still silently enabled for presenters. We don't want that either now:
# app/config/config.neon
application:
scanDirs: false
3. Refresh Composer Autoloading
And tell Composer, to regenerate its autoloader:
composer dump-autoload
Note: This command is run by default after composer update
, composer require ...
and similar commands. Since we'd manually changed our autoload
section, we had to run it manually.
Now try our application and it should run.
You are finished and all your classes are loaded by Composer. Congratulations!
Level 2: Rename Directories to capital case, to Respect PSR-4
There is one more level I do with my applications, so my composer.json
is nice and clear. But this is optional. Do it only if you'd like to write better code with lower WTF factor!
Turn this:
/app
/forms
/model
/presenters
/routing
/...
Into this:
/app
/Forms
/Model
/Presenters
/Routing
/...
After these steps, you can simplify your autoload
section as such:
{
"autoload": {
"psr-4": {
"App\\": "app"
}
}
}
Don't forget to run:
composer dump-autoload
And you've unlocked Level 2.
That's it!