PHP Composer and Autoloading
last modified October 22, 2026
In this article, we show how to use Composer to install
dependencies and how to load our own classes automatically with PSR-4
autoloading.
Composer is the standard dependency manager for PHP. It reads a
composer.json file and downloads the required packages from
Packagist into a vendor
directory.
Composer also generates vendor/autoload.php. Including that single
file makes every class of the installed packages — and of our own
project — available without a long list of require
statements.
Main Features
These are the files and concepts we work with in this article.
- composer.json: describes the project, its requirements and its autoload rules.
- composer.lock: records the exact installed versions; commit it for applications.
- vendor/autoload.php: the autoloader to include once at the start of the program.
- PSR-4: maps a namespace prefix to a directory.
- Classmap and files: alternative autoload strategies.
The project layout
Our example project has two classes in namespaced directories under src, a file with helper functions, and an entry script.
app/
├── composer.json
├── index.php
└── src/
├── helpers.php
├── Model/
│ └── User.php
└── Service/
└── Greeter.php
composer.json
The composer.json file is the configuration of the project. It is placed in the project root directory.
{
"name": "zetcode/demo",
"description": "Composer autoloading demo",
"type": "project",
"require": {
"php": ">=8.2",
"nesbot/carbon": "^3.15"
},
"autoload": {
"psr-4": {
"App\\": "src/"
},
"files": [
"src/helpers.php"
]
}
}
The require section lists the dependencies; here only the minimum PHP version. The autoload section tells Composer how to find our classes.
The PSR-4 rule maps the namespace prefix App\ to the src/ directory. A class App\Service\Greeter is expected in src/Service/Greeter.php.
The files entry loads helpers.php on every request. This is how functions, which cannot be autoloaded, are made available.
The classes
Each class lives in its own file whose path follows the PSR-4 rule.
<?php
declare(strict_types=1);
namespace App\Model;
class User
{
public function __construct(
public readonly string $name,
public readonly string $email,
) {}
}
The namespace mirrors the directory path below src/, and the file is named after the class.
This one-to-one convention is what lets the autoloader find a class without any configuration per class.
<?php
declare(strict_types=1);
namespace App\Service;
use App\Model\User;
class Greeter
{
public function greet(User $user): string
{
return "Hello, {$user->name} <{$user->email}>";
}
}
The Greeter refers to User by its fully qualified name through a use statement. Neither file includes the other.
<?php
declare(strict_types=1);
function dd_line(string $text): void
{
echo str_repeat('-', strlen($text)), "\n", $text, "\n";
}
A plain function file, loaded through the files autoload entry.
Using the autoloader
The entry script includes the autoloader and uses the classes and the helper function.
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use App\Model\User;
use App\Service\Greeter;
$user = new User('Alice', 'alice@example.com');
$greeter = new Greeter();
dd_line($greeter->greet($user));
The only require is the one for vendor/autoload.php.
When new User is evaluated, PHP asks the autoloader for App\Model\User. The autoloader turns the name into a path and includes the file.
Whenever the autoload section of composer.json changes, regenerate the autoloader with composer dump-autoload.
$ composer dump-autoload Generating autoload files Generated autoload files $ php index.php -------------------------------- Hello, Alice <alice@example.com>
How the mapping is stored
Composer writes the PSR-4 rules into vendor/composer/autoload_psr4.php.
Looking at it shows there is no magic involved: the autoloader just tries the prefix directory plus the rest of the class name.
$ tail -6 vendor/composer/autoload_psr4.php
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'App\\' => array($baseDir . '/src'),
);
For production, use composer dump-autoload -o (or --classmap-authoritative). It builds a class map of every class up front, so PHP does not touch the file system to search for classes.
$ composer dump-autoload -o Generating optimized autoload files Generated optimized autoload files containing 3 classes
Installing a package
The composer require command downloads a package and adds it to composer.json. It also updates composer.lock and regenerates the autoloader.
$ composer require nesbot/carbon Using version ^3.15 for nesbot/carbon
<?php
require __DIR__ . '/vendor/autoload.php';
use Carbon\Carbon;
echo Carbon::parse('2026-10-22')->addDays(10)->toDateString(), "\n";
Packages are used through the same vendor/autoload.php as our own code.
The lock file guarantees that every developer and the production server get the same versions after composer install.
The composer update command moves to newer versions allowed by the version constraint. The constraint ^3.15 means at least 3.15 and below 4.0.
$ php use_pkg.php 2026-11-01
Useful commands
The following table lists the Composer commands used most often.
| Command | Purpose |
|---|---|
composer init | Create a composer.json interactively. |
composer install | Install the versions recorded in composer.lock. |
composer update | Update packages within their version constraints. |
composer require --dev phpunit/phpunit | Add a development-only dependency. |
composer install --no-dev -o | Production install without dev packages and with an optimized autoloader. |
composer remove vendor/package | Remove a dependency. |
composer outdated | List packages with newer versions. |
composer audit | Check installed packages for known vulnerabilities. |
Commit composer.json and composer.lock to version control, but not the vendor directory.
Summary
In this article, we have created a composer.json with a PSR-4 autoload rule, loaded our classes through vendor/autoload.php, and installed a third-party package.
Author
List all PHP tutorials.