Composer and PSR-4 autoloading
A library never has enough books of its own. When the book you need is not on the shelves, the librarian orders it from another library, and a couple of days later it is waiting for you.
Code works the same way. Writing to a log, sending emails, working with dates—all of this has already been written and published by other developers as packages.
Copying other people's files by hand is awkward: a package has its own dependencies, they have theirs, and all of it has to stay in compatible versions. This job is done by Composer, PHP's dependency manager: a program that downloads a project's packages together with everything they need.
Composer installs packages
Composer is run in a terminal, in the project folder. Laravel Herd installs it along with PHP; on Linux, it is installed separately, as described in the "Set up your computer" section of the course page:
$ composer init
$ composer require monolog/monologThe first command asks a few questions and creates the file composer.json, the list of what the project needs. The second finds the Monolog logging package in the Packagist package catalog, downloads it and adds it to composer.json. After it the project holds:
composer.json—what the project needs: package names and allowed versions;composer.lock—which versions are actually installed;- the
vendor/folder—the downloaded packages and the filevendor/autoload.php.
vendor/ will hold not only monolog but also psr: Monolog itself depends on the package psr/log, and Composer downloaded it without being asked.
The vendor/ folder is not edited by hand and not saved to git, the store of a project's code history: it can always be rebuilt with composer install. But composer.json and composer.lock are saved to git.
composer.lock: install and update
composer.lock stores the exact versions of everything installed—both your packages and their dependencies. composer install reads this file and installs exactly what is recorded in it, so a colleague and the server get the same code. composer update picks fresh versions within composer.json again and rewrites the lock file: you run it when you have decided to update the packages.
PSR-4 autoloading
Composer loads not only other people's classes but yours too. Autoloading is a mechanism that pulls in a class's file by itself, at the moment the class is needed. It is the librarian's helper: from a class's full name it finds the shelf and the right book.
The rule that turns a class name into a file path is set in composer.json:
{
"require": {
"monolog/monolog": "^3.12"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}The autoload section says: everything that starts with App\ lives in the folder src/. In JSON a backslash is doubled, as in the PHP string 'App\\', while the name itself has one.
This rule is called PSR-4. For the class App\Http\Kernel it works like this:
- The prefix
App\is replaced with the foldersrc/, andHttp\Kernelis left of the name. - Every
\becomes/: that givessrc/Http/Kernel. .phpis added at the end:src/Http/Kernel.php.
So the class lives in the file src/Http/Kernel.php and declares a namespace that matches the folder:
<?php
namespace App\Http;
class Kernel
{
public function handle(): string
{
return 'ok';
}
}After editing the autoload section, run composer dump-autoload: Composer rebuilds the autoloader. The entry point, the file index.php, pulls in one single file—require __DIR__ . '/vendor/autoload.php';. After that new App\Http\Kernel() works without any require: the autoloader finds src/Http/Kernel.php by itself.
PSR-4 is one of the PSR standards that the authors of popular PHP projects agreed on; PSR-12 from the same series describes code formatting.
What happens at new
vendor/autoload.php registers an autoloader function with spl_autoload_register. When PHP meets a class that does not exist yet, it does not give up at once: first it calls this function with the full class name. The function finds and pulls in the file, the class appears—and new goes on.
In the program below the class is declared right inside the autoloader: this way one file stands in for the file Composer would pull in. Click "Next step" and watch the moment the line load App\User appears.
1<?php2 3namespace App;4 5spl_autoload_register(function ($name) {6 echo 'load ', $name, "\n";7 class User8 {9 public string $role = 'guest';10 }11});12 13$u = new User();14echo $u->role, "\n";
Everything declared in this file gets the prefix App\.
What to remember after the widget:
- the autoloader is called at
new, when the class does not exist yet, and it gets the full nameApp\User, notUser; - a second
new User()will not call it again: the class is declared, and there is nothing to look for; - the line
use App\Models\User;does not call the autoloader: it only shortens the name.
How PHP finds classes without require is a common interview question. The answer: it calls the registered autoloader with the class's full name, and Composer's autoloader turns the name into a path by the PSR-4 rule.
Here the librarian picture is inaccurate. A real librarian orders a book when you ask, but Composer works only before the program starts. While the program runs, nothing is downloaded: everything is already in vendor/.
Common mistake
The namespace in a file does not match its folder:
<?php
namespace App; // ✗
class Kernel
{
public function handle(): string
{
return 'ok';
}
}Fatal error: Uncaught Error: Class "App\Http\Kernel" not foundThe file sits in src/Http/Kernel.php but declares the class App\Kernel. From the name App\Http\Kernel the autoloader finds and pulls in this file, but the class it needs is not in it. The namespace must repeat the path from src/: namespace App\Http;.
Key points
composer requiredownloads a package with all its dependencies intovendor/;vendor/is not saved to git, whilecomposer.jsonandcomposer.lockare.composer installinstalls the versions fromcomposer.lock, whilecomposer updatepicks new ones and rewrites it.- PSR-4 turns
App\Http\Kernelintosrc/Http/Kernel.php: the prefix becomes the folder,\becomes/, and.phpgoes at the end. - PHP calls the autoloader with the full class name at the first
new, andvendor/autoload.phpis pulled in once.