Skip to content
Theory · Files, namespaces and Composer · lesson 4 of 7

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:

TEXT
$ composer init
$ composer require monolog/monolog

The 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 file vendor/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:

TEXT
{
    "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:

  1. The prefix App\ is replaced with the folder src/, and Http\Kernel is left of the name.
  2. Every \ becomes /: that gives src/Http/Kernel.
  3. .php is 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
<?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.

LiveThe autoloader and newstep 1 of 8 · frames: 1

Everything declared in this file gets the prefix App\.

Watch when the autoloader is called and with which name

What to remember after the widget:

  • the autoloader is called at new, when the class does not exist yet, and it gets the full name App\User, not User;
  • 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
<?php

namespace App; // ✗

class Kernel
{
    public function handle(): string
    {
        return 'ok';
    }
}
TEXT
Fatal error: Uncaught Error: Class "App\Http\Kernel" not found

The 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 require downloads a package with all its dependencies into vendor/; vendor/ is not saved to git, while composer.json and composer.lock are.
  • composer install installs the versions from composer.lock, while composer update picks new ones and rewrites it.
  • PSR-4 turns App\Http\Kernel into src/Http/Kernel.php: the prefix becomes the folder, \ becomes /, and .php goes at the end.
  • PHP calls the autoloader with the full class name at the first new, and vendor/autoload.php is pulled in once.